Page MenuHomeFreeBSD

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
Index: vendor/subversion/dist/CHANGES
===================================================================
--- vendor/subversion/dist/CHANGES (revision 286500)
+++ vendor/subversion/dist/CHANGES (revision 286501)
@@ -1,5173 +1,5383 @@
+Version 1.8.14
+(5 Aug 2015, from /branches/1.8.x)
+http://svn.apache.org/repos/asf/subversion/tags/1.8.14
+
+ User-visible changes:
+ - Client-side bugfixes:
+ * document svn:autoprops (r1678494 et al.)
+ * cp: fix 'svn cp ^/A/D/H@1 ^/A' to properly create A (r1674455, r1674456)
+ * resolve: improve conflict prompts for binary files (r1667228 et al.)
+ * ls: improve performance of '-v' on tag directories (r1673153)
+ * improved Sqlite 3.8.9 query performance regression on externals (r1672295 et al.)
+ * fixed issue #4580: 'svn -v st' on file externals reports "?" instead
+ of user and revision after 'svn up' (r1680242)
+
+ - Server-side bugfixes:
+ * mod_dav_svn: do not ignore skel parsing errors (r1658168)
+ * detect invalid svndiff data earlier (r1684077)
+ * prevent possible repository corruption on power/disk failures (r1680819)
+ * fixed issue #4577: Read error with nodes whose DELTA chain starts with
+ a PLAIN rep (r1676667, r1677267)
+ * fixed issue #4531: server-side copy (over dav) is slow and uses
+ too much memory (r1674627)
+
+ Developer-visible changes:
+ - General:
+ * support building on Windows with Visual Studio 2015 (r1692785)
+ * avoid failing some tests on versions of Python with a very old sqlite (r1674522)
+ * fix Ruby tests so they don't use the users real configuration (r1597884)
+
+ - Bindings:
+ * swig-pl: fix some stack memory problems (r1668618, 1671388)
+
+Version 1.8.13
+(31 Mar 2015, from /branches/1.8.x)
+http://svn.apache.org/repos/asf/subversion/tags/1.8.13
+
+ User-visible changes:
+ - Client-side bugfixes:
+ * ra_serf: prevent abort of commits that have already succeeded (r1659867)
+ * ra_serf: support case-insensitivity in HTTP headers (r1650481, r1650489)
+ * better error message if an external is shadowed (r1655712, r1655738)
+ * ra_svn: fix reporting of directory read errors (r1656713)
+ * fix a redirect handling bug in 'svn log' over HTTP (r1650531)
+ * properly copy tree conflict information (r1658115, r1659553, r1659554)
+ * fix 'svn patch' output for reordered hunks (issue #4533)
+ * svnrdump load: don't load wrong props with no-deltas dump (issue #4551)
+ * fix working copy corruption with relative file external (issue #4411)
+ * don't crash if config file is unreadable (r1590751, r1660350)
+ * svn resolve: don't ask a question with only one answer (r1658417)
+ * fix assertion failure in svn move (r1651963 et al)
+ * working copy performance improvements (r1664531, r1664476, et al)
+ * handle existing working copies which become externals (r1660071)
+ * fix recording of WC meta-data for foreign repos copies (r1660593)
+ * fix calculating repository path of replaced directories (r1660646)
+ * fix calculating repository path after commit of switched nodes (r1663991)
+ * svnrdump: don't provide HEAD+1 as base revision for deletes (r1664684)
+ * don't leave conflict markers on files that are moved (r1660220, r1665874)
+ * avoid unnecessary subtree mergeinfo recording (r1666690)
+ * fix diff of a locally copied directory with props (r1619380 et al)
+
+ - Server-side bugfixes:
+ * fsfs: fix a problem verifying pre-1.4 repos used with 1.8 (r1561419)
+ * svnadmin freeze: fix memory allocation error (r1653039)
+ * svnadmin load: tolerate invalid mergeinfo at r0 (r1643074, issue #4476)
+ * svnadmin load: strip references to r1 from mergeinfo (issue #4538)
+ * svnsync: strip any r0 references from mergeinfo (issue #4476)
+ * fsfs: reduce memory consumption when operating on dag nodes (r1655651)
+ * reject invalid get-location-segments requests in mod_dav_svn and
+ svnserve (r1667233)
+ * mod_dav_svn: reject invalid txnprop change requests (r1667235)
+
+ - Client-side and server-side bugfixes:
+ * fix undefined behaviour in string buffer routines (r1650834)
+ * fix consistency issues with APR r/w locks on Windows (r1611380 et al)
+ * fix occasional SEGV if threads load DSOs in parallel (r1659013, r1659315)
+ * properly duplicate svn error objects (r1651759)
+ * fix use-after-free in config parser (1646785, r1646786, r1646797)
+
+ Developer-visible changes:
+ * add lock file config for testing against HTTPD 2.4+ (r1544302, r1544303)
+ * make sqlite amalgamated build work with sqlite 3.8.x+ (r1659399)
+ * fix build with Ruby 2 (r1596882)
+ * process 'svnadmin dump' output as binary in the test suite (r1592987)
+ * simplify Windows resource compilation to avoid warnings (r1532287)
+
+
+Version 1.8.12
+(Not released, see changes for 1.8.13.)
+
+
+Version 1.8.11
+(15 Dec 2014, from /branches/1.8.x)
+http://svn.apache.org/repos/asf/subversion/tags/1.8.11
+
+ User-visible changes:
+ - Client-side bugfixes:
+ * checkout/update: fix file externals failing to follow history and
+ subsequently silently failing (issue #4185)
+ * patch: don't skip targets in valid --git difs (r1592014, r1592034)
+ * diff: make property output in diffs stable (r1589360)
+ * diff: fix diff of local copied directory with props (r1619380, r1619393)
+ * diff: fix changelist filter for repos-WC and WC-WC (r1621978, r1621981)
+ * remove broken conflict resolver menu options that always error out
+ (r1620332)
+ * improve gpg-agent support (r1600331, r1600348, 1600368, r1600563,
+ r1600781)
+ * fix crash in eclipse IDE with GNOME Keyring (issue #3498)
+ * fix externals shadowing a versioned directory (issue #4085)
+ * fix problems working on unix file systems that don't support
+ permissions (r1612225)
+ * upgrade: keep external registrations (issue #4519)
+ * cleanup: iprove performance of recorded timestamp fixups (r1633126)
+ * translation updates for German
+
+ - Server-side bugfixes:
+ * disable revprop caching feature due to cache invalidation problems
+ (r1543594, r1619774, r1619105, r1619118, r1619153, r1619802)
+ * skip generating uniquifiers if rep-sharing is not supported (r1561426)
+ * mod_dav_svn: reject requests with missing repository paths (r1643409)
+ * mod_dav_svn: reject requests with invalid virtual transaction names
+ (r1643437)
+ * mod_dav_svn: avoid unneeded memory growth in resource walking
+ (issue #4531)
+
+ Developer-visible changes:
+ - General:
+ * make sure all members of the repos layer notify struct are valid,
+ fixes crashes in API users using all members (r1616131)
+ * properly generate a version resource when building on Windows (r1542610,
+ r1564576, r1568180)
+ * fix LIBTOOL_M4 and LIBTOOL_CONFIG variable not be evaluated properly
+ during a unix build (r1637826)
+ * allow the use of libtool 2.4.3 (r1640862, r1640873, r1643793)
+
+
Version 1.8.10
(11 Aug 2014, from /branches/1.8.x)
http://svn.apache.org/repos/asf/subversion/tags/1.8.10
User-visible changes:
- Client-side bugfixes:
* guard against md5 hash collisions when finding cached credentials
(r1550691, r1550772, r1600909)
* ra_serf: properly match wildcards in SSL certs. (r1615211, 1615219)
* ra_serf: ignore the CommonName in SSL certs where there are Subject Alt
Names (r1565531, r1566503, r1568349, r1568361)
* ra_serf: fix a URI escaping bug that prevented deleting locked paths
(r1594223, r1553501, r1553556, r1559197, issue #3674)
* rm: Display the proper URL when deleting a URL in the commit log editor
(r1591123)
* log: Fix another instance of broken pipe error (r1596866, issue #3014)
* copy: Properly handle props not present or excluded on cross wc copy
(r1589184, r1589188)
* copy: Fix copying parents of locally deleted nodes between wcs
(r1589460, r1589486)
* externals: Properly delete ancestor directories of externals when
removing the external by changing svn:externals. (r1600311, 1600315,
r1600323, r1600393)
* ra_serf: fix memory lifetime of some hash values (r1606009)
- Server-side bugfixes:
* fsfs: omit config file when creating pre-1.5 format repos (r1547454,
r1561703)
Developer-visible changes:
- General:
* fix improper linking when serf is in the same prefix as existing svn
libraries. (r1609004)
* use proper intermediate directory when building with VS 2003-2008
(r1595431)
* support generating VS 2013 and later project files.
- Bindings:
* ruby: removing warning about Ruby 1.9 support being new. (r1593992)
* python: fix notify_func callbacks (r1594794, r1594814, r1594834, r1595061)
Version 1.8.9
(07 May 2014, from /branches/1.8.x)
http://svn.apache.org/repos/asf/subversion/tags/1.8.9
User-visible changes:
- Client-side bugfixes:
* log: use proper peg revision over DAV (r1568872)
* upgrade: allow upgrading from 1.7 with exclusive locks (r1572102 et al)
* proplist: resolve inconsitent inherited property results (r1575270 et al)
* increase minimal timestamp sleep from 1ms to 10ms (r1581305 et al)
* merge: automatic merge confused by subtree merge (issue #4481)
* propget: report proper error on invalid revision for url (r1586255)
* commit: fix an assertion when committing a deleted descendant
(r1571747, r1571787, r1571795)
* merge: resolve segfault when '--force' merges a directory delete
(r1577812, r1577813, r1579429)
* resolve: prevent interactive conflict resolution when nothing has been
done to resolve the conflict (r1577294)
* update: fix locks lost from wc with pre-1.6.17 servers (issue #4412)
* merge: honor the 'preserved-conflict-file-exts' setting (r1577151)
* list: fix '--verbose' against older servers (r1591111)
* unlock: fix ability to remove locks with timeouts (r1579588)
* copy: fix 'svn copy URL WC' on relocated working copies
(r1580626, r1580650)
* export: allow file externals to be exported (issue #4427)
* move: fix working copy db inconsistency in cert scenarios (issue #4437)
* commit: fix an issue where mixed revision copy with non copy descendants
that shadow a not present node couldn't be committed (r1518942 et al)
* delete: properly remove move_to info when the node in its original
location is removed (r1538812 et al)
* status; fix an issue where output would vary based on if the target
was the node itself or its parent (r1544597 et al)
- Server-side bugfixes:
* ensure proper access synchronization in fsfs on Windows (r1568953 et al)
* svnadmin dump: don't let invalid mergeinfo stop dump (r1574868 et al)
* svnserve: resolve performance regression caused by iprops (r1578853 et al)
* reduce size of memory buffer when reading config files (r1581296)
* remove dead transaction if commit was blocked by hook (r1583977)
* svnrdump load: fix crash when svn:* normalization (issue #4490)
* fix memcached support (issue #4470)
* svndumpfilter: fix order of node record headers (r1578670 et al)
* mod_dav_svn: blacklist building with broken versions of httpd; 2.2.25,
2.4.5 and 2.4.6 (r1545835)
* mod_dav_svn: allow generic DAV clients to refresh locks (issue #3515)
* mod_dav_svn: detect out of dateness correctly during commit (issue #4480)
Developer-visible changes:
- General:
* improve consistency checks of DAV inherited property requests (r1498000)
* fix ocassional failure in autoprop_tests.py (r1567752)
* avoid duplicate sqlite analyze information rows (r1571214)
* add Mavericks to our sysinfo output (r1573088)
* bump copyright years to 2014 (r1555403)
* unbreak test suite when running as root (r1583580)
* resolve buffer overflow in testcode (r1481782)
* fix libmagic detection with custom LDFLAGS (r1577200)
* fix an out of scope variable use in merge (r1587946)
* javahl: fix crash from resolve callback throwing an exception (r1586439)
* ruby: fix two memory lifetime bugs (r1586052, r1586467)
* fix a missing null byte when handling old pre-1.4 deltas (r1587968)
* fix building with APR 0.9.x (r1585499)
* make svn_ra_get_locks() and svn_ra_get_lock() report not locked nodes
with a NULL svn_lock_t *, as documented (r1578273, r1578311, r1578326)
* fix tests for compiler flags (r1573106)
Version 1.8.8
(19 Feb 2014, from /branches/1.8.x)
http://svn.apache.org/repos/asf/subversion/tags/1.8.8
User-visible changes:
- Client-side bugfixes:
* use CryptoAPI to validate intermediary certificates on Windows (r1564623)
* fix automatic relocate for wcs not at repository root (r1541638 et al)
* diff: fix when target is a drive root on Windows (r1541635)
* wc: improve performance when used with SQLite 3.8 (r1542765)
* copy: fix some scenarios that broke the working copy (r1560690)
* move: fix errors when moving files between an external and the parent
working copy (r1551524, r1551579)
* log: resolve performance regression in certain scenarios (r1553101 et al)
* merge: decrease work to detect differences between 3 files (r1548486)
* checkout: don't require flush support for symlinks on Windows (r1547774)
* commit: don't change file permissions inappropriately (issue #4440)
* commit: fix assertion due to invalid pool lifetime (r1553376 et al)
* version: don't cut off the distribution version on Linux (r1544878 et al)
* flush stdout before exiting to avoid information being lost (r1499470)
* status: fix missing sentinel value on warning codes (r1543145)
* update/switch: improve some WC db queries that may return incorrect
results depending on how SQLite is built (r1567109)
- Server-side bugfixes:
* reduce memory usage during checkout and export (r1564215)
* fsfs: create rep-cache.db with proper permissions (issue #3437)
* mod_dav_svn: prevent crashes with SVNListParentPath on (CVE-2014-0032)
* mod_dav_svn: fix SVNAllowBulkUpdates directive merging (r1548105)
* mod_dav_svn: include requested property changes in reports (r1557522)
* svnserve: correct default cache size in help text (r1563110)
* svnadmin dump: reduce size of dump files with '--deltas' (r1554978)
* resolve integer underflow that resulted in infinite loops (r1567985)
Developer-visible changes:
- General:
* fix ocassional failure of check_tests.py 12 (r1496127 et al)
* fix failure with SQLite 3.8.1-3.8.3 when built with
SQLITE_ENABLE_STAT3/4 due to bug in SQLite (r1567286, r1567392)
* specify SQLite defaults that can be changed when SQLite is built
to avoid unexpected behavior with Subversion (r1567064)
- API changes:
* numerous documentation fixes
* svn_client_commit_item3_dup() fix pool lifetime issues (r1550803)
* ra_serf: properly ask multiple certificate validation providers for
acceptance of certificate failures (r1535532)
* release internal fs objects when closing commit editor (r1555499)
* svn_client_proplist4() don't call the callback multiple times for
the same path in order to deliver inherited properties (r1549858 et al)
- Bindings:
* javahl: make test suite run without installing on OS X (r1535115)
* swig: fix building out of tarball on OS X (r1555654)
* swig-pl: fix with --enable-sqlite-compatibility-version (r1559009)
* swig: fix building bindings on OS X when APR has the -no-cpp-precomp
flag in the apr-config --cppflags output. (r1535610)
* swig: fix building from tarball with an out-of-tree build (r1543187)
Version 1.8.7
(Not released, see changes for 1.8.8.)
Version 1.8.6
(Not released, see changes for 1.8.8.)
Version 1.8.5
(25 November 2013, from /branches/1.8.x)
http://svn.apache.org/repos/asf/subversion/tags/1.8.5
User-visible changes:
- Client-side bugfixes:
* fix externals that point at redirected locations (issues #4428, #4429)
* diff: fix assertion with move inside a copy (issue #4444)
- Server-side bugfixes:
* mod_dav_svn: Prevent crashes with some 3rd party modules (r1537360 et al)
* mod_dav_svn: canonicalize paths properly (r1542071)
* mod_authz_svn: fix crash of mod_authz_svn with invalid config (r1541432)
* hotcopy: fix hotcopy losing revprop files in packed repos (issue #4448)
- Other tool improvements and bugfixes:
* mod_dontdothat: Fix the uri parser (r1542069 et al)
Developer-visible changes:
- General:
* fix compilation with '--enable-optimize' with clang (r1534860)
* fix compilation with debug build of BDB on Windows (r1501656, r1501702)
* fix '--with-openssl' option when building on Windows (r1535139)
* add test to fail when built against broken ZLib (r1537193 et al)
- Bindings:
* swig-rb: fix tests to run without installing on OS X (r1535161)
* ctypes-python: build with compiler selected via configure (r1536537)
Version 1.8.4
(29 October 2013, from /branches/1.8.x)
http://svn.apache.org/repos/asf/subversion/tags/1.8.4
User-visible changes:
- Client- and server-side bugfixes:
* fix assertion on urls of the form 'file://./' (r1516806)
* stop linking against psapi.dll on Windows (r1534102)
* translation updates for Swedish
- Client-side bugfixes:
* revert: fix problems reverting moves (issue #4436)
* update: fix assertion when file external access is denied (issue #4422)
* merge: reduce network connections for automatic merge (r1468980 et al)
* merge: fix path corruption during reintegration (r1523666)
* mergeinfo: fix crash (r1519955)
* ra_serf: verify the result of xml parsing (r1501199 et al)
* ra_serf: improve error messages during commit (r1526439)
* ra_local: fix error with repository in Windows drive root (r1518184)
* fix crash on windows when piped command is interrupted (r1522892)
* fix crash in the crash handler on windows (r1532023)
* fix assertion when upgrading old working copies (r1520529)
- Server-side bugfixes:
* hotcopy: cleanup unpacked revprops with '--incremental' (r1512300 et al)
* fix OOM on concurrent requests at threaded server start (r1527103 et al)
* fsfs: improve error message when unsupported fsfs format found (r1534158)
* fix memory problem in 3rd party FS module loader (r1519615 et al)
Developer-visible changes:
- General:
* allow compiling against serf 1.3 and later on Windows (r1517123)
- Bindings:
* javahl: canonicalize path for streamFileContent method (r1524869)
Version 1.8.3
(29 August 2013, from /branches/1.8.x)
http://svn.apache.org/repos/asf/subversion/tags/1.8.3
User-visible changes:
- Client- and server-side bugfixes:
* translation updates for Swedish
* enforce strict version equality between tools and libraries (r1502267)
* consistently output revisions as "r%ld" in error messags (r1499044 et al)
- Client-side bugfixes:
* status: always use absolute paths in XML output (issue #4398)
* ra_serf: 'svn log -v' fails with a 1.2.x server (issue #4044)
* ra_serf: fix crash when committing cp with deep deletion (issue #4400)
* diff: issue an error for files that can't fit in memory (r1513119 et al)
* svnmucc: generate proper error for mismatched URLs (r1511353)
* update: fix a crash when a temp file doesn't exist (r1513156)
* commit & update: improve sleep for timestamps performance (r1508438)
* diff: continue on missing or obstructing files (issue #4396)
* ra_serf: use runtime serf version for User-Agent (r1514315, r1514628)
* ra_serf: ignore case when checking certificate common names (r1514763)
* ra_serf: format distinguished names properly (r1514804)
* ra_serf: do not retry HTTP requests if we started to parse them (r1503318)
* ra_serf: output ssl cert verification failure reason (r1514785 et al)
* ra_serf: allow session reuse after SVN_ERR_CEASE_INVOCATION (r1502901)
* ra_serf: include library version in '--version' output (r1514295 et al)
* info: fix spurious error on wc root with child in conflict (r1515366)
- Server-side bugfixes:
* svnserve: fix creation of pid files (r1516556)
* svnadmin: fix output encoding in non-UTF8 environments (r1506966)
* svnsync: fix high memory usage when running over ra_serf (r1515249 et al)
* mod_dav_svn: do not map requests to filesystem (r1512432 et al)
* svnauthz: improve help strings (r1511272)
* fsfs: fixed manifest file growth with revprop changes (r1513874)
* fsfs: fix packed revprops causing loss of revprops (r1513879 et al)
- Other tool improvements and bugfixes:
* svnwcsub/irkerbridge: fix symlink attack via pid file (r175 from upstream)
Developer-visible changes:
- General:
* describe APR unimplemented errors as coming from APR (r1503010 et al)
* mod_dav_svn: update INSTALL to reflect configure defaults (r1515141)
* davautocheck: use the correct apxs binary by default (r1507889, r1507891)
- API changes:
* svn_config_walk_auth_data() config_dir arg: permit NULL (r1507382 et al)
- Bindings:
* swig-pl: fix SVN::Client not honoring config file settings (r150744)
* swig-pl & swig-py: disable unusable svn_fs_set_warning_func (r1515119)
Version 1.8.2
(Not released, see changes for 1.8.3.)
Version 1.8.1
(23 July 2013, from /branches/1.8.x)
http://svn.apache.org/repos/asf/subversion/tags/1.8.1
User-visible changes:
- Client- and server-side bugfixes:
* translation updates for German and Simplified Chinese
* improve sqlite error message output (r1497804)
* support platforms lacking mmap (r1498136)
* allow configuration files to start with UTF-8 BOM (r1499100 et al)
* don't fail on UTF-8 data when encoding conversion not available (r1503009)
* improve error messages when encoding conversion fails (r1503010)
- Client-side bugfixes:
* merge: rename 'automatic merge' to 'complete merge' (r1491432)
* mergeinfo: reduce network usage for '--show-revs' (r1492005)
* ra_serf: improve http status handling (r1495104)
- * merge: avoid unneeded ra session (r1493475)
+ * merge: avoid unneeded RA session (r1493475)
* merge: reduce network usage (r1478987)
* merge: remove duplicated ancestry check (r1493424, r1495597)
* ra_serf: fix 'Accept-Encoding' header for IIS interoperability (r1497551)
* svn status: improve documentation for lock columns (r1497318, r1497319)
* ra_serf: fix support for 'get-file-revs-reversed' capability (r1498456)
* log: reduce network usage on repository roots (r1496957)
* diff: avoid temporary files when calling external diff (issue #4382)
* upgrade: fix notification of 1.7.x working copies (r1493703, r1494171)
* fix crash during tree conflict resolution (issue #4388)
* interactive file merge: add two additional choices (r1491816, r1494089)
* diff: use local style paths in error messages (r1500680)
* resolve: improve the interactive conflict resolution menu (r1491739 et al)
* switch: use local style path in error message (r1500074)
* ra_serf: improve error output when receiving invalid XML (r1498851)
* svn cleanup: explain what the command does in help output (r1497310)
* blame: error on -r M:N where M>N unless server supports (r1498449 et al)
* gpg-agent auth: don't try to use agent when unavailable (r1500762 et al)
* gpg-agent auth: don't require GPG_TTY or TERM env vars (r1500801)
* update: fix some tree conflicts not triggering resolver (r1491868 et al)
* commit: remove stale entries from wc lock table when deleting (r1491756)
* merge: fix --record-only erroring out on renamed path (issue #4387)
* svnmucc: fix 'make install' symlink to work when DESTDIR is set (r1501072)
* wc: fix crash when target is symlink to a working copy root (issue #4383)
* ra_serf: change "internal malfunction" errors to normal errors (r1502577)
* ra_serf: handle proxies not supporting chunked requests (r1502401 et al)
- Server-side bugfixes:
* fsfs: resolve endless loop problem when repos/db/uuid has \r\n (r1492145)
* fsfs: remove revision property buffer limit (r1491770)
* mod_dav_svn: better status codes for anonymous user errors (r1495918)
* mod_dav_svn: better status codes for commit failures (r1490684)
* fix performance regression in 'svn log' against root (r1494913)
* allow deleting non-user-visible 'svn:' properties (r1495432)
* fsfs: fix crash on strict-alignment architectures (r1495806, r1495985)
* svnadmin upgrade: fix error of non-sharded fsfs repositories (r1494287)
* svnadmin create: deny '--fs-type=fsfs --compatible-version=1.0' (r1494223)
* svnadmin upgrade: fix data loss when cancelling in last stage (r1494298)
* mod_dav_svn: fix incorrect path canonicalization (r1503528)
See CVE-2013-4131, and descriptive advisory at
http://subversion.apache.org/security/CVE-2013-4131-advisory.txt
- Other tool improvements and bugfixes:
* fsfs-stats (tool): resolve segfault when passing invalid path (r1492164)
* svn-bench: fix help output (r1493951)
* svnpubsub: add version header to server (r1491707)
Developer-visible changes
- General:
* ra_serf: fix some test runner issues on Windows (r1490679)
* fix two issues in reverse svn_ra_get_file_revs() (r1492148, et al)
* handle --compatible-version=1.8 in the C tests (r1494342)
* improve clang compatibility (r1480080 et al)
* use proper cancel baton when handling conflicts (r1495850)
* fs: BDB: provide proper error value from BDB (r1495428)
* ra_serf: tweak connection failed error value (r1496132, et al)
* svn_client_log5: resolve possible segfault (r1496110)
* fix metadata_only move to work when target is unversioned node (r1498564)
* ra_svn: fix segfault with a NULL commit message (r1498550, r1499727)
* Ev2: correctly initialize node kind in shims' change table (r1501058)
* Ev2: fix copyfrom URL construction in shims (r1500226)
* fs: improve test against newlines in filenames (r1498483 et al)
* make building with BDB 6 an opt-in feature (r1499438)
* sqlite: allow placing amalgamation in build dir (r1499034, r1500175)
* ra_svn: make sessions usable after log callback early out (r1503554)
- Bindings:
* swig-rb: fix tests with out-of-tree-builds (r1492295)
* javahl: fix encoding of error messages produced by javahl (r1492264)
* swig-pl: silence compiler warnings (r1487094)
* swig-pl: improve documentation (r1488693, r1490721, r1500904)
Version 1.8.0
(18 Jun 2013, from /branches/1.8.x)
http://svn.apache.org/repos/asf/subversion/tags/1.8.0
User-visible changes:
- General:
* require serf as client-side http library (neon support removed) (r1349694)
* deprecate the Berkeley DB FS backend (libsvn_fs_base) (r1464985 et al)
- Major new features:
* working copy records moves as first-class operation (issue #3631, #4232)
* merge uses reintegrate mode automatically when needed (r1369896 et al)
* FSFS: Packing of revision property shards (issue #3944)
* support inheritable properties (r1395109)
* repository can suggest config for autoprops and ignores (r1401908)
* support gpg-agent for password caching (r1151069)
* authz rules can be stored inside the repository (r1424780)
- Minor new features and improvements (client-side):
* doubled svn:// protocol throughput (r1325899)
* optimize file/dir truename checks on Windows (r1435527)
* new 'commit --include-externals' option (related to issues #1167, #3563)
* new --include-externals option for 'svn list' (issue #4225)
* remove extraneous externals output from 'svn status -q' (issue #1935)
* reject some attempts to merge between unrelated branches (r1215273)
* new --ignore-properties option for 'svn diff' (r1239553, -617)
* new --properties-only option for 'svn diff' (r1336110)
* new --patch-compatible option for 'svn diff' (r1239561)
* new --no-diff-added option for 'svn diff' (r1433958)
* new w/c subtree duplication tool (tools/client-side/detach.py)
* new mergeinfo fixup tool (tools/client-side/mergeinfo-sanitizer.py)
* 'svn diff' can compare arbitrary files and directories (r1310291, et al)
* ra_serf avoids re-downloading content present in pristine store (r1333936)
* 'svn mergeinfo' now honors the --revision (-r) option (issue #4199)
* 'svn mergeinfo' now shows a summary graph by default (issue #4239)
* new --search and --search-and options for 'svn log' (r1354666, -83518)
* 'svn log' reports the node kind even for pre-1.6 revision files (r1242958)
* sort path list generated by "svn log -v --xml" (r1299323)
* new built-in interactive text conflict merge tool (r1357864, et al)
* 'svn --version' shows build system info (r1368662)
* 'svn --version --verbose' shows runtime environment info (r1370813 et al)
* 'svn' is now non-interactive when not run in a terminal device (r1424037)
* 'svn propset' checks spelling of reserved property names (r1470781)
* improve working copy performance on network disks (issue #4176)
* support for custom keyword definitions in svn:keywords (issue #890)
* svn:ignore __pycache__ directories by default (r1150073)
* 'svn diff --git' include copyfrom revision in "copied" headers (r1155279)
* svn:mergeinfo related operations now use much less memory (r1149519 et al)
* get list of supported schemes for RA libraries (r1148134)
* 'svn checkout' skips file externals from other repositories (r1153110)
* 'svn resolve' exits non-zero if conflicts could not be resolved (r1150439)
* let HTTPv2-aware clients fetch v2-style resources (r1161202)
* 'svn status' with better NLS support (r1157537, -682)
* better tracking of shallow-yet-complete merges (issues #4056, #4057)
* make 'svn status --quiet' w/ externals quieter still (issue #1935)
* ensure that conflict paths are shown relative-ized (r1337520)
* improve performance of local multi-target deletions (r1195873)
* various interactive conflict resolver improvements in 'svn' (r1440421 etc)
* improved tree diff implementation for diff and merge (r1440599 et al)
* tree conflicts on directories detected better during merges (issue #3150)
* allow reverting unmodified copies with 'svn remove' (r1442611)
* make 'svn diff' with mixed URL and local path targets work (r1442640)
* make 'svn patch' re-add deleted directories if needed (r1445333)
* make repos-wc diffs fully ancestry-aware (r1445904)
* 'svn diff --git' now implies 'svn diff --show-copies-as-adds' (r1446279)
* 'svn diff --show-copies-as-adds' now implies --notice-ancestry (r1446279)
* improved tree-conflict detection for 'svn switch' (r1449413, r1450582)
* allow up to 8 revision number digits in 'svn status -v' output (r1428637)
* show node kind (file or dir) in tree conflict descriptions (r1429907)
* restore deleted switched paths upon next update (issue #4295)
* add support for copying paths from foreign repositories (issue #3590)
* fix merge -cA,B with --accept option aborts if rA conflicts (issue #4238)
* 'svn resolve' interactive support; no longer requires --accept (r1336929)
* notify when removing externals leaves behind modified files (r1366021)
* new 'http-max-connections' configuration option for serf (r1421559)
* new 'http-bulk-updates' configuration option for serf (r1421490)
* 'svn cleanup' now runs SQLite "vacuum" to reclaim space (r1418459)
* 'svn info' displays repository-relative URL (r1415365)
* fix serf memory leak on checkout (issue #4194)
* detect duplicate paths setting svn:externals (issue #4227)
* make ra_serf work over HTTP/1.0 proxies (issue #3979)
* make ra_serf accept gzip compression for all responses (r1407454)
* double ra_serf performance for checkout and export (r1407545)
* improve network and disk i/o interleaving in ra_serf (r1407934)
* avoid assert in ra_serf when REPORT response was truncated (r1407935)
* rewrite ra_serf XML parser (r1409259 et al)
* ra_serf can create transaction with inline txnprops (r1375167)
* partially fix replace+propset of locked file fails over DAV (issue #3674)
* fix ra_serf doesn't handle bad baseline error from server (issue #4127)
* decreased default http timeout for ra_serf (issue #3968)
* prevent ra_serf from corrupting the working copy (issue #3993)
* ra_serf transmits property changes inline to reduce requests (r1378927)
* by default avoid SSL certificate prompts in client (issue #2410)
* improve interactive resolution of property conflicts (r1387678 et al)
* make ra_serf raise an error upon delta-base mismatch (issue #4235)
* tune ra_svn transmit buffer handling (r1391788)
* make 'svnrdump' work with serf (issue #4116)
* fix 'svnrdump' on path below repository root (issue #4101)
* support ipv6 in URLs (e.g. http://[::1]/svn/repos) (r1454047)
* conflict resolver now iterates paths in a sorted order (r1461820)
* mod_dav_svn does keyword expansion with 'kw=1' query arg (r1466055)
- Minor new features and improvements (server-side):
* improve performance of config file parsing (r1344347 et al)
* new 'svnadmin load --revision' load filtering support (issue #3734)
* new 'svnadmin hotcopy --incremental' support for FSFS (issue #3815)
* new 'svnadmin lock' / 'svnadmin unlock' subcommands (issue #3942, #4092)
* new SVNUseUTF8 configuration option for mod_dav_svn (issue #2487)
* new SVNHooksEnv configuration option for mod_dav_svn (r1239966)
* new SvnPubSub distributed commit hooks (tools/server-side/svnpubsub)
* new light-weight benchmarking client (tools/client-side/svn-bench)
* svndumpfilter dependency analysis (tools/server-side/svnpredumpfilter.py)
* new automatic working copy updater (tools/server-side/svnpubsub)
* new 'svnadmin freeze' subcommand (r1376228)
* 'svndumpfilter' now supports --delta dumpfiles (r1351009, -3745)
* new --drop-all-emtpy-revs option for 'svndumpfilter' (issue #3681)
* client version info now reported to commit hooks (issue #4124)
* txn name now reported to post-commit hooks (r1240856)
* support for server-side keyword expansion in mod_dav_svn (r1466055)
* FSFS now able to cache revision properties (r1326307)
* FSFS cache for changed-paths increases 'svn log' performance (r1378358)
* FSFS cache mergeinfo requested during 'log -g' (r1395439)
* many FSFS caching improvements (r1390435, r1390447)
* directory and property deltification option in FSFS (issue #4084)
* fine-grained control deltification behavior via fsfs.conf (r1311476)
* FSFS de-duplication ("rep sharing") now works within a revision (r1397773)
* FSFS de-duplication now works for properties as well (r1243312)
* read FSFS data using fewer fopen calls (issue #3372)
* 'svnadmin verify' will now check meta data (issues #3956, #4211)
* 'svnadmin verify' now checks for issue #4129 style corruption (r1304656)
* new --client-speed option for svnserve (r1391788)
* new --single-threaded option in svnserve (r1296018)
* hook script templates are now marked as executable (r1153414)
* error out on non-canonical fspaths in the authz file (r1166111)
* improve path lookup performance in FSFS (r1442088)
* svnserve now logs explicit path and reason for authz failures (r1446542)
* validate offsets from rep-cache to prevent FSFS corruption (issue #4277)
* new AuthzSVNGroupsFile option to store authz groups separately (r1438407)
* new 'SVNAllowBulkUpdates prefer' option for mod_dav_svn (r1417642, et al)
* new 'SVNMasterVersion' option for mod_dav_svn (r1398962)
* added virtual-host support to 'svnserve' (r1401296)
* new fsfs-stats tool which prints FSFS repository stats (r1410995)
* new fsfs-reorg tool to optimize FSFS packing (r1383214, r1385395)
* new --compatible-version option for 'svnadmin create' (r1407279 )
* new --ignore-properties option for 'svnlook diff' (r1407905)
* new --properties-only option for 'svnlook diff' (r1407905)
* new --diff-cmd option for 'svnlook diff' (r1413449)
* allow leading "r"'s in http: ?p= and ?r= query parameters (r1221463)
* faster 'svn ls' for large directories (r1296627)
* mod_dav_svn now advertises supported POST types (r1375123)
* mod_dav_svn can create transaction with inline txnprops (r1375167)
* run start-commit hook after transaction creation (r1376201)
* avoid byte-for-byte comparison where it can be avoided (r1390641)
* various server-side performance improvements for 'log -g' (r1395442 et al)
* allow up to 10Gbit throughput with svnserve (r1391788)
* install mod_dontdothat correctly (r1454450)
* svnadmin verify can now verify transactions (r1462353)
* FSFS verifies revisions as they are added (r1462409)
- Client-side bugfixes:
* fix inconsistent 'svn log' output for empty revisions (issue #3964)
* fix mis-ordered text output of 'svn log --diff' on Windows (r1220783)
* fix 'svn log --diff' on moved file (issue #4153).
* fix 'svn revert' of 'svn move' (issue #876)
* fix file externals wrongly "resurrecting" a deleted file (#4017)
* fix reporting of corrupted 1.6 w/cs by 'svn upgrade' (r1182904, -9)
* fix bug caused by URI-decoding local merge source paths (r1210539)
* fix properties out of sync with repos after merge and revert (issue #4305)
* fix merge of replacement on local delete fails (issue #4011)
* fix replacements on deletes produce wrong tree conflicts (issue #3806)
* made ra_serf handle location headers that are not RFC-compliant (r1443906)
* merge no longer errors out after resolving all conflicts (issue #4316)
* fix svn blame mis-categorizing file type as binary (issue #2089)
* fix externals not removed when working copy is made shallow (issue #3741)
* fix update under add with not-present parent (issue #4111)
* fix revert of files with svn:needs-lock under copied dirs (r1343168)
* fix repos->wc diff of local copied/moved-here directories (r1341927)
* fix repos->wc diff of local copied/moved-here files (r1341544)
* fix "svn diff -cN PATH" where PATH was deleted in rN (r1338708)
* fix dependency on APR hash order in several logic paths (r1338350 et al)
* fix path inconsistencies in 'svn diff' output (r1338291)
* fix misleading error message printed by 'svn switch' (issue #2337)
* fix bug in mergeinfo recording during foreign-repos merge (r1430310)
* fix spurious merge conflicts for binary files with keywords (issue #4221)
* fix patching symlinks with 'svn patch' (issue #4273)
* make 'svn switch' refresh lock information (issue #3376)
* fix 'svn diff' output doesn't apply as patch without fuzz (issue #3362)
* fix mergeinfo recording for multiple-revision-range merge (issue #4306)
* fix diffs shown by 'show-diff' conflict prompt option (r1438879)
* don't print an update summary header with no content (r1439480)
* make 'svn rm' remove externals registrations below its targets (r1361256)
* fix crashes in ra_serf where AVG 2012 Surf-Shield is in use (issue #4175)
* don't raise conflicts on identical binary files (issue #4128)
* improve error messages when wc.db missing (issue #4118)
* fix 'svn diff' showing wrong text change (issue #4270)
* fix 'svn diff -rN' failing to show local replace (issue #3797)
* fix 'svn diff' showing wrong revision (issue #4010)
* fix 'svn merge' showing spurious notifications (issue #2910)
* parse '.@HEAD' correctly (issue #3606)
* fix 'svn revert' after conflict in sparse working copy (issue #4168)
* fix bug in global/per-server config handling in serf (r1421516)
* properly display errors from serf (r1398742)
* fix crash in ra_serf (r1408291)
* fixed svnmucc propset and propdel on repository root (issue #3663)
* fix 'svn info' output with ancient svnserve servers (pre-1.2) (r1409732)
* ra_serf shows error message for 408 Request Timeout response (r1410983)
* fix handling of "\ No newline ..." in diff/patch (r1411723, r1412382)
* allow infinite http timeout in ra_serf (r1411976)
* using unknown svn: property names now requires --force (issue #4261)
* fix handling of case insensitive configuration files (r1215089)
* properly handle errors during password caching (r1380695)
* fix svnversion output not always a number (issue #4226)
* fix conflict resolver losing executable bit of a file (r1391019)
* fix redundant notifications when merging with ra_serf (issue #3802)
* fix 'svn add --force /path/to/wcroot' should work (issue #4241)
* fix file permissions changed after commit (issue #4331)
* improve handling of http errors in ra_serf (1452792, 1452870)
* include checksum of missing pristines in error message (r1452800)
* fix an assert when merging against a replaced source (issue #4132)
* fix replacement in merge source has incorrect notification (issue #4138)
* improve performance of checkout (r1453791)
* fixed documentation regarding merge source (issue #3247)
* fix merge errors out after resolving conflicts (issue #4316)
* fix delete/move with file external in unversioned dir (issue #4293)
* fix resolving tree conflict with local node missing (r1461848)
* fix invalid read during diff suffix scanning (issue #4339)
* fix assertion when running 'svn log <SOME_URL>@PREV' (r1462134)
* optimize enumerating configuration options (r1464478)
* revert will now sleep for timestamps if using commit times (r1464769)
* don't allow externals to be deleted with 'svn rm' (r1464992)
* improved memory usage in ra_serf and ra_local (r1465280)
* replace some assertions with more helpful error messages (r1465975)
* fixed long keyword expansion truncated (issue #4349)
- Server-side bugfixes:
* SVNParentPath / repository listing now authz-filtered (r1408184)
* user/group names in the authz config file are case-sensitive (r1475772)
* limit commit runtime for nodes with very deep histories (r1224836)
* 'svnadmin recover' truncates rep-cache at the right point (issue #4077)
* fix crashes in dumpstream loading with skipped revs (r1214202, r1214216)
* fix 'svn log -g' incorrectly treating rename as merge (issue #4022)
* fix bug where fsfs file-hinting fails (issue #4320)
* don't leak path of repository on server's disk to clients (r1330906)
* remove spurious is-fresh-txn-root from empty revision files (issue #4031)
* fix a stdout handling problem in 'svnlook diff' (r1411971)
* fix erratic behaviour in 'svnlook diff' showing property diffs (r1412224)
* fix inconsistent authz error messages in 'svn log' in svnserve (r1292462)
* fix svndumpfilter for empty paths in included or excluded lists (r1294583)
* make fsfs packing threadsafe (r1376011)
* don't error out on intermittent memcached failures (r1394470)
* fix a ra_svn deadlock with zero-copy server option (r1465622)
- Other tool improvements and bugfixes:
* 'svnmucc' promoted to first-class supported utility (issue #3308, #4279)
* make 'svnmucc' prompt for log messages (issue #3418)
* rename 'svnauthz-validate' to 'svnauthz' (issue #4284)
* make 'svnauthz' optionally validate user/path access (r1197588)
* fix mailer.py test suite problems (r1449582)
* fix mailer.py not showing dirs with property deletions (r1449582)
* make mailer.py generate Date and Message-ID headers (r1449592)
* new '-?' option support for 'svnmucc' (r1339428)
* provide the repository name to mailer.py (r1439592)
* add '--force-interactive' to svnmucc (r1457789)
* add '--trust-server-cert' to svnmucc (r1458995)
Developer-visible changes:
- General:
* now require Python 2.5 for tests and dev tools (r1243627)
* now require bzip2 for tests and dev tools (r1148512)
* configure defaults to --without-apache-libexecdir (r1469862)
* support builds with APR pool debugging (r1176894)
* 'make extraclean' is more thorough now (r1149460)
* support for Serf 2 (r1147538)
* introduction of editor v2 (via private APIs only) (r1166332 et al)
* improve SQLite setup for compatibility with OS X 10.7. (r1181666)
* rework switch statement to accommodate OWC compiler limitations (r1204407)
* new --enable-sqlite-compatibility-version configure option (r1201421)
* make test suite LD_LIBRARY_PATH include just-built auth plugins (r1200474)
* packages/ directory removed, contents were outdated and unused (r1442167)
* rename 'makefile.ezt' to 'build-outputs.mk.ezt' (r1444822)
* use expensive compiler optimizations with --enable-optimize (r1445063)
* in Visual C++ builds, move temp files to different directory (r1446416)
* remove --with-ssl and --with-gssapi configure options (r1449023)
* require at least serf 1.2.0 as build dependency (issue #4296)
* fix error tracing to record file/line properly (r1331242)
* add --log-level argument to win-tests.py (r1335461)
* improve GDB pretty-printing of svn types (r1351336, r1364750, r1365035)
* load third-party FS modules (if --enable-runtime-module-search) (r1362434)
* enable running the regression tests over https (r1349699)
* support 'make davautocheck' on OS X (r1421583)
* new '--enable-gcov' configure option (r1416646)
* fix build with Apache HTTPD 2.5 (r1408985)
* allow running the test suite through a http proxy (r1410195)
* don't use non-constant initializers in struct variables (r1412911)
* allow generation of Visual Studio 2012 compatible projects (r1245152)
* nicer pretty-printing of Subversion data types in gdb (r1367262 et al)
* teach serf build on Windows to use static APR/Util and OpenSSL (r1371338)
* add --ssl-cert option to win-tests.py to run tests over https (r1372760)
* don't strip Content-Type header form .po files on Windows (r1380056)
* configure now script auto-detects GNOME keyring (r1387230)
* allow configure to detect BDB on Debian-based Linux distros (r1390633)
* auto-detect serf via pkg-config (r1391662)
* improve queries for compatibility with SQLite 3.7.16 (r1455239)
* remove support for in-tree apr, apr-util and apr-memcache (r1456924)
* FSFS caching supports prefixes now (r1462436)
* maintainer mode now prints symbolic error codes (r1465157)
* don't require NLS support for kwallet support (r1466445)
* make Julian happy (r1413030)
- API changes:
* fix inconsistent handling of log revs without changed paths (issue #3694)
* deprecated SVN_ERR_SQLITE_UNSUPPORTED_SCHEMA (r1173240)
* provide API to clear cached auth credentials (issue #2775)
* improve repository location information in various APIs (issue #4170)
* major rewrite of conflict storage and handling APIs (r1354973 et al)
* hide (deprecate) svn_wc APIs that use editors (r1243339)
* svn_stringbuf_ensure() allocates an extra byte for terminator (r1308966)
* switch and update apis are now more consistent (r1465292)
* deprecated svn_client_merge_reintegrate (r1466742)
* deprecated low level ra_svn apis (r1466907)
- Bindings:
* star-imports in swig-py only import 'svn_*' symbols (r1303375)
* fix compilation of Perl bindings on Mandriva 2007 (issue #2617)
* new JavaHL testing targets (r1182983)
* enable returning an error on malfunctions for JavaHL (r1366215)
* MacOS X build fix to cope with missing GNOME keyring (r1397844)
* fix swig bindings tests on MacOS X (r1397846)
* fix assertion failure in JavaHL error reporting (r1405922)
* support ruby 1.9 (r1407206)
* JavaHL: Include OSGI Manifest information in svn-javahl.jar (r1234864)
* new svn_auth_set_gnome_keyring_unlock_prompt_func function (r1241554)
* fix svn_txdelta window ops for python bindings (r1389054)
* fix build of Perl bindings with newer versions of SWIG (r1389658)
* add missing API functions to Perl bindings (issue #2646)
* add missing API functions to Python bindings (r1392038 et al)
* add missing API functions to JavaHL bindings (issue #4326)
* fix some reference counting bugs in swig-py bindings (r1464899, r1466524)
+
+
+Version 1.7.21
+(5 Aug 2015, from /branches/1.7.x)
+http://svn.apache.org/repos/asf/subversion/tags/1.8.21
+
+ User-visible changes:
+ - Client-side bugfixes:
+ * cp: fix 'svn cp ^/A/D/H@1 ^/A' to properly create A (r1674455, r1674456)
+ * fix issue #4551: svnrdump load commits wrong properties, or fails, on a
+ non-deltas dumpfile (r1652182 et al.)
+
+ - Server-side bugfixes:
+ * fix 'svnadmin recover' for pre-1.4 FSFS repositories (r1561419)
+
+ Developer-visible changes:
+ - General:
+ * support building on Windows with Visual Studio 2012, 2013 and 2015 (r1687158, r1692783)
+
+ - Bindings:
+ * swig-pl: fix some stack memory problems (r1668618, 1671388)
+
+Version 1.7.20
+(31 Mar 2015, from /branches/1.7.x)
+http://svn.apache.org/repos/asf/subversion/tags/1.7.20
+
+ User-visible changes:
+ - Client-side bugfixes:
+ * fix 'svn patch' output for reordered hunks (issue #4533)
+
+ - Server-side bugfixes:
+ * reject invalid get-location-segments requests in mod_dav_svn and
+ svnserve (r1667233)
+ * mod_dav_svn: reject invalid txnprop change requests (r1667235)
+
+ - Client-side and server-side bugfixes:
+ * properly duplicate svn error objects (r1651759)
+ * fix use-after-free in config parser (1646785, r1646786, r1646797)
+
+ Developer-visible changes:
+ * add lock file config for testing against HTTPD 2.4+ (r1544302, r1544303)
+ * fix build with absolute path to neon install (r1664789)
+
+
+Version 1.7.19
+(15 Dec 2014, from /branches/1.7.x)
+http://svn.apache.org/repos/asf/subversion/tags/1.7.19
+
+ User-visible changes:
+ - Client-side bugfixes:
+ * rm: display the proper URL in commit log editor (r1591123)
+ * diff: fix invalid read during suffix scanning (issue #4339)
+ * fix crash in eclipse IDE with GNOME Keyring (issue #3498)
+ * checkout/update: fix file externals failing to follow history and
+ subsequently silently failing (issue #4185)
+
+ - Server-side bugfixes:
+ * svnadmin dump: don't let invalid mergeinfo prevent a dump (issue #4476)
+ * mod_dav_svn: reject requests with missing repository paths (r1643409)
+ * mod_dav_svn: reject requests with invalid virtual transaction names
+ (r1643437)
+ * mod_dav_svn: avoid unneeded memory growth in resource walking
+ (issue #4531)
+
+ Developer-visible changes:
+ - General:
+ * properly generate a version resource when building on Windows (r1542610,
+ r1564576, r1568180)
+ * fix a problem with the unix build that could result in linking to the
+ wrong Subversion libraries at build or at run time (r1594157)
+ * use a proper intermediate directory when building with Visual Studio
+ 2003-2008 (r1595431)
+ * fix LIBTOOL_M4 and LIBTOOL_CONFIG variable not be evaluated properly
+ during a unix build (r1637826)
+ * allow the use of libtool 2.4.3 (r1640862, r1640873, r1643793)
Version 1.7.18
(11 Aug 2014, from /branches/1.7.x)
http://svn.apache.org/repos/asf/subversion/tags/1.7.18
User-visible changes:
- Client-side bugfixes:
* guard against md5 hash collisions when finding cached credentials
(r1550691, r1550772, r1600909)
* ra_serf: properly match wildcards in SSL certs. (r1615211, 1615219)
* ra_serf: ignore the CommonName in SSL certs where there are Subject Alt
Names (r1565531, r1566503, r1568349)
Developer-visible changes:
- General:
* fix ocassional failure in checkout_tests.py test 12. (r1496127)
* disable building ZLib's assembly optimizations on Windows.
Version 1.7.17
(07 May 2014, from /branches/1.7.x)
http://svn.apache.org/repos/asf/subversion/tags/1.7.17
User-visible changes:
- Client-side bugfixes:
* log: use proper peg revision over DAV (r1568872)
* move: fix errors when moving files between an external and the parent
working copy (r1551579)
* copy: fix 'svn copy URL WC' on relocated working copies
(r1580626, r1580650)
- Server-side bugfixes:
* mod_dav_svn: blacklist building with broken versions of httpd; 2.2.25,
2.4.5 and 2.4.6 (r1545835)
* mod_dav_svn: detect out of dateness correctly during commit (issue #4480)
Developer-visible changes:
- General:
* fix libmagic detection with custom LDFLAGS (r1577200)
* fix a missing null byte when handling old pre-1.4 deltas (r1587968)
* support building with Serf 1.3.x (r1517123, r1535139)
- Bindings:
* javahl: fix crash from resolve callback throwing an exception (r1586439)
Version 1.7.16
(26 Feb 2014, from /branches/1.7.x)
http://svn.apache.org/repos/asf/subversion/tags/1.7.16
User-visible changes:
- Client-side bugfixes:
* copy: fix some scenarios that broke the working copy (r1560690)
* diff: fix regressions due to fixes in 1.7.14 (issue #4460)
- Server-side bugfixes:
* mod_dav_svn: prevent crashes with SVNListParentPath on (CVE-2014-0032)
* reduce memory usage during checkout and export (r1564215)
Developer-visible changes:
- General:
* fix failure in checkout_tests.py
* support compiling against Cyrus sasl 2.1.25 (r1404912, r1413402)
* support compiling against neon 0.30.x (r1566320)
Version 1.7.15
(Not released, see changes for 1.7.16.)
Version 1.7.14
(25 Nov 2013, from /branches/1.7.x)
http://svn.apache.org/repos/asf/subversion/tags/1.7.14
User-visible changes:
- Client- and server-side bugfixes:
* fix assertion on urls of the form 'file://./' (r1516806)
- Client-side bugfixes:
* upgrade: fix an assertion when used with pre-1.3 wcs (r1530849)
* ra_local: fix error with repository in Windows drive root (r1518184)
* fix crash on windows when piped command is interrupted (r1522892)
* fix externals that point at redirected locations (issues #4428, #4429)
* diff: fix incorrect calculation of changes in some cases (issue #4283)
* diff: fix errors with added/deleted targets (issues #4153, #4421)
- Server-side bugfixes:
* mod_dav_svn: Prevent crashes with some 3rd party modules (r1537360 et al)
* fix OOM on concurrent requests at threaded server start (r1527103 et al)
* fsfs: limit commit time of files with deep change histories (r1536790)
* mod_dav_svn: canonicalize paths properly (r1542071)
- Other tool improvements and bugfixes:
* mod_dontdothat: Fix the uri parser (r1542069 et al)
Developer-visible changes:
- Bindings:
* javahl: canonicalize path for streamFileContent method (r1524869)
Version 1.7.13
(29 Aug 2013, from /branches/1.7.x)
http://svn.apache.org/repos/asf/subversion/tags/1.7.13
User-visible changes:
- General
* merge: fix bogus mergeinfo with conflicting file merges (issue #4306)
* diff: fix duplicated path component in '--summarize' output (issue #4408)
* ra_serf: ignore case when checking certificate common names (r1514763)
- Server-side bugfixes:
* svnserve: fix creation of pid files (r1516556)
* mod_dav_svn: better status codes for commit failures (r1490684)
* mod_dav_svn: do not map requests to filesystem (r1512432 et al)
Developer-visible changes:
- General:
* support linking against gssapi on Solaris 10 (r1515068)
* don't use uninitialized variable to produce an error code (r1482282)
- Bindings:
* swig-pl: fix SVN::Client not honoring config file settings (r150744)
* swig-pl & swig-py: disable unusable svn_fs_set_warning_func (r1515119)
Version 1.7.12
(Not released, see changes for 1.7.13.)
Version 1.7.11
(23 Jul 2013, from /branches/1.7.x)
http://svn.apache.org/repos/asf/subversion/tags/1.7.11
User-visible changes:
- General
* translation updates for Simplified Chinese
- Server-side bugfixes:
* mod_dav_svn: fix incorrect path canonicalization (r1503528)
See CVE-2013-4131, and descriptive advisory at
http://subversion.apache.org/security/CVE-2013-4131-advisory.txt
- Other tool improvements and bugfixes:
* fix argument processing in contrib hook scripts (r1485350)
See CVE-2013-2088, and descriptive advisory at
http://subversion.apache.org/security/CVE-2013-2088-advisory.txt
Developer-visible changes:
- Bindings:
* javahl: fix bug in error constructing code (r1405922)
Version 1.7.10
(30 May 2013, from /branches/1.7.x)
http://svn.apache.org/repos/asf/subversion/tags/1.7.10
User-visible changes:
- Client-side bugfixes:
* fix 'svn revert' "no such table: revert_list" spurious error (issue #4168)
* fix 'svn diff' doesn't show some locally added files (issue #3797)
* fix changelist filtering when --changelist values aren't UTF8 (r1475724)
* fix 'svn diff --git' shows wrong copyfrom (issue #4294)
* fix 'svn diff -x-w' shows wrong changes (issues #4133 and #4270, r1427278)
* fix 'svn blame' sometimes shows every line as modified (issue #4034)
* fix regression in 'svn status -u' output for externals (r1434750)
* fix file permissions change on commit of file with keywords (issue #4331)
* improve some fatal error messages (r1465975)
* fix externals not removed when working copy is made shallow (issue #3741)
- Server-side bugfixes:
* fix FSFS repository corruption due to newline in filename (issue #4340)
See CVE-2013-1968, and descriptive advisory at
http://subversion.apache.org/security/CVE-2013-1968-advisory.txt
* fix svnserve exiting when a client connection is aborted (r1482759)
See CVE-2013-2112, and descriptive advisory at
http://subversion.apache.org/security/CVE-2013-2112-advisory.txt
* fix svnserve memory use after clear (issue #4365)
* fix repository corruption on power/disk failure on Windows (r1483781)
Developer-visible changes
- General:
* make get-deps.sh compatible with Solaris /bin/sh (r1451678)
* fix infinite recursion bug in get-deps.sh (r1421541, r1424977)
* fix uninitialised output parameter of svn_fs_commit_txn() (r1461743)
- Bindings:
* fix JavaHL thread-safety bug (r1476359)
Version 1.7.9
(04 Apr 2013, from /branches/1.7.x)
http://svn.apache.org/repos/asf/subversion/tags/1.7.9
User-visible changes
- Client-side bugfixes:
* improved error messages about svn:date and svn:author props. (r1440620)
* fix local_relpath assertion (issue #4257)
* fix memory leak in `svn log` over svn:// (r1458341)
* fix incorrect authz failure when using neon http library (issue #4332)
* fix segfault when using kwallet (r1421103)
- Server-side bugfixes:
See CVE-2013-1845, CVE-2013-1846, CVE-2013-1847, CVE-2013-1849,
and CVE-2013-1884, and descriptive advisories at
http://subversion.apache.org/security/
* svnserve will log the replayed rev not the low-water rev. (r1461278)
* mod_dav_svn will omit some property values for activity urls (r1453780)
* fix an assertion in mod_dav_svn when acting as a proxy on / (issue #4272)
* improve memory usage when committing properties in mod_dav_svn (r1443929)
* fix svnrdump to load dump files with non-LF line endings (issue #4263)
* fix assertion when rep-cache is inaccessible (r1422100)
* improved logic in mod_dav_svn's implementation of lock. (r1455352)
* avoid executing unnecessary code in log with limit (r1459599)
Developer-visible changes:
- General:
* fix an assertion in dav_svn_get_repos_path() on Windows (r1425368)
* fix get-deps.sh to correctly download zlib (r13520131)
* doxygen docs will now ignore prefixes when producing the index (r1429201)
* fix get-deps.sh on freebsd (r1423646)
- Bindings:
* javahl status api now respects the ignoreExternals boolean (r1435361)
Version 1.7.8
(17 Dec 2012, from /branches/1.7.x)
http://svn.apache.org/repos/asf/subversion/tags/1.7.8
User-visible changes
- Client- and server-side bugfixes:
* Fix typos in pt_BR, es and zh_TW translations (r1402417, r1402421)
- Client-side bugfixes:
* fix crash with --username option on Windows (r1396285)
* add missing attributes to "svn log -v --xml" output (r1398100)
* fix svn patch ignoring hunks after no trailing newline (r1399174)
* fix hang with ra_serf during error processing (r1403583)
* ignore file externals with mergeinfo when merging (r1401915)
* fix "svnmucc cp" segfault with a missing last argument (issue #4079)
* fix conflict handling on symlinks (issue #4091)
- Server-side bugfixes:
* properly detect threading availability (r1398325)
* fix "svnadmin load --bypass-prop-validation" (r1237779)
* fix parsing of [groupsfoo] sections in authz file (issue #3531)
* add Vary: header to GET responses to improve cacheability (r1390653)
* fix fs_fs to cleanup after failed rep transmission (r1403964, et al)
* fix mod_dav_svn to complain about revisions > HEAD (r1403588)
Developer-visible changes:
- General:
* fix incorrect status returned by 1.6 API (r1403258)
* fix compilation with g++ 4.7 (r1345740)
* fix svn_uri_get_file_url_from_dirent on Windows (r1409146)
Version 1.7.7
(09 Oct 2012, from /branches/1.7.x)
http://svn.apache.org/repos/asf/subversion/tags/1.7.7
User-visible changes
- Client- and server-side bugfixes:
* fix memory read bug (r137614)
* update Chinese translation
- Client-side bugfixes:
* fix issues with applying Git patch files (r1374800, et al)
* fix status does not descend into dir externals after upgrade (issue #4016)
* fix file externals don't update with old mod_dav_svn (issue #4224)
* fix external diff tool duplicates Index: lines with 'svn diff' (r1380697)
* fix GNOME keyring library fails with very old glib (r1378847)
* fix unknown password stores in config file cause error (r1375052)
* fix assertions in ra_serf running against server root (r1365519, et al)
* fix ra_serf checkout/export aborts early on Windows (issue #4174)
- Server-side bugfixes:
* fix an assert with SVNAutoVersioning in mod_dav_svn (issue #4231)
* fix unbounded memory use with SVNPathAuthz short_circuit (r1387943)
* fix svndumpfilter exclude --targets requires leading slash (issue #4234)
* fix connection ttl for memcache should be 50 seconds (r1391641)
* stabilize order of paths in dumpfiles with APR 1.4.6 (r1344864, et al)
Developer-visible changes:
- General:
* print "All tests successful" at the end of 'make check' (r1375089)
* fix sandbox violation in a test (r1371282)
* fix tests fail when running within a format 30 WC (r1391188, et al)
* fix return value of svn_client_update4() incorrect (r1380295)
* fix make check summary missing test failures (r1390965)
* fix build does not fail when apache httpd is not available (r1374198)
- Bindings:
* fix swig-pl build fails with swig 2.0.7 and newer. (r1389658)
* fix swig-py runtime problems with swig 2.0.5 and newer (r1351117)
Version 1.7.6
(15 Aug 2012, from /branches/1.7.x)
http://svn.apache.org/repos/asf/subversion/tags/1.7.6
User-visible changes:
- Client- and server-side bugfixes:
- Client-side bugfixes:
* Fix "svn status -u --depth empty FILE" (r1348822, r1349215)
* Fix example output of 'svn help status' (issue #3962)
* propset of svn:eol-style might not notice related text changes (r1353572)
* sort output of 'svn propget -R' (r1355699)
* sort output of 'svn proplist' (r1355698)
* sort output of 'svn status' (r1341012)
* avoid a filestat per working copy find operation (r1340556)
* optimize 'svn upgrade' performance on large working copies (r1342984)
* allow 'file:///C:\repos' style arguments on Windows, like 1.6 (r1346765)
* fix ra_serf against Subversion 1.2 servers (r1349367)
* fix 'svn upgrade' on working copies with certain tree conflicts (r1345482)
* avoid workqueue references to system temp dir (r1367854)
* allow non-existent canonical paths (r1367853)
* fix 'svn revert --depth files' to operate on files (r1365554)
* fix ra_serf XML namespace handling against malicious server (r1337441)
* fix relocate with server-relative externals (issue 4216)
* change two asserts into errors for TortoiseSVN (r1368128, r1368065)
* don't attempt to anchor an operation outside a wc root (r1361341)
- Server-side bugfixes:
* partial sync drops properties when converting to adds (issue #4184)
* replaying a copy and delete of an unreadable child fails (issue #4121)
* allow svnlook to operate on r0 (r1362508)
* make FSFS revision files independent of APR hash order (r1367498)
- Other tool improvements and bugfixes:
* move mod_dontdothat to install-tools (r1307177)
Developer-visible changes:
- General:
* fix running tests against httpd 2.4 (r1291594)
* use constant struct initialisers for C89 compatibility (r1352068)
- Bindings:
* JavaHL: Don't assert on some invalid input (r1354626, r1354652)
* JavaHL: Add missing new in 1.7 notifications (r1351772)
Version 1.7.5
(17 May 2012, from /branches/1.7.x)
http://svn.apache.org/repos/asf/subversion/tags/1.7.5
User-visible changes:
- Client- and server-side bugfixes:
* http: report deleted-revision upon delete during update (r1327474)
- Client-side bugfixes:
* avoid potential segfault when canonicalizing properties (r1296369)
* improve memory and file-handle management with externals (issue #4130)
* serf: convert assertions to "MERGE failed" errors (r1302417)
* fix undefined behaviour during multi-segment reverse merges (issue #4144)
* fix potential use of already freed memory during diff operation (r1311935)
* improve performance of scan for the working copy root (r1306334)
* cmdline: fix segfault during 'svn diff' argument processing (r1311702)
* fix regression from 1.6 in update with --depth option (issue #4136)
* propset: avoid undefined behaviour in error path (r1325361)
* reset sqlite statements, partly for sqlite-3.7.11 compat (r1328846, et al)
* fix assertion during 'svn diff -r BASE:HEAD ^/trunk' (issue #4161)
* notify upon 'update' just removing locks on files (r1329876)
* neon: fix potential use of freed memory during commits (r1329388)
* 'status --xml' doesn't show repository deletes correctly (issue #4167)
* fix assert on svn:externals with drive letter on Windows (issue #4073)
* fix 'svn update --depth=empty' against 1.4 servers (issue #4046)
* handle missing svn:date reported by svnserve gracefully (r1306111)
* fix merges which first add a subtree and then delete it (issue #4166)
* fix a regression with checkout of file externals (issue #4087)
* don't add spurious mergeinfo to subtrees in edge-case merge (issue #4169)
* improve performance of status on large working copies (issue #4178)
- Server-side bugfixes:
* fix non-fatal FSFS corruption bug with concurrent commits (issue #4129)
* mod_dav_svn: raise an error on MERGE of non-existent resource (r1298343)
* mod_dav_svn: support compiling/running under httpd-2.4 (r1232267)
* mod_dav_svn: forbid BDB repositories under httpd's event MPM (issue #4157)
- Other tool improvements and bugfixes:
* emacs support: updates to dsvn.el and vc-svn.el (r1200896, et al)
Developer-visible changes:
- General:
* windows example distribution scripts: include svnrdump (r1295007)
* fix running the test suite with jsvn (r1335555)
- Bindings:
* swig-py tests: avoid FAILs on APR hash order (r1296137, r1292248)
* swig-rb tests: avoid FAILs on APR hash order (r1310535, r1310594)
* swig-pl: Improved perl detection in gen-make.py (r1291797, r1291810)
Version 1.7.4
(08 Mar 2012, from /branches/1.7.x)
http://svn.apache.org/repos/asf/subversion/tags/1.7.4
User-visible changes:
* fix 'svn log --diff' for moved paths (r1210147, et al)
* fix ra_serf problem with reading directory entries via HTTPv2 (r1238121)
* prepend "sqlite:" to error messages from SQLite (r1245738, -817)
* fix randomly missing "Merged via" notifications in 'svn log -g' (r1293229)
* fix spurious conflict when merging deleted symbolic link (issue #4052)
* fix URL-to-WC copy of externals on Windows (issue #4123)
* improve an FSFS sanity-check error message (r1294470)
* fix regressions with symlinks pointing at externals (issue #4102)
* fix 'svn log --diff' output ordering issue on Windows (r1295671)
Developer-visible changes:
* don't build mod_dontdothat if not building with httpd (r1243976)
* fix the testsuite to avoid FAILs on APR hash order (r1230714, et al)
Version 1.7.3
(14 Feb 2012, from /branches/1.7.x)
http://svn.apache.org/repos/asf/subversion/tags/1.7.3
General:
* ship mod_dontdothat with the standard release
User-visible changes:
* fix segfault on 'svn rm $ROOT_URL' (issue #4074)
* replace a couple of assertions in favor of errors (r1207858, -949)
* fix a server assert after being upgraded (r1210195)
* fix segfault on 'svn mkdir svn://localhost' (r1211483)
* make 'svnadmin recover' prune the rep cache (r1213331, et al)
* make svnmucc use values from --config-dir option
* update and clarify the merge help text (r1154121, et al)
* replace wc assertion with informative error (r1222521, -693)
* copy permissions correctly for FSFS dirs (r1229252)
* improve 'svn log --with-all-revprops' over ra-dav (issue #4082)
* fix segfault when remapping a file external (issue #4093)
* fix segfault caused by obstructing unversioned dir (r1229677)
* fix regression on first update of external dir with '-r' (issue #4053)
* fix various EOL-handling problems in 'svn patch' (issues #3814, #3991)
* fix segfault in 'svn revert' (r1229303)
* improve correctness of 'svn patch --dry-run' (r1231944, -5)
* enforce revisions given in 'svn:externals' (issue #4053)
* fix potential corruption on 32-bit FSFS with large files (r1230212)
* make 'svn status --xml' show new files (issue #4097)
* fix 'svn mergeinfo' correctness (issue #4050)
* return the correct status for non-present nodes (r1232202, -07, -21, -22)
* improve SASL error messages (r1236343, et al)
* improve server cert error code for ra_serf (r1232413)
* fix SVNParentPath listings for parent path symlinks (r1221767, -80)
* fix mod_dav_svn's handling of POST errors (issue #4086)
* log some mod_dav_svn errors, rather than ignoring them (r1237720, -9596)
* relax requirements for canonicalization in mod_dav_svn (r1236173)
* fix a rare source of FSFS corruption (r1240752)
* allow committing the result of some copy operations (issue #4059)
* prevent one-byte buffer overflow in base64 decoding (r1242337)
Developer-visible changes:
* JavaHL: Add missing notify action, fixing an exception (r1221793)
* fix swig-py memory leak (r1235264, -296, -302, -736)
* fix spurious test suite failure (r1220742, -50)
* allow running tests on UNC shares (r1225491)
* bindings: see platform-specific password providers (r1242660, -1)
* skip 'svnrdump dump' tests over ra_serf (r1242537)
* convert a few ra_serf assertions to errors (r1242607)
Version 1.7.2
(02 Dec 2011, from /branches/1.7.x)
http://svn.apache.org/repos/asf/subversion/tags/1.7.2
User-visible changes:
* fix working copy corruption after interrupted update/switch (issue #4040)
* avoid segfaults against pre-1.5 servers (r1186928)
* improve configure error message if apr-util uses old or no bdb (r1186784)
* make 'svn patch' ignore '/dev/null' targets for compat with git (r1197998)
* fix 'svn patch' segfault on patch that skips and deletes files (r1199950)
* omit "Committed revision N." output from 'svn commit --quiet' (r1200837)
* fix authz denial when svnserve root is a repository (issue #4060)
* fix uninitialized memory read in client diff code (r1201002)
* avoid potential segfault during merges (r1202807)
* fix an assertion failure when a symlink is updated (r1186944, -81, -83)
* make working copy operations fail if nodes have no base checksum (r1202630)
* fix nested <Location>s when using v2 protocol (r1203546, -651, -653)
* make mod_dav_svn ignore non-Subversion POST requests (r1187695)
* avoid reading freed memory (r1204478)
* recognize empty (only byte order mark) UTF-8 files as text (issue #4064)
* fix 1.7 client regression when operating against a 1.0.x server (r1199876)
* remove empty parent dirs of removed externals on update (issue #4044)
* make 'svn diff -c N' work for files added in rN (issue #2873)
* plug a memory leak in the bdb backend (r1205726)
* fix 'svn import' with native eol-style and inconsistent EOLs (r1205193)
* fix reading beyond the end of a string in bdb backend (r1205839, -48)
* don't assert when committing an incomplete directory (issue #4042)
Developer-visible changes:
* JavaHL: allow 'status -u' to function properly (r1189190, -395)
* don't put '\r' characters in our generate sql headers (r1189580)
* properly define WIN64 on Windows x64 builds (r1188609)
* better adherence to C89 in enum definitions (r1189665)
* bump copyright year in Windows DLLs (r1189261)
* log a better error when opening rep-cache.db fails (r1204610, -73)
Version 1.7.1
(24 Oct 2011, from /branches/1.7.x)
http://svn.apache.org/repos/asf/subversion/tags/1.7.1
User-visible changes:
* improve performance of 'svn info' (r1164386)
* improve hash table sorting performance (r1167659)
* update bash completion for 1.7 (r1177001)
* make 'svn ls' continue to work with 1.0-1.3 repos (r1154278, -379, -82)
* improve handling of error messages generated by Cyrus SASL (r1179767)
* update INSTALL documentation file (r1182115, and others)
* error instead of assert when upgrading corrupt WCs (r1182904, -9)
* improve memory usage in merge (r1176915)
* fix an invalid assertion in merge (r1149103, -35)
* improve performance of 'merge --reintegrate' in edge-case (r1167681)
* fixed: 'svn mergeinfo' shows wrong revisions for added nodes (issue #3791)
* make 'svn add --parents D/file' work if D is deleted (r1185222)
* improve performance of trivial text file merges (issue #4009)
* add FSFS sanity check to prevent corruption seen in the wild (r1178280)
* improve correctness/performance of recursive info and proplist (r1164426)
* fix memory leak in 'merge --reintegrate' (r1180154)
* fix handling of directories after 'update --set-depth=empty' (r1185911)
* fix 'checksum != NULL' assertions in some upgraded WCs (r1177732)
* fix upgrading of WCs containing authz-restricted dirs (r1185738)
* make the server tolerate svn:mergeinfo with malformed paths (r1182771)
* fix some erroneous warning messages from the svn client (r1185746)
* fix WC upgrade with replaced nodes in edge-case (issue #4033)
Developer-visible changes:
* fix object lifetime issues in the JavaHL bindings (r1175888)
* fix org.tigris JavaHL wrappers to avoid double finalize (r1179680)
* don't write to const memory (r1177492)
* improve zlib configuration (r1174761, -98, -806)
* improve SQLite runtime init for OS X 10.7 compat (r1181666)
* improve test suite correctness (r1174111)
* fix potential segfault seen by TSVN (r1183263)
* fix backward compat crashes in JavaHL (r1183054, -347)
* fill in repos_* fields of svn_wc_status3_t for repos-only nodes (r1181609)
* disable the SQLite shared process cache (r1185242, r1185280)
Version 1.7.0
(11 Oct 2011, from /branches/1.7.x)
http://svn.apache.org/repos/asf/subversion/tags/1.7.0
See the 1.7 release notes for a more verbose overview of the changes since
the 1.6 release: http://subversion.apache.org/docs/release-notes/1.7.html
User-visible changes:
- General:
* No longer including contrib/ in the release tarballs (r877798)
- Major new features:
* Less verbose HTTP-based repository access protocol (issue #1161, #3371)
* Rewritten working copy metadata storage (issue #3357)
* New 'svn patch' subcommand (issue #511)
* Rewritten FSFS in-memory caching for better performance
* New remote repository dump/load client 'svnrdump'
- Minor new features and improvements:
* Better handling of HTTP redirects (issue #2779)
* Improved and much more consistent path handling (issue #2028, and others)
* 'svnadmin load' rewrites changed revnums in mergeinfo (issue #3020)
* Error message and help text improvements
* 'svn log' can print unidiff of changes made in a revision (issue #2909)
* 'svn diff' can print git-style unidiff annotations
* svnsync can now steal locks on a mirror repository (issue #3309)
* display the wc root in the output of 'svn info' (issue #3355)
* add 'svnlook filesize' (issue #3509)
* add 'svn upgrade' command for upgrading working copies (r877675)
* add 'svnsync --disable-locking' (issue #3545)
* subtree merges don't unconditionally stop reintegrate merge (issue #3577)
* 'svn relocate' replaces 'svn switch --relocate' (r1026475)
* 'svn relocate' updates relative externals (issue #3597)
* allow svnsync users to specify the source repo (issue #3637)
* remove redundant mergeinfo notifications for 2-URL merges (issue #3671)
* 'svn export' into the current directory (issue #3727)
* added '--parents' to 'svn update' (issue #3748)
* allow configurable connection timeout in ra_serf (r876161)
* add digest authentication in ra_serf (r876405)
* add extensive caching support to servers (r1067669, -75, -72302)
* add configurable caching to svnadmin (r1078357)
* make server-side network data compression rate configurable (r1072288)
* added support for auto-detecting mime-types with libmagic (r1131120)
* 'svn rm url1 url2 url3' uses single txn per repo (issue #1199)
* don't leave unversioned files when reverting copies (issue #3101)
- Client-side bugfixes:
* 'svn cp A B; svn mv B C' is equivalent to 'svn cp A C' (issue #756)
* revert fetches missing directories from the server (issue #1040)
* allow subdirs of moved dirs to be moved and committed (issue #1259)
* improved performance of 'svn mv' with whole directories (issue #1284)
* 'svn rm B; svn cp A B' now works (issue #1516)
* 'svn diff URL1 URL2' now reverse of 'svn diff URL2 URL1' (issue #2333)
* error if relocating to an unused URL (issue #2531)
* 'svn blame -rWORKING' is now supported (issue #2544)
* improve correctness of commit on a relocated wc over ra_dav (issue #2578)
* add early error to 'svn add --auto-props' with mixed eols (issue #2713)
* allow 'svn diff' to accept symlinks as targets (issue #2716)
* don't lose props for replaced items (issue #2743)
* handle mergeinfo for subtrees removed outside of svn (issue #2915)
* add ability to force 'svn diff' to use internal diff (issue #3701)
* correctly recover a schedule-for-delete rm'd outside of svn (issue #3106)
* don't create self-referential mergeinfo from own history (issue #3157)
* improve 'svn log -g' handling of bad mergeinfo source paths (issue #3270)
* better conflict stat printing (issue #3342, issue #3594)
* 'svn update' restores excluded files (issue #3544)
* allow reintegrate merges into WCs with missing subtrees (issue #3603)
* more gracefully error when given back cmdline input (issue #3620)
* update exit codes to reflect command failure (issue #3622)
* don't double-update file externals (issue #3665)
* improve output of multi-target update (issue #3693, #3746)
* make 'svn up --set-depth=exclude FILE' work (issue #3736)
* return correct error code for 'svn cat' on nonexisting file (issue #3713)
* support svn:externals on locally added directories (issue #2267)
* use installed GSSAPI lib for Kerberos in ra_serf (r877381)
* allow 'svn info' to run on an excluded item (issue #3792)
* improve 'log -g' output with reverse merges (issue #3176)
* don't print error message if stdout is a pipe and is closed (issue #3014)
* removed special copy-handling during updates added in 1.5.0 (issue #3711)
* fix warning about copies committed with non-infinity depth (issue #3752)
* can now commit multiple wc paths lacking a common parent (issue #2381)
* 'svn export --depth $WC' now works correctly (issue #3800)
* added support for case-only renames on Windows (issue #3702)
* 'svn delete --force' removes tree conflicts (issue #3805)
* don't throw an error when skipping tree conflicts in update (issue #3329)
* don't break commits of wc->wc copies with file externals (issue #3589)
* allow 'svn info' to work on symlinks to working copies (issue #2305)
* allow 'svn st --show-updates' to work across symlinks (issue #3117)
* 'svn revert' shouldn't loop on symlinks (issue #3972)
* fixed: wc-to-wc copy of a switch source (issue #1802)
* fixed: 'svn st' reports symlinks as obstructed items (issue #2284)
* fixed: 'cd e:\; svn up e:\' fails (issue #2556)
* fixed: svn aborts on commiting from root dir on windows (issue #3346)
* fixed: removing a dir scheduled for deletion corrupts wc (issue #2741)
* fixed: 'svn cleanup' fails on obstructed paths (issue #2867)
* fixed: case-only renames resulting from merges don't work (issue #3115)
* fixed: 'svn mergeinfo' ignores peg rev for wc target (issue #3180)
* fixed: unable to merge to wc of deleted branch (issue #3221)
* fixed: move via merge leaves behind versioned move source (issue #3324)
* fixed: ra_serf does not honor http-proxy-exceptions (issue #3428)
* fixed: 'svn mv A B; svn mv B A' loses history (issue #3429)
* fixed: ra_serf doesn't support http-auth-types config (issue #3435)
* fixed: merge sets incorrect mergeinfo on skipped paths (issue #3440)
* fixed: ra_serf inconsistent handling of cached authn creds (issue #3450)
* fixed: ra_serf sefault with using NTLM or Negotiate auth (r876910)
* fixed: excluded subtrees are not detected by svnversion (issue #3461)
* fixed: submitting a changelist while obstructed item exists (issue #3484)
* fixed: crash when changing an external's URL (issue #3530)
* fixed: target moved after branching breaks reintegrate (issue #3640)
* fixed: potential race condition in svnsync (issue #3546)
* fixed: spurious merge conflicts with pre-1.7 mod_dav_svn (issue #3657)
* fixed: repeat merge is not a no-op (issue #3564)
* fixed: inheritance results in self-referential mergeinfo (issue #3668)
* fixed: inheritance results in nonexistent mergeinfo sources (issue #3669)
* fixed: memory leaks in ra_serf (issue #3684)
* fixed: corruption of 'svn pg' output for large properties (issue #3721)
* fixed: 'svnsync copy-revprops' doesn't sync revprop dels (issue #3728)
* fixed: executable flag not correctly set on merge (issue #3686)
* fixed: 'svn rm' fails on multiple URLs with encoded spaces (issue #3839)
* fixed: children of replaced dirs cannot be deleted (issue #3468)
* fixed: executable flag of binary file lost during merge (issue #3686)
* fixed: merging a symlink-turned-regular-file breaks the wc (issue #2530)
* fixed: can't remove file externals (issue #3351)
* fixed: 'svn unlock' attempts to unlock wrong token on DAV (issue #3794)
* fixed: forced DAV 'svn unlock' results in 403, not warning (issue #3801)
* fixed: rm -> ci -> cp = missing directory (issue #2763)
* fixed: 'svn info' returns parent info on missing dirs (issue #3178)
* fixed: spurious prop conflict with 'merge --reintegrate' (issue #3919)
* fixed: 'svn --version' fails with non-existent $HOME (issue #3947)
* fixed: unforced export silently overwites existing file (issue #3799)
* fixed: reverse merge which adds subtree mergeinfo fails (issue #3978)
* fixed: 'svn up -r{R>HEAD}' hangs client over ra_svn (issue #3963)
* fixed: 'svn up' updates file externals in target siblings (issue #3819)
* many other minor bugfixes, optimizations, plugs of memory leaks, etc
- Server-side bugfixes:
* mod_dav_svn is less strict about auto-merging for commits (issue #1704)
* allow SVNListParentPath to be used with authz (issue #2753)
* allow nav to repo list from repo top with SVNListParentPath (issue #3159)
* allow repositories in the root of a drive on windows (issue #3535)
* don't destroy mergeinfo with 'svnadmin load --parent-dir' (issue #3547)
* fixed: 'svnadmin hotcopy' does not duplicate symlinks (issue #2591)
* fixed: post-revprop-change errors cancel commit (issue #2990)
* fixed: mod_dav_svn runs pre-revprop-change hook twice (issue #3085)
* fixed: mod_dav_svn doesn't return stderr to user on failure (issue #3112)
* fixed: hotcopy may corrupt target rep-cache.db (issue #3596)
* fixed: mod_dav_svn can cause spurious merge conflicts (issue #3657)
* fixed: DAV can overwrite directories during copy (issue #3314)
* fixed: 'svn log' returns log of unrelated path (issue #3931)
* match paths against authz rules in case sensitive way (issue #3781)
* svnserve can now force usernames to upper/lower case (issue #3726)
* reduce duplicate log messages in 'log -g' (issue #3650)
* svnserve: don't crash on shutdown with SASL in inetd mode (issue #3664)
* disallow arbitrary HTTP headers from committers (issue #2872)
* limit FSFS memory consumption (issue #3478, #3593)
* many other minor bugfixes too numerous to list here
- Other tool improvements and bugfixes:
* svnsync now takes the '--config-option' argument (issue #2027)
* svnsync can translate non-UTF-8 properties to UTF-8 (issue #3817)
* svnadmin now errors on non-UTF-8 revision properties (issue #3755)
* svnadmin verify now errors on non-UTF-8 paths (r1129641)
Developer-visible changes:
- General:
* improved output of 'make check'
* introduce scratch_pool/result_pool parameter paradigm
* improved error tracing (r877208, -736)
* improve building with sqlite on Windows (issue #3364)
* allow mod_dav_svn to compile against Apache 2.4 (issue #3548)
* support running tests against older servers (r876016)
* notification of unversioned obstructions (r877344)
* removed virtually all abort() calls (issue #2780)
* don't include client-specific suggestions in error msgs (issue #3887)
- API changes:
* don't crash svn_client_copy if ctx->log_msg_func is NULL (issue #3234)
* much improved ra_serf error handling (issue #3375)
* provide clients with old and new revision on update (r876515)
* close both files, even on error in svn_stream_copy3() (r887262)
* added 'work-in-progress' XFail test status (r876549)
* notifications sent when mergeinfo changes (r877588)
* add information on text and property mods in log APIs (r877688)
* fixed: svn_ra_local__get_file() leaks file descriptors (issue #3290)
* svn_ra_neon__get_dir() returns correct dir set for URLs (issue #3093)
* swig-py: always set ChangedPath.path (also for deletes) (issue #2630)
* improve conflict resolver API for a specific direction (issue #3049)
- Bindings:
* New JavaHL package: org.apache.subversion
* Deprecate the SVNClientSynchronized class in JavaHL (issue #2755)
* fixed setting binary properties in JavaHL (issue #3770)
* fix type mapping of svn_txdelta_window_t in python bindings (issue #3688)
Version 1.6.23
(30 May 2013, from /branches/1.6.x)
http://svn.apache.org/repos/asf/subversion/tags/1.6.23
User-visible changes
- Server-side bugfixes:
* fix FSFS repository corruption due to newline in filename (issue #4340)
* fix svnserve exiting when a client connection is aborted (r1482759)
See CVE-2013-2112, and descriptive advisory at
http://subversion.apache.org/security/CVE-2013-2112-advisory.txt
- Other tool improvements and bugfixes:
* fix argument processing in contrib hook scripts (r1485350)
Version 1.6.22
(Not released, see changes for 1.6.23.)
Version 1.6.21
(04 Apr 2013, from /branches/1.6.x)
http://svn.apache.org/repos/asf/subversion/tags/1.6.21
User-visible changes
- Server-side bugfixes:
* mod_dav_svn will omit some property values for activity urls (r1453780)
* improve memory usage when committing properties in mod_dav_svn (r1443929)
* fix mod_dav_svn runs pre-revprop-change twice (issue #3085)
* fixed: post-revprop-change errors cancel commit (issue #2990)
* improved logic in mod_dav_svn's implementation of lock. (r1455352)
Developer-visible changes:
- General:
* fix a compatibility issue with g++ 4.7 (r1345740)
Version 1.6.20
(04 Jan 2013, from /branches/1.6.x)
http://svn.apache.org/repos/asf/subversion/tags/1.6.20
User-visible changes
- Client- and server-side bugfixes:
* Fix typos in pt_BR, es and zh_TW translations (r1402417)
- Server-side bugfixes:
* add Vary: header to GET responses to improve cacheability (r1390653)
* fix fs_fs to cleanup after failed rep transmission (r1403964, et al)
* fix an assert with SVNAutoVersioning in mod_dav_svn (issue #4231)
Version 1.6.19
(10 Sep 2012, from /branches/1.6.x)
http://svn.apache.org/repos/asf/subversion/tags/1.6.19
- Client-side bugfixes:
* handle missing svn:date reported by svnserve gracefully (r1306111)
- Server-side bugfixes:
* fix possible server hang if a hook script fails to start (r1330410)
* fix write-through proxy commit regression introduced in 1.6.17 (r1088602)
* partial sync drops properties when converting to adds (issue #4184)
- Developer-visible changes:
* fix the testsuite to avoid FAILs on APR hash order (r1230714, et al)
Version 1.6.18
(29 Mar 2012, from /branches/1.6.x)
http://svn.apache.org/repos/asf/subversion/tags/1.6.18
User-visible changes:
* reject invalid svn:mergeinfo at commit time over DAV (issue #3953)
* fix reintegrate merge regression introduced in 1.6.13 (issue #3957)
* make the stderr output of the post-commit hook XML-safe (r893478)
* fix a rare source of FSFS corruption (r1240752)
* plug a memory leak in the bdb backend (r1205726)
* server-side performance fix for "log -g" (r1152282)
* fix description of svndumpfilter's --targets option (r1151911)
* fix datastream corruption during resumed transfer in ra_serf (r1154733)
* fix a crash in ra_svn SASL authentication (r1166555, -678)
* fix potential corruption on 32-bit FSFS with large files (r1230212)
* make website links point to subversion.apache.org (r896893, -901, r915036)
* fix non-fatal FSFS corruption bug with concurrent commits (issue #4129)
Developer-visible changes:
* fix sqlite distfile retrieval in get-deps.sh (r1134734)
* fix swig-py memory leak (r1235264, -296, -302, -736)
* allow passing --with-jdk to gen-make.py on Windows (r966167)
Version 1.6.17
(01 Jun 2011, from /branches/1.6.x)
http://svn.apache.org/repos/asf/subversion/tags/1.6.17
User-visible changes:
* improve checkout speed on Windows (issue #3719)
* make 'blame -g' more efficient with large mergeinfo (r1094692)
* avoid some invalid handle exceptions on Windows (r1095654)
* preserve log message with a non-zero editor exit (r1072084)
* fix FSFS cache performance on 64-bit platforms (r1103665)
* make svn cleanup tolerate obstructed directories (r1091881)
* fix deadlock in multithreaded servers serving FSFS repositories (r1104093)
* detect very occasional corruption and abort commit (issue #3845)
* fixed: file externals cause non-inheritable mergeinfo (issue #3843)
* fixed: file externals cause mixed-revision working copies (issue #3816)
* fix crash in mod_dav_svn with GETs of baselined resources (r1104126)
See CVE-2011-1752, and descriptive advisory at
http://subversion.apache.org/security/CVE-2011-1752-advisory.txt
* fixed: write-through proxy could directly commit to slave (r917523)
* detect a particular corruption condition in FSFS (r1100213)
* improve error message when clients refer to unknown revisions (r939000)
* bugfixes and optimizations to the DAV mirroring code (r878607)
* fixed: locked and deleted file causes tree conflict (issue #3525)
* fixed: update touches locked file with svn:keywords property (issue #3471)
* fix svnsync handling of directory copyfrom (issue #3641)
* fix 'log -g' excessive duplicate output (issue #3650)
* fix svnsync copyfrom handling bug with BDB (r1036429)
* server-side validation of svn:mergeinfo syntax during commit (issue #3895)
* fix remotely triggerable mod_dav_svn DoS (r1130303)
See CVE-2011-1783, and descriptive advisory at
http://subversion.apache.org/security/CVE-2011-1783-advisory.txt
* fix potential leak of authz-protected file contents (r1130303)
See CVE-2011-1921, and descriptive advisory at
http://subversion.apache.org/security/CVE-2011-1921-advisory.txt
Developer-visible changes:
* fix reporting FS-level post-commit processing errors (r1104098)
* fix JVM recognition on OS X Snow Leopard (10.6) (r1028084)
* allow building on Windows with recent Expat (r1074572)
Version 1.6.16
(02 Mar 2011, from /branches/1.6.x)
http://svn.apache.org/repos/asf/subversion/tags/1.6.16
User-visible changes:
* more improvement to the 'blame -g' memory leak from 1.6.15 (r1041438)
* avoid a crash in mod_dav_svn when using locks (r1071239, -307)
See CVE-2011-0715, and descriptive advisory at
http://subversion.apache.org/security/CVE-2011-0715-advisory.txt
* avoid unnecessary globbing for performance (r1068988)
* don't add tree conflicts when one already exists (issue #3486)
* fix potential crash when requesting mergeinfo (r902467)
* don't attempt to resolve prop conflicts in 'merge --dry-run' (r880146)
* more fixes for issue #3270.
Developer-visible changes:
* ensure report_info_t is properly initialized by ra_serf (r1058722)
* locate errors properly on a malfunction (r1053208)
* fix output param timing of svn_fs_commit_txn() on fsfs (r1051751)
* for svn_fs_commit_txn(), set invalid rev on failed commit (r1051632, -8)
* fix sporadic Ruby bindings test failures (r1038792)
* fix JavaHL JVM object leak when dumping large revisions (r947006)
* use Perl to resolve symlinks when building swig-pl (r1039040)
* allow Perl bindings to build within a symlinked working copy (r1036534)
* don't overwrite the LD_LIBRARY_PATH during make check-swig-pl (r946355)
* improve unit tests for some fs functions (r1051744, -5, -3185, -241)
Version 1.6.15
(26 Nov 2010, from /branches/1.6.x)
http://svn.apache.org/repos/asf/subversion/tags/1.6.15
User-visible changes:
* hide unreadable dirs in mod_dav_svn's GET response (r996884)
* make 'svnmucc propsetf' actually work (r1005446)
* limit memory fragmentation in svnserve (r1022675)
* fix 'svn export' regression from 1.6.13 (r1032970)
* fix 'svn export' mistakenly uri-encodes paths (issue #3745)
* fix server-side memory leaks triggered by 'blame -g' (r1032808)
This has been tracked as CVE-2010-4644
* prevent crash in mod_dav_svn when using SVNParentPath (r1033166)
This has been tracked as CVE-2010-4539
* allow 'log -g' to continue in the face of invalid mergeinfo (issue #3270)
* filter unreadable paths for 'svn ls' and 'svn co' (r997026, -070, -474)
* fix abort in 'svn blame -g' (issue #3666)
* fix file handle leak in ruby bindings (issue #3512)
* remove check for 1.7-style working copies (issue #3729)
Developer-visible changes:
* improve some swig parameter mapping (r984565, r1035745)
* improve test accuracy over dav (r991534, r877814)
* create fails.log for test runs (r964349)
* improve detection of 'svnversion' when building (r877219, et al)
* don't violate API layering in dumpstream logic (issue #3733)
* don't report working copy installs as switched (r1033921)
Version 1.6.14
(Not released, see changes for 1.6.15.)
Version 1.6.13
(01 Oct 2010, from /branches/1.6.x)
http://svn.apache.org/repos/asf/subversion/tags/1.6.13
User-visible changes:
* don't drop properties during foreign-repo merges (issue #3623)
* improve auto-props failure error message (r961970)
* improve error message for 403 status with ra_neon (r876615)
* don't allow 'merge --reintegrate' for 2-url merges (r959004)
* improve handling of missing fsfs.conf during hotcopy (r980811, -1449)
* escape unsafe characters in a URL during export (issue #3683)
* don't leak stale locks in FSFS (r959760)
* better detect broken working copies during update over ra_neon (r979045)
* fsfs: make rev files read-only (r981921)
* properly canonicalize a URL (r984928, -31)
* fix wc corruption with 'commit --depth=empty' (issue #3700)
* permissions fixes when doing reintegrate merges (related to issue #3242)
* fix mergeinfo miscalculation during 2-url merges (issue #3648)
* fix error transmission problems in svnserve (r997457, -66)
* fixed: record-only merges create self-referential mergeinfo (issue #3646)
* fixed: 'SVNPathAuthz short_circuit' unsolicited read access (issue #3695)
See CVE-2010-3315, and descriptive advisory at
http://subversion.apache.org/security/CVE-2010-3315-advisory.txt
* make 'svnmucc propset' handle existing and non-existing URLs (r1000607)
* add new 'propsetf' subcommand to svnmucc (r1000612)
* warn about copied dirs during 'svn ci' with limited depth (r1002094)
Developer-visible changes:
* make ruby bindings compatible with Ruby 1.9 (r957507)
* use the repos verify API in JavaHL (r948916)
* teach ra_serf to parse md5 checksums with update editors (r979429)
* let ra_serf work with current serf releases (r879757, r880320, r943796)
Version 1.6.12
(21 Jun 2010, from /branches/1.6.x)
http://svn.apache.org/repos/asf/subversion/tags/1.6.12
User-visible changes:
* further improvements for issue #3242
* allow deletion of uris which need character escaping (issue #3636)
* fix errors with 'svn mkdir --parents' (issue #3649)
* update address to which crash reports are sent (r901304)
* check for server certificate revocation on Windows (r898048)
* disable custom file mutexes on Windows (r879902, -16)
* fix handling of peg revision'd copy targets (issue #3651)
* more improvements to 'svn merge --reintegrate' (r935631)
* allow copying of broken symlinks (issue #3303)
* improve rep-sharing performance on high-concurrency repos (issue #3506)
* fixed: added subtrees with mergeinfo break reintegrate (issue #3654)
* fixed: assertion triggered by tree-conflicted externals (issue #3469)
Developer-visible changes:
* give windows devs more flexibility with sqlite versions (r944635)
* allow the pack tests to work with low file descriptor limits (r937610)
* improve exception handling on Windows Vista and 7 (r878447, -910, -916)
Version 1.6.11
(19 Apr 2010, from /branches/1.6.x)
http://svn.apache.org/repos/asf/subversion/tags/1.6.11
User-visible changes:
* fix for repositories mounted via NFS (issue #3501)
* enable TCP keep-alives in svnserve (r880552)
* tighten restrictions on revprops for 'svnadmin verify' (r904594)
* make ra_serf give better out-of-date information (issue #3561)
* improve error message upon connection failure with svn+ssh:// (r922516)
* allow 'svn log' on an uncommitted copy/move destination (r901752)
* make 'svnadmin hotcopy' copy the fsfs config file (r905303)
* mergeinfo improvements with non-inheritable mergeinfo (issue #3573)
* make mergeinfo queries not require access to the repo root (issue #3242)
* update URLs to refer the new apache.org repository (r904301, -94)
* update relative externals during a switch (issue #3390)
* fix 'merge --reintegrate' with self-referential mergeinfo (r892050, -85)
* improve wc-ng working copy detection (r929382)
* improve handling of mergeinfo when using serf (r880461)
* fixed: 'svnlook plist --revprop' with '-t TXN_NAME' (r917640, -8211)
* fixed: file external from URL cannot overwrite existing item (issue #3552)
* fixed: potential memory error in 'svn status' (r923674, -9)
* fixed: merge records mergeinfo from natural history gaps (issue #3432)
* fixed: theoretical possibility of DB corruption (r926151, -67)
Developer-visible changes:
* disable checks for wc-ng working copies when running the test suite
* on Windows, don't ignore move operation error codes (r896915)
* more precise reporting of errors occuring with sqlite init (r927323, -8)
* ensure rangelist APIs are commutative (r923389, -91)
Version 1.6.10
(Not released, see changes for 1.6.11.)
Version 1.6.9
(25 Jan 2010, from /branches/1.6.x)
http://svn.apache.org/repos/asf/subversion/tags/1.6.9
User-visible changes:
* allow multiple external updates over ra_svn (issue #3487)
* fix a segmentation fault when using FSFS (r881905)
* support Berkeley DB 4.8 (r879688)
* various autoprop improvements (r880274, -5)
* improve usage of svn+ssh:// on Windows (issue #2580)
* teach 1.6.x to recognize 1.7 working copies (1.6.x-future-proof branch)
* update help text for 'svn update' and 'svn switch' (r886164, -97)
* make 'svnadmin load --parent-dir' create valid mergeinfo (r888979, -9081)
* tolerate relative merge source paths in mergeinfo (r889840)
* teach mod_dav_svn to support the Label header (issue #3519)
* fixed: svnsync leaves stale sync-locks on mirrors (r884842)
* fix applicability of 'svn resolve --accept=theirs-conflict' (r880525, -6)
* fixed: segfault in 'svn resolve' (r896522, -47)
* fix commit failure against an out-of-date mirror (r900797)
Developer-visible changes:
* update ruby bindings test expectation (r880162)
* don't allow rangelist and mergeinfo API to modify input args (r879093)
Version 1.6.8
(Not released, see changes for 1.6.9.)
Version 1.6.7
(Not released, see changes for 1.6.9.)
[ Note: All revision numbers for versions prior to 1.6.7 reference the
original repository on svn.collab.net. For more information see:
http://svn.apache.org/repos/asf/subversion/README ]
Version 1.6.6
(22 Oct 2009, from /branches/1.6.x)
http://svn.apache.org/repos/asf/subversion/tags/1.6.6
User-visible changes:
* fix crash during 'svn update' (r39673)
* respect Apache's ServerSignature directive (r40008, -21, -31)
* don't add a file with mixed line endings, and then abort (issue #2713)
* support Neon 0.29.
* fix a crash in 'svn rm --force' (r37953)
* handle tree conflicts involving replacements (issue #3486)
* allow non-threadsafe sqlite if APR has no threads (r39301)
* print newline before plaintext SSL cert / password prompts (r38982, r39302)
* improve merge performance with implicit subtree mergeinfo (issue #3443)
* fix "libsvn_ra_svn/marshal.c assertion failed (opt || cstr)" (issue #3485)
* make file externals work for binary files (issue #3368)
* perform MIME type matching case-insensitively (issue #3479)
* do not treat non-existent revisions as HEAD in 'svn export' (issue #3400)
* revert r36720's default MIME type change back to "text/plain" (issue #3508)
* improve "tree conflict already exists" error message (r38872)
* fix failure to commit replacement of a directory (issue #3281)
* fix mod_dav_svn parent dir links to preserve peg revisions (issue #3425)
Developer-visible changes:
* fix 2 failing tests in ruby bindings (r38886)
* do not require GNU grep for build (issue #3453)
* use '$SED' instead of 'sed' in build scripts (issue #3458)
* add svn.client.{log5,merge_peg3} to python bindings (r39635, -6, -7)
* include the time of a test run in tests.log (r39887)
Version 1.6.5
(22 Aug 2009, from /branches/1.6.x)
http://svn.apache.org/repos/asf/subversion/tags/1.6.5
User-visible changes:
* fix mod_dav_svn directory view links to preserve peg revisions (r38021)
* do not error on Windows when ALLUSERPROFILE dir nonexistent (r38053, -5, -7)
* properly escape lock comments over ra_neon (r38101, -2)
* allow syncing copies of '/' over ra_neon and ra_serf (issue #3438)
* make 'svnlook diff' show empty added or deleted files (r38458)
* fix building with Apache 2.4 (r36720)
* fix possible data loss on ext4 and GPFS filesystems (issue #3442)
* resolve symlinks when checking for ~/.subversion (r36023)
* don't let svn+ssh SIGKILL ssh processes (issue #2580)
* allow PLAIN and LOGIN mechanisms with SASL in svnserve (r38205)
* fix peg revision parsing in filenames like 'dir/@file.txt' (issue #3416)
* fix detection of Apache <2.0.56 (r38290, -3, -4)
* don't pretend to do tree conflict resolution (r38799, -801, -805)
* fix data corruption when syncing from svnserve to mod_dav_svn (r38686, -7)
* fix GNOME Keyring with '--non-interactive' option (r38222, -3, -61, -410)
* fixed: false "File '...' already exists" error during commit (issue #3119)
Developer-visible changes:
* avoid referencing uninitialized variables (r38388)
* plug a couple of error leaks (r38572)
* improve windows test output (r38616, -7, -9, -49)
Version 1.6.4
(06 Aug 2009, from /branches/1.6.x)
http://svn.apache.org/repos/asf/subversion/tags/1.6.4
User-visible changes:
* fixed: heap overflow vulnerability on server and client
See CVE-2009-2411, and descriptive advisory at
http://subversion.apache.org/security/CVE-2009-2411-advisory.txt
Version 1.6.3
(22 Jun 2009, from /branches/1.6.x)
http://svn.apache.org/repos/asf/subversion/tags/1.6.3
User-visible changes:
* fix segfault in WC->URL copy (r37646, -56)
* let 'svnadmin load' tolerate mergeinfo with "\r\n" (r37768)
* make svnsync normalize svn:* props to LF line endings (issue #3404)
* better integration with external merge tools (r36178)
* return a friendly error message for 'svn diff' (r37735)
* update dsvn.el for 1.6 (r37774)
* don't allow setting of props on out-of-date dirs under neon (r37745)
* improve BASH completion (r36450, -52, -70, -79, -538)
* always show tree conflicts with 'svn st' (issue #3382)
* improve correctness of 'svn mergeinfo' (issue #3126)
* decrease the amount of memory needed for large commits (r37894, -6)
* work around an APR buffer overflow seen by svnsync (r37622)
* ra_svn clients now use TCP keep-alives if available (issue #3347)
* improve 'svn merge' perf by reducing server contact (r37491, -593, -618)
* stop propagating self-referential mergeinfo in reintegrate merges (r37931)
* fix NLS detection where -liconv is required for bindtextdomain() (r37827)
* don't delete unversioned files with 'rm --keep-local' (r38015, -17, -19)
* bump apr and apr-util versions included in deps to latest. (r37941)
* avoid temp file name collisions with ra_serf, ra_neon (r37972)
* fixed: potential segfault with noop file merges (r37779)
* fixed: incorrect output with 'svn blame -g' (r37719, -23, -41)
* fixed: bindings don't load FS libs when module search enabled (issue #3413)
* fixed: DAV RA layers not properly handling update/switch working copy
directory to revision/place in which it doesn't exist (issue #3414)
* fixed: potential abort() in the working copy library (r37857)
* fixed: memory leak in hash reading functions (r37868, -979)
Developer-visible changes:
* improve memory usage in file-to-stringbuf APIs (r37907)
* reduce memory usage for temp string manipulation (r38010)
Version 1.6.2
(11 May 2009, from /branches/1.6.x)
http://svn.apache.org/repos/asf/subversion/tags/1.6.2
User-visible changes:
* vastly improve memory usage with 'svn merge' (issue #3393)
* make default depth for merge 'infinity' (r37156)
* make 'status --quiet' show tree conflicts (issue #3396)
* allow '--set-depth infinity' to expand shallow subtrees (r37169)
* return an error if attempting to reintegrate from/to the repo root (r37385)
* don't store bogus mergeinfo for '--ignore-ancestry', foreign merges (r37333)
* don't allow merge of difference between two repos (r37519)
* avoid potential segfault with subtree mergeinfo (r36613, -15, -31, -41)
* recommend sqlite 3.6.13 (r37245)
* avoid unnecessary server query for implicit mergeinfo (r36509)
* avoid unnecessary server query during reverse merges (r36527)
* set depth=infinity on 'svn add' items with restricted depth (r37607)
* fixed: commit log message template missing paths (issue #3399)
* fixed: segfault on merge with servers < 1.6 (r37363, -67, -68, -79)
* fixed: repeat merge failures with non-inheritable mergeinfo (issue #3392)
* fixed: another memory leak when performing mergeinfo-aware merges (r37398)
* fixed: incorrect mergeinfo on children of shallow merges (issue #3407)
* fixed: pool lifetime issues in the BDB backend (r37137)
Developer-visible changes:
* don't fail if an embedding app has already initialized SQLite (issue #3387)
* resolve naming collisions with static stat() function in svnserve (r37527)
* fix an expectation for a failing dirent windows test (r37121)
Version 1.6.1
(9 Apr 2009, from /branches/1.6.x)
http://svn.apache.org/repos/asf/subversion/tags/1.6.1
User-visible changes:
* recommend Neon 0.28.4. (r36388)
* improve performance of 'svn merge --ignore-ancestry' (r36256)
* improve 'svn merge' performance with subtree mergeinfo (r36444)
* correctly proxy LOCK and UNLOCK requests (r36159)
* prevent a crash when updating old working copies (r36751)
* don't let svnmerge.py delete a nonexistent property (r36086, -767, -769)
* don't fail when upgrading pre-1.2 repositories (r36851, -7)
* allow escaping of separator characters in autoprops (r36763, -84)
* improve tempfile creation robustness on Windows (r36442, -3)
* fix change-svn-wc-format.py for 1.6.x working copies (r36874, -5)
* improve configure's detection of Berkeley DB (r36741, -2)
* don't allow foreign merges to add foreign mergeinfo (issue #3383)
* improve performance of 'svn update' on large files (r36389, et. al.)
* fixed: error leak and potential crash (r36860)
* fixed: parent directory handling on Windows (r36049, -50, -51, -131)
* fixed: uninitialized memory errors (r36252, -3)
* fixed: potential working copy corruption (r36714)
* fixed: working copy upgrade error (r36302)
* fixed: pointer dereference error (r36783)
* fixed: error diff'ing large data with ignored whitespace (r36816)
* fixed: potential hang in ra_serf (r36913)
* fixed: problem with merge and non-inheritable mergeinfo (r36879)
* fixed: repeated merging of conflicted properties fails (issue #3250)
* fixed: excluding an absent directory segfaults (issue #3391)
Developer-visible changes:
* ensure svn_subst_translate_cstring2() properly flushes data (r36747)
* make serf report a base checksum to apply_textdelta (r36890)
* syntax updates for strict C89 compilers (r36799)
* update RPM scripts for RHEL4 (r36834)
* allow tests to be run with Python 2.6.1 on Windows (r36149, -50, -51, -56)
* allow building JavaHL with Visual Studio 2008 (r36954)
* stop setting default translation domain in JavaHL (r36955)
* fixed: warning with Python 2.6 and ctypes bindings (r36559)
* fixed: undefined references to svn_fs_path_change2_create() (r36823)
Version 1.6.0
(20 Mar 2009, from /branches/1.6.x)
http://svn.apache.org/repos/asf/subversion/tags/1.6.0
User-visible changes:
- General:
* Now require Windows 2000 or newer on Windows (r33170)
- Major new features:
* identical files share storage space in repository (issue #2286)
* file-externals support for intra-repository files (issue #937)
* "tree" conflicts now handled more gracefully (issue #2282, #2908)
* repository root relative URL support on most commands (issue #3193)
- Minor new features and improvements:
* pre-lock hook can now specify lock tokens via stdout (r32778)
* svnmucc: support '--with-revprop' (r29492)
* merge: log include-descendants in operational log (r30426, r30428)
* improved operational logging for 'svn switch' (r30517)
* new 'Header' keyword, similar to 'Id' but with full URL (r35386)
* warn/disallow when storing plain-text passwords (r31046)
* support KWallet and GNOME keyring for password storage (r31241, -337)
* client now caches SSL client cert passphrases (issue #2489)
* add '--prefix-file' option to 'svndumpfilter' (issue #2697)
* add '--ignore-externals' option to 'svn cp' (issue #3365)
* add '--with-no-revprops' to 'svn log' (issue #3286)
* new 'svnadmin pack' command to compress FSFS filesystems
* new SVNAllowBulkUpdates mod_dav_svn directive (issue #3121)
* new public mod_dav_svn URI syntax: path?[p=PEG][&r=REV] (r34076)
* new 'svnsync info' command to show synchronization information (r35053)
* conflict resolver supports display-conflict, mine-conflict and theirs-conflict
- Client-side bugfixes:
* faulty reflexive merges (issue #2897)
* buffer overflow on a 0 byte string buffer (r35968, -74)
* conflict resolver needed more useful 'diff' option (issue #3048)
* disable username assumption (issue #2324)
* more accurate usage message for 'svn log' (r30449)
* do not repeat merge if target has explicit mergeinfo (issue #2821)
* corruption when filtering self-referential mergeinfo (r30467)
* filter empty mergeinfo with self-referential mergeinfo (r30510)
* pay attention to partial replay from the server in svnsync (r30440)
* improved property name handling in svnsync (r30480)
* properly recognize the file:/// in repository with svnsync (r30482)
* svn+ssh SIGKILLs ssh processes (issue #2580)
* 'svn up'/'svn co' early abort with svn:externals (issue #3148)
* improve tempfile names for conflict resolver (issue #3166)
* ra_serf: 'svn merge' aborts (issue #3212)
* 'svn cleanup' failed on non-ASCII characters (issue #3313)
* 'svn update' fails on moved, modified file with local mods (issue #3354)
* easier use of NTLM for proxy with ra_neon (r29874)
* 2-url merge from DAV-accessed foreign repo makes bad wcprops (issue #3118)
* can't add .svn (and children) to your wc via '--parents' (r35819)
* improved performance removing unversioned directories (r36111)
* 'svn cp --parents' had path URL encoding issues (issue #3374)
* support shell quoting rules in externals definitions (issue #2461)
* new SVN_LOCALE_DIR environment variable for localization (issue #2879)
* scheme and domain name in urls handled case insensitive (issue #2475)
* merge: pick default revisions with peg revision in single url (r30455)
* many other minor bugfixes, optimizations, plugs of memory leaks, etc
- Server-side bugfixes:
* mod_dav_svn runs pre-revprop-change twice (issue #3085)
* mod_dav_svn ignores pre-revprop-change failure on delete (issue #3086)
* mod_dav_svn prevented lock breaks from being propagated to client (r29914)
* non-UTF8 filenames could enter repository (issue #2748)
* 'svnlook proplist' xml output (issue #2809)
* don't let mod_dav_svn hide errors from client (issue #3102)
* ra_serf failure during update (issue #3113)
* ra_serf comply with RFC 2617 in handling authentication headers (r35981)
* use both SHA1 and MD5 in the FS backends (r34388)
* many other minor bugfixes too numerous to list here
- Contributed tools improvements and bugfixes:
* commit-email.pl: Deprecated; use mailer.py instead (r31755, -67)
* svnmerge.py migration tool munged svn:mergeinfo ordering (issue #3302)
* And other random sundry stuff
Developer-visible changes:
- General:
* serf 0.3.0 required, when building with serf (r35586)
* require SQLite 3.4.0 or newer (r33520)
* allow the use of an in-tree SQLite amalgamation (r35263)
* svn_log_changed_path_t now includes a 'kind' field (issue #1967)
* BDB `changes' table inconsistency when APIs are misused (issue #3349)
* configure should prefer apr-1 over apr-0 if both are present (issue #2671)
* make 'Not Found' errors consistent between RA layers (issue #3137)
* fix a potential buffer overrun (r34374)
* many bug fixes and improvements to the test suite
- API changes:
* notification system for properties and revision properties (issue #783)
* make ra_svn's merge commit-revprops public (r30462, r30453)
* mod_dav_svn operational logging compatible with svnserve logging (r30518)
* improve speed of svn_client__get_copy_source() (issue #3356)
* if fsfs commit fails return SVN_INVALID_REVNUM (r35950)
- Bindings:
* new: ctypes python bindings
* many improvements to all bindings (Java, Perl, Python, and Ruby)
* respect CFLAGS in SWIG bindings (r35879)
* fix building Ruby bindings with Ruby 1.9 (r35852, r35883)
Version 1.5.9
(06 Dec 2010, from /branches/1.5.x)
http://svn.apache.org/repos/asf/subversion/tags/1.5.9
User-visible changes:
* fix proxying of LOCK and UNLOCK requests with WebDAV proxies (r36159)
* improve performance of --ignore-ancestry merges (r36256)
* avoid crash with when using subtree mergeinfo (r36613, -13, -31, -41)
* improve merge correctness with non-inheritable mergeinfo (r36789)
* fixed: repeated mergeinfo of conflicting properties fails (issue #3250)
* fix segfault in wc->URL copy (r37646, -56)
* make 'svn up --set-depth infinity' expand shallow subtrees (r37169)
* resolve symlinks when checking for ~/.subversion (r36023)
* make default depth of 'svn merge' infinity (r37156)
* don't allow foreign merges to add foreign mergeinfo (issue #3383)
* error if attempting to reintegrate to/from the repo root (r37385)
* let 'svnadmin load' tolerate mergeinfo with "\r\n" (r37768)
* improve memory performance in 'svn merge' (issue #3393)
* fixed: 'SVNPathAuthz short_circuit' unsolicited read access (issue #3695)
See CVE-2010-3315, and descriptive advisory at
http://subversion.apache.org/security/CVE-2010-3315-advisory.txt
* prevent crash in mod_dav_svn when using SVNParentPath (r1033166)
* limit memory fragmentation in svnserve (r1022675)
* fix server-side memory leaks triggered by 'blame -g' (r1032808)
* perform MIME type matching case-insensitively (issue #3479)
* respect Apache's ServerSignature directive (r880082)
* error early if attempting to use Serf >= 0.4.0 (r1041545)
Developer-visible changes:
* fix pointer dereference (r36783)
* fix error leak (r36860)
* make basic_tests 12 compatible with Windows and Python 2.5+ (r35930)
Version 1.5.8
(Not released, see changes for 1.5.9.)
Version 1.5.7
(06 Aug 2009, from /branches/1.5.x)
http://svn.apache.org/repos/asf/subversion/tags/1.5.7
User-visible changes:
* fixed: heap overflow vulnerability on server and client
See CVE-2009-2411, and descriptive advisory at
http://subversion.apache.org/security/CVE-2009-2411-advisory.txt
Version 1.5.6
(26 Feb 2009, from /branches/1.5.x)
http://svn.apache.org/repos/asf/subversion/tags/1.5.6
User-visible changes:
* allow colons within mergeinfo path names (r35040)
* make it impossible to add .svn to wc via 'svn add --parents' (r35143, -5)
* copy properties of added but uncommitted files (r32448)
* speedup JavaHL bindings on Windows (r35733)
* improve performance of log operation on < 1.5 servers (r35566)
* allow commits over Neon of files >2GB (POSIX only) (r34919, -24)
* allow serf from behind MS ISA proxy servers (r35981)
* prevent svnmerge-migrate-history.py from committing bogus mergeinfo (r35516)
Developer-visible changes:
* fix error handling in mod_dav_svn (r35250, -86)
* support --server-minor-version in windows testsuite (r31393)
* fix depth_tests.py 23 on Windows with a BDB repo (r34875)
* allow svn_mergeinfo_parse() to tolerate unordered mergeinfo (r35297, -367)
* allow overlapping rangelists into svn_mergeinfo_parse() (r35466, -712, -713)
Version 1.5.5
(22 Dec 2008, from /branches/1.5.x)
http://svn.apache.org/repos/asf/subversion/tags/1.5.5
User-visible changes:
* allow prop commits on dirs with modified children (r34487, -92, -94)
* make Cyrus auth implementation always prefer EXTERNAL to ANONYMOUS (r33866)
* do not create mergeinfo for wc-wc moves or copies (r34184, -585)
* do not autoupgrade old BDB filesystems to 1.5 or 1.4 format (r34653, -6)
* return mergeinfo to prior state during reverse merges (r30257, r33024, -6)
* remove mergeinfo deleted by merge (issue #3323)
* make proxy slaves pass through txn GET and PROPFIND requests (issue #3275)
* merge can now use targets with inconsistent newlines (issue #3262)
* don't allow empty-string changelists (issue #3344)
* remove false positive ra_neon mergeinfo errors (r34822)
* improve performance of 'svn merge --reintegrate' (r34091, -4, and others)
* fixed: foreign merges keep UUID of foreign repository (r34050, -1, -3)
* fixed: properly encode diff headers used in conflict resolution (r34171)
* fixed: segfault in 'svn cp --parents' (r31311, -4)
* fixed: mergeinfo for '...' maps to empty revision range (issue #3312)
* fixed: segfault in BDB backend node-origins cache (r34506)
* fixed: broken merge if target's history includes resurrections (r34385, -93)
* fixed: invalid mergeinfo created on a subtree during merge (r34560, -2)
Developer-visible changes:
* fixed: svn_repos_get_logs() chokes on some revision arguments (r33873, -4)
Version 1.5.4
(24 Oct 2008, from /branches/1.5.x)
http://svn.apache.org/repos/asf/subversion/tags/1.5.4
User-visible changes:
* Properly handle explicit mergeinfo added in merge source (r32968, -75)
* fixed: merging of paths containing spaces (r33641, -44)
* fixed: regression in mergeinfo-aware merges against 1.5.3 (r33693, -704)
Version 1.5.3
(10 Oct 2008, from /branches/1.5.x)
http://svn.apache.org/repos/asf/subversion/tags/1.5.3
User-visible changes:
* Allow switch to continue after deleting locally modified dirs (issue #2505)
* Update bash_completion to be compatible with 1.5 (r32900, -11, -12)
* Improve 'svn merge' execution time by 30% on Windows (r33447)
* Reuse network sessions during 'svn merge', improving performance (r33476)
* Improve temp file creation time on Windows (r33464)
* Greatly improve merge performance (r29969, r32463, r33013, -016, -022, -112)
* Improve file IO performance on Windows (r33178, -85)
* fixed: merging files with spaces in name (r33109, -121, -369)
* fixed: incorrect relative externals expansion (r33109, -121, -369)
* fixed: 'svn mv' hangs and consumes infinite memory (r33201, -12)
* fixed: correctness regression in 'svn log -g' (issue #3285)
* fixed: current early bailout of 'svn log -g' (r32977)
Developer-visible changes:
* Allow the tests to run as non-administrator on Windows Vista (r31203)
* Allow out-of-tree build of bindings on BSD (r32409)
* Translate messages in svn_fs_util.h (r32771)
* fixed: bindings test for Perl 5.10 (r31546)
* fixed: building bindings and C API tests with VS2008 (r32012)
* fixed: svn_ra_replay API over ra_serf (r33173)
Version 1.5.2
(30 Aug 2008, from /branches/1.5.x)
http://svn.apache.org/repos/asf/subversion/tags/1.5.2
User-visible changes:
* Set correct permissions on created fsfs shards (r32355, -7)
* Pass client capabilities to start-commit hook (issue #3255)
* Disallow creating nested repositories (issue #3269)
* Support Neon 0.28.3
* Properly canonicalize URIs with an empty hostname (issue #2116)
* Improved merge performance for superfluous ranges (r32643)
* Better error message for 'Malformed URL for repository' (r31867, r32365)
* Improved svn:externals parsing (r32672, -673, -674, -739)
* fixed: improper ordering in 'svnlook diff' output (r32019)
* fixed: mod_dav_svn memory leak with 'SVNPathAuthz short_circuit' (r32360)
* fixed: duplicate svn:externals targets fail on co/up (issue #3246)
* fixed: 'svn merge --depth' inconsistencies (issue #2825)
* fixed: ra_serf test failures (1.5.x-ra_serf-backports branch)
* fixed: memory leak and crashes in FS (r32545, -58, -82)
* fixed: core dump with relative externals (issue #3237)
* fixed: 'svn copy' working copy corruption (r32467, -70)
* fixed: perl bindings errors in non-English locale (issue #3258)
* fixed: 'svn merge' incorrectly reverses previous merges (r32494, -522, -523)
* fixed: 'svn merge' errors with subtree mergeinfo (issue #3067)
Developer-visible changes:
* make libsvn_ra_neon initialization thread-safe (r32497, r32510)
* respect LDFLAGS in SWIG bindings (r32416, r32421, r32442)
* fixed: test failures in non-English locales (r32491)
Version 1.5.1
(26 Jul 2008, from /branches/1.5.x)
http://svn.apache.org/repos/asf/subversion/tags/1.5.1
User-visible changes:
* mergeinfo on switched subtrees should elide in repos (issue #3188)
* Add support for --config-dir to svnmerge.py (r31727)
* improve performance of bdb post-commit deltification (r31820, -59)
* return faster when there is nothing to be merged (r30748)
* don't commit an add of a missing item (issue #3198)
* don't create unneeded self-referential mergeinfo (issue #3157)
* support 'http-library' (if --enable-runtime-module-search) (r31425, -722)
* support Berkeley DB 4.7 (r32017, -29)
* fixed: make serf usable with root-level authz (r31464)
* fixed: 'svndumpfilter' partial-path matching bug (r31833)
* fixed: crash on invalid dates in 'log' and 'blame' (issue #2721)
* fixed: 'svn status --xml' outputting invalid XML (issue #2887)
* fixed: 'svn merge' prints incorrect range (r30746, -47)
* fixed: using neon/serf, can not replace branch (issue #2939)
* fixed: 'file not found' error when merging to a broken symlink (r31159, -79)
* fixed: using serf, crash or endless loop fetching authn data (r31619)
* fixed: ArrayIndexOutOfBoundsException in JavaHL bindings (r31719, -806)
* fixed: authn password lookup used wrong username (issue #2242)
* fixed: unbounded memory usage in wc-to-wc copy and move (r31868)
* fixed: subtree merges broken for non-intersecting ranges (issue #3199)
* fixed: invalid XML from 'svn log --xml' against pre-1.2 servers (r31875)
* fixed: 'svnlook diff' ignores --diff-copy-from for properties (issue #3248)
* fixed: 'svnlook diff' doesn't report that binary files differ (issue #3249)
* fixed: bogus results from commits to subtrees added by merge (issue #3240)
* fixed: non-existent subtree in destination breaks the merge (issue #3067)
* fixed: serf merge bug too complex to describe here (r32056)
* fixed: 'svn log -g' correctness and speed (issue #3220, issue #3235)
* fixed: merge chokes on renamed subtrees (issue #3174)
Developer-visible changes:
* export svn_path_is_url() to the bindings (r31603)
* don't clobber LDFLAGS in configure when given '--with-zlib' (r31825)
* make libsvn_ra depend on libsvn_delta unconditionally (r31852)
* correctly set the peg revision for copy in JavaHL (r31994)
* 'svn mergeinfo' handles wc paths (r31023, -873, -874, -929, -930, -038)
* fixed: crash when when svn_ra_open3() is passed a bogus URL (r31223)
* fixed: JavaHL compilation on Windows (r31737)
* fixed: crash in calling apr_pstrcat (affects TortoiseSVN) (r32080)
Version 1.5.0
(19 Jun 2008, from /branches/1.5.x)
http://svn.apache.org/repos/asf/subversion/tags/1.5.0
User-visible changes:
- Major new features:
* Merge Tracking [foundational] (issue #820)
* Sparse checkouts (see new '--depth' option) (issue #695)
* Interactive conflict resolution (r25670 et al)
* svn:externals handles relative URLs (issue #1336) and peg URLs
* Changelist support
* WebDAV transparent write-through proxy
* Better support for large FSFS deployments (via sharding & partitioning)
* Cyrus SASL support for ra_svn and svnserve (issue #1144)
- Minor new features and improvements:
* 'svn resolve' (with '--accept' option) replaces "resolved" (issue #2784)
* 'svn move file1 file2 ... dir' now moves the files into dir (issue #747)
* 'svn mkdir' and 'svn copy' now take '--parents' option (issue #1776)
* 'svn delete' now takes '--keep-local' to not remove working copy files
* 'svn copy', 'move' now support peg revisions (issue #2546; also r26484)
* 'svn copy A B ; svn move B C' now the same as 'svn copy A C' (issue #756)
* 'svn copy -rBASE' now works in a working copy (issue #1643)
* 'svn import' now takes '--force' (issue #2806)
* 'svn status -u' now shows of locally deleted directories (issue #2420)
* 'svn switch' now takes '--force' (issue #2392)
* 'svn switch' now takes '--ignore-externals' option (issue #2189)
* 'svn switch' now supports peg revisions (issue #2545)
* 'svn checkout' now takes '--force' option (issue #1328)
* 'svn proplist' and 'svn propget' now support peg revisions (issue #3070)
* 'svn propget' now takes '--xml' option (issue #2696)
* 'svn propedit' now support URLs (issue #2238, but see issue #2923)
* 'svn proplist --quiet' no longer prints extra info (issue #1547)
* 'svn diff --summarize' now takes '--xml' option (issue #2967)
* 'svn diff -x' now takes '-p' extension option (issue #2995)
* 'svn log' now takes '-c' option (r27933)
* 'svn log' now takes '-l' as short form of '--limit' (r25829)
* 'svn log --xml' now takes '--with-revprop' option (issue #2850)
* 'svn diff'/'svnlook diff' now show property actions better (issue #3019)
* 'svn merge' now has informative messages on reverse merges (issue #2848)
* 'svn merge FILE' now honors '--ignore-ancestry' (issue #2853, r25891)
* 'svn merge' handles multiple notifications for single items (issue #2828)
* 'svn merge' handles skipped path better (issue #2829)
* 'svn merge' handles merges from foreign repositories more completely
* 'update', 'checkout', 'switch' now handle obstructions gracefully (r22257)
* 'svn update' now takes '--force' (issue #2392)
* 'svn update' now sometimes copies or moves local files, for efficiency
* 'svnadmin lslocks' now accepts path within repository (issue #2965)
* 'svnadmin recover' now supports FSFS repositories (issue #2992)
* 'svnadmin verify' now has '-q' and '-r' options (r22103)
* 'svnadmin setrevprop' command added (r21736)
* 'svnadmin setuuid' command added (r28511)
* 'svnsync sync' now shows commit progress like 'svn commit'
* 'svnsync' now takes '-q, --quiet' option (r26465)
* 'svnsync' now supports separate authn for source/target (issue #2717)
* 'svnsync copy-revprops' now supports revision ranges (r23498)
* 'svnsync copy-revprops' now supports "HEAD" revision alias (r23500)
* 'svnmucc' is new name for contrib tool formerly called 'mucc'
* 'svnmucc' now has propset and propdel subcommands (issue #2758)
* 'svnmucc' now has more authentication options
* 'svnmucc' now now takes '--non-interactive' option (r25977)
* 'svnmucc' now takes a global base revision, for extra safety (r23764)
* 'svnlook' now takes '--extensions' option (issue #2912)
* 'svnlook' now takes '-N' option (issue #2663)
* 'svnlook history' now takes '-l' / '--limit' option (r25843)
* 'svnserve' now takes '--config-file' option (r24119)
* 'mod_dav_svn' now uses Apache default mime-type for files (issue #2304)
* new '--with-revprop' option on all commands that commit (issue #1976)
* now accept "peg dates" (URL@{DATE}), behaving like peg revs (issue #2602)
* easier to try out experimental ra_serf http:// access module
* select ra_neon vs ra_serf on a site-by-site basis in config (r25535)
* client-side post-commit processing now more efficient (issue #2607)
* windows binaries now use a custom crash handler (issue #1628)
* add vim swap file patterns to default global-ignores (r24348)
* add "*.pyc" and "*.pyo" patterns to default global-ignores (issue #2415)
* add unix/libtool library patterns to default global-ignores (issue #2415)
* naming scheme for conflict files is now configurable (issue #2474)
* removed svn-ref.tex as it's extremely out of date (issue #2762)
* improved cancellation response in many situations
* support Neon up to 0.28
* character set conversion now uses native API on Windows (r25650)
* HTTP authn protocol now configurable (for Neon 0.26 and higher) (r21531)
* http:// (over Neon) supports HTTP redirection / relocation (issue #660)
* support PKCS#11-provided (smartcard) SSL client certs with Neon (r29421)
* authz now supports aliases (r21982)
* authz token rules for authenticated-only, anonymous, and inverse (r23750)
* mailer.py now supports properties in commit messages (r21684)
* ra_serf now supports NTLM/SSPI authentication (issue #2900)
* warn if try to turn off boolean property via propset/propedit (r25486)
* display repository basename in XML and HTML index views (r25837, r25838)
* config 'http-auth-type' can be overridden to force BASIC auth (r23900)
* translation updates for all languages, as usual
* Revamp mod_dav_svn logging; see tools/server-side/svn_dav_log_parse.py
* misleading configure arg --enable-dso now --enable-runtime-module-search
- Client-side bugfixes:
* 'svn revert' of missing scheduled addition broke wc (issue #2425)
* 'svn export' should export svn:externals from local copies (issue #2429)
* 'svn status -uN' should show status of files (issue #2468)
* 'svn update' overwrote if local timestamp unchanged (issue #2746)
* 'svn update -N' errored when receiving a deletion (issue #3039)
* 'svn merge' would delete locally modified props (issue #2857)
* 'svn log --xml' could output invalid XML (issue #2866)
* 'svn copy' on URL with spaces made wrong WC file name (issue #2955)
* 'svn diff' was failing w/ large diffs on Windows (issue #1789)
* 'svn delete' no longer deletes locally-modified files (issue #1808)
* 'svn move' moved files to wrong directory on Windows (issue #1869)
* 'svn revert' mistakenly used leftover .svn-revert files (issue #2927)
* 'svn diff' output now shows relative paths (issue #2723)
* 'svn diff' wasn't ignoring all EOLs (issue #2920)
* 'svn cleanup' no longer fails on a missing .svn/tmp dir (r23370)
* infinite loop in UTF conversion in non-C locale (issue #2577)
* interrupting "svn status" could make svn crash (issue #2623)
* commit-email.pl date header output now RFC2822-compliant (issue #2633)
* authz write access to folder wasn't permitting locking (issue #2700)
* stop complaining just because $HOME is unreadable (issue #2363)
* do not display unescaped characters in error message (issue #2471)
* propchange received on subdir merge causes conflict (issue #2969)
* revert replaced-with-history files should restore checksum (issue #2928)
* catch improper arguments to diff (issue #2996)
* handle URLs like http://hostname (i.e. no path part) (issue #1851)
* config autoprops honored regardless of case of entry (issue #2036)
* "Cannot replace a directory from within" error now rarer (issue #2047)
* handle _svn/.svn as part of a path (issue #3026)
* make permissions changes on symlinks a no-op (issue #2581)
* error usefully if asked to update a URL (r22296)
* fixed infinite loop on Windows if fail to find repository root (r22483)
* 'svn info $REPO_ROOT' now supports pre-1.2 svn:// servers (r26264)
* be more resilient in the face of faulty .svn/entries files (r26482)
* 'svn diff -x --ignore-eol-style' failed to ignore all EOLs (r27094)
* rare property dataloss bug now fixed (issue #2986, see also r29538)
* fixed faulty status reporting for some missing directories (issue #2804)
* 'svn diff --summarize' showed wrong output paths (issue #2765)
* propset and move interaction could cause property weirdness (r25833)
* 'svn propget <propname> .@HEAD' now works (issue #3012)
* 'svnsync' had bug with replaced+modified rev over serf (issue #2904)
* 'svnsync --config-dir' sometimes ignored, thus tunnel agent bug (r27056)
* update/merge safely receives file on top of schedule-add file (r23506)
* http:// (over Neon) reports progress while disk-spooling delta (r26271)
* print "Out of memory" before dying from memory shortage (issue #2167)
* warn when used on old checkout without a repository root entry (r25168)
* merge to missing file target wrongly appeared to succeed (issue #2782)
* 'svn merge URL PATH -cX' could cause property corruption (issue #2781)
* URL parsing now consistently checks for error earlier (issue #2207)
* security hole: files could be created above cwd (r26047, CVE-2007-3846)
* local property mods to replaced-with-history file could be lost (r26364)
* revert of replaced-with-history path left copyfrom info (r23452)
* character encoding translation could hang (r23492)
* un-substituting keywords was buggy ($Id$ vs. $Id:$) (issue #2640)
* ra_neon and ra_serf lost pre-revprop-change hook output (issue #443)
* merge of non-empty subdir could be committed incorrectly (issue #1962)
* many other minor bugfixes, optimizations, plugs of memory leaks, etc
- Server-side bugfixes:
* segfault in svnserve and svnversion commands fixed (issue #2757)
* segfault when stopping httpd (if BDB repository) fixed (issue #2732)
* 'svnadmin dump' had a path ordering bug (issue #2641)
* better FSFS support for NFS v3 and lower (r24470)
* better FSFS support for some buggy NFS clients (r29448)
* authentication and authz bugs w.r.t. anonymous access (issue #2712)
* inconclusive authz result should deny, not allow (r23815)
* better reporting of problems parsing authz files (r22329)
* set svn:date revprop even if dumpstream does not (issue #2729)
* http:// commit can now create empty files properly (r25471, r25474)
* squelch not-a-directory errors in both FS backends (issue #2549)
* segfault on update-report response without base revision (issue #3023)
* 'svnserve --root PATH' checks that PATH exists (r22580, r22701)
* 'svnlook propget -t TXN_NAME' reports errors better (r22772)
* make location of mod_dav_svn activity database configurable (r24873)
* select only paths that are proper children of requested path (r25231)
* http:// commit error could leave empty transactions behind (r23594)
* 'svn switch --relocate' now works against unreadable repos root (r23848)
* many other minor bugfixes too numerous to list here
- Contributed tools improvements and bugfixes:
* svn_load_dirs.pl:
- Support global-ignores list (issue #2470)
- Allow "@" in filenames (r22203, Debian bug 359145)
- Add -no_auto_exe option (r26399)
* svnmerge.py:
- fixed: Always get end_rev from source instead of target (issue #2863)
- fixed: 'init' now chooses a better default revision range (issue #2810)
- fixed: Consider revs changing blocking status as reflected (issue #2814)
- Performance inmprovement (issue #2812)
- initialized revisions can be excluded (issue #2851)
* new 'svn-populate-node-origins-index' tool (issue #3024)
* new 'svn-merge-vendor.py' to assist in merging vendor branches (r23030)
* 'svn2rss.py' is now called 'svn2feed.py'
* svn2cl: New release 0.9 (r24498)
* commit-email.pl: various improvements (r22971, r22589)
* commit-email.rb: various improvements
* psvn.el: too many improvements and new features to list them all here
* dsvn.el: improve XEmacs compatibility (r24337)
* svn-tweak-author.py: make NEWAUTHOR argument optional (r24387)
* And more stuff that we just didn't have time to list. Enjoy.
Developer-visible changes:
* General:
- libsvn_ra_neon is new name for libsvn_ra_dav (to accommodate ra_serf)
- many abort() calls removed, replaced with error returns
- client and server now do capabilities exchange (r29358 et al)
- gen_win.py: auto-detect the path to the JDK on Windows (r24333)
* API changes:
- many, many new APIs and types as part of the new features in 1.5.0
- APIs to allow retrieving multiple revprops in one fetch (issue #2850)
- basic progress reporting for ra_svn (issue #901)
- new APIs for creating and using iterators (r26533)
- svn_fs_node_origin_rev finds line of history origin (issue #3017, #3024)
- svn_revnum_parse for parsing revision numbers (r26195)
- svn_path_is_canonical for validating paths (r26481)
- new API svn_fs_txn_root_base_revision() (r22610)
- pass individual arguments rather than config objects (r25182, r25190)
- clients can now extend HTTP User-Agent header (r28613)
- SVN_ERR_RA_DAV_PATH_NOT_FOUND is deprecated and no longer raised
* Bindings:
- Many improvements to all bindings (Java, Perl, Python, and Ruby)
Version 1.4.6
(21 Dec 2007, from /branches/1.4.x)
http://svn.apache.org/repos/asf/subversion/tags/1.4.6
User-visible changes:
- Client:
* fixed: unbounded memory use in "svn cat" over ra_svn (r26964, -8)
* fixed: 'svn diff --summarize file' displays erroneous output (issue #2765)
* fixed: 'svn status' wrong on previously-reverted deleted dir (issue #2804)
* fixed: 'svn up' can delete unversioned symlinks (issue #1808)
* fixed: use correct properties for locally replaced files (issue #2743)
* fixed: 'svn info -R $REPO_ROOT' w/ pre-1.2 svnserve broken (r26264)
* fixed: svnsync ignores '--config-dir' (r27056)
* datestamps can be localized (r26156)
* fixed: text base not updated when merging a replaced file (issue #2698)
* fixed: inverted 'switch --relocate' error message (r22355)
* fixed: sporadically failing file and directory removal on Windows (r25520)
* fixed: property file handling for schedule-delete files (r25833)
* fixed: allow invalid svn:eol-style values (r28331)
* fixed: 'svnadmin rmlocks' should error when no path provided (r28431)
* support neon 0.26.4 (r26077)
- Server:
* fixed: authz granted if calculation inconclusive (r23815)
* fixed: svndumpfilter crashes on Windows (r23494)
* fixed: wrong pointer type used for memset (r27263)
* fixed: invalid FSFS directory cache can corrupt repository (r27256)
* fixed: dir props on FSFS filesystem root never conflict (issue #2608)
- Client and Server:
* fixed: "No newline at end of file" message translated (issue #2906)
* use compressed delta encoding for 'svn blame' in svnserve (r26115)
* translation updates for Simplified Chinese
Developer-visible changes:
* svnserveautocheck.sh script is executable (r23942)
* add RHEL5 RPM (r25593)
* test suite passes with trunk servers (forwards-compatibility) (r25607)
* javahl bindings:
- improve error reporting from native code (r25208)
Version 1.4.5
(27 Aug 2007, from /branches/1.4.5)
http://svn.apache.org/repos/asf/subversion/tags/1.4.5
User-visible changes:
* fixed: file placement vulnerability (Win32 clients only)
See CVE-2007-3846, and descriptive advisory at
http://subversion.apache.org/security/CVE-2007-3846-advisory.txt
Version 1.4.4
(30 May 2007, from /branches/1.4.x)
http://svn.apache.org/repos/asf/subversion/tags/1.4.4
User-visible changes:
- Client:
* fixed: 'svn up' of replaced file without history fails (issue #2618)
* fixed: 'svn export' succeeds on non-existent URL (r23191, -3, -5, -200)
* fixed: 'svn diff' fails writing large hunks to Win console (issue #1789)
* fixed: 'svn merge' shows 'G' notifications for unchanged files (r24483)
* fixed: svnsync cannot sync unreadable modified dir copies (issue #2705)
* fixed: ra_dav litters empty transactions if initial setup fails (r23594)
* fixed: inconsistent expansion of revision number keywords (issue #1743)
- Server:
* fixed: rare dirprop dataloss leading to BDB repo corruption (issue #2751)
* fixed: race condition when changing FSFS revprops (r23439, r23440)
* fixed: 'svnadmin load' invents svn:date if none exists (issue #2729)
* fixed: svnserve can't commit locked file if root unwritable (issue #2700)
* fixed: 'svnadmin dump' output invalid for non-ASCII paths (issue #2641)
* fixed: security flaw in 'svn prop*' commands [CVE-2007-2448]
(r25095, -099, -104, -105, -10)
- Client and Server:
* fixed: hang during character translation (r23491, r23492)
* translation updates for Simplified Chinese, Japanese, and Norwegian
Developer-visible changes:
* new "make svnserveautocheck" testing target (r23558)
* fixed: ra_serf fails checkout if access to repos root is forbidden (r23846)
* fixed: svn_client_cat2() doesn't accept WORKING as a revision (r23556)
* javahl bindings:
- fixed: potential segfault in initialisation (r23383)
- fixed: SVNClientSynchronized.logMessages() isn't synchronised (r23978)
- fixed: SVNClient.info2() misreports itself as unlock in errors (r24219)
* SWIG/perl bindings:
- fixed: ra_do_{update,switch,status} don't work with Perl delta editors
(r20667, r22311)
* SWIG/python bindings:
- fixed: memory leak whenever C APIs returned errors (r23521)
* SWIG/ruby bindings:
- fixed: typos in method Svn::Wc#merge_prop_diffs and docs (r23405, -6)
Version 1.4.3
(18 January 2007, from /branches/1.4.x)
http://svn.apache.org/repos/asf/subversion/tags/1.4.3
User-visible changes:
- Client:
* fixed: crash using automatic auth protocols with Neon 0.26 (r22440, -61)
* fixed: svn_load_dirs.pl cannot import file names containing '@' (r22203)
* fixed: error when committing replaced directories (r22991, -8)
* fixed: inability to change file perms due to existing file perms (r23018)
* include newest version of svn-graph.pl (r22969)
- Server:
* fixed: incorrectly reporting authz circular dependencies (issue #2684)
* fixed: potential filesystem memory leak in commit finalisation (r22729)
- Client and Server:
* fixed: crash in character translation, particularly on Windows (r22417)
* fixed: potential string corruption when resizing string buffers (r22689)
* translation updates for Korean, Spanish, Italian, Simplified Chinese,
and Japanese (fixing issues #2649 and #2681)
Developer-visible changes:
* support Neon 0.26.2 (issue #2666)
* update (experimental) ra_serf repository access module for DAV (r22872)
* Windows installer improvements (r21516, r22155, r22224)
* fixed: svn_{ra,repos}_replay() doesn't send checksums (r22346, -51, -52)
* fixed: error when calling svn_repos_replay2() with a txn root (r22609)
* fixed: Solaris packaging script broken (issue #2669)
* javahl bindings:
- fixed: auth cache is created in the current directory (r22780)
- fixed: SVNAdmin's setLog() method always fails (r22387)
- fixed: target dependency order in generated build scripts (r22209)
* SWIG/perl bindings:
- fixed: memory leak when calling methods on a Perl commit editor (r22332)
Version 1.4.2
(2 November 2006, from /branches/1.4.x)
http://svn.apache.org/repos/asf/subversion/tags/1.4.2
User-visible changes:
- Client:
* new "notes/svnsync.txt" file explains common svnsync usage
* install a manpage for svnsync (r21403)
* install/package svnsync on Windows (r21387, r21424)
* translation updates for all languages
* dramatically speed up commit of wc-to-wc copy (r21471)
* fixed: support 'svn co URL@{DATE}' (issue #2602)
* fixed: cannot access repositories with spaces via svn:// (issue #2612)
* fixed: passing full URL in some DAV requests, breaking proxies (r21526)
* fixed: history-tracing can fail for renamed directories (issue #2600)
* fixed: crash if interrupted while opening a working copy (r21792)
* fixed: 'svn merge' should notify about conflicted files (issue #2584)
* fixed: 'svn revert' should notify about prop-only reverts (issue #2517)
* fixed: 'svn status -u' not showing props changed on wc root (issue #2533)
* fixed: 'svn status -u' fails in a read-only working copy (r21904, -19)
* fixed: 'svn up' failing with checksum mismatch error (issue #2618)
* fixed: 'svnsync sync' copying missing implicit revprops (issue #2613)
* fixed: svnsync unable to synchronise copies of URL-unsafe paths (r22092)
* svnshell tool: support "setrev head" (r20992)
* include newest version of svnmerge.py
- Server:
* FSFS: improve detection of disk write errors (r21346)
* FSFS: prevent API violation from corrupting repository (issue #2467)
* improved error checking when running hook scripts, etc (r21483)
* mailer.py: new commit_url option links to web page for a commit (r21333)
Developer-visible changes:
* support Neon 0.26.0 and 0.26.1 (r21289, r21293, r21956)
* support current CVS versions of libtool (post-1.5.22) (r22120)
* now compiles on architectures without APR_HAS_DSO (e.g. RISC OS) (r21473)
* fixed: build error on FreeBSD due to missing svnsync manpage (r21403)
* RHEL3 RPM package requires correct version of Apache httpd (r21974)
* numerous improvements to coverage of the test suite
* javahl bindings:
- compile Java bytecode for Java 1.2 VM (r21765, -7, r21814)
- fixed: crash if using 1.4.x bindings with older libraries (r21316, -429)
- fixed: crash when empty destination path passed to checkout (r21770)
* SWIG/ruby bindings:
- fixed: accept nil for Svn::Repos#load_fs's parent_dir argument (r21793)
* SWIG/python bindings:
- fixed: crash when using an apr_hash_t typemap (issue #2606)
- fixed: in tests, use URLs that work on Windows (r21392)
* SWIG/perl bindings:
- fixed: ra_replay works with Perl delta editors (r20666)
Version 1.4.1
(Not released, see changes for 1.4.2.)
Version 1.4.0
(10 September 2006, from /branches/1.4.x)
http://svn.apache.org/repos/asf/subversion/tags/1.4.0
User-visible changes:
- Client:
* new 'svnsync' commandline tool for repository replication
* numerous working copy improvements (WARNING! upgrades to new format!):
- improved performance when detecting modified files (r18628 -56)
- new property storage is faster and uses less disk space (r17583)
- internal wcprops take up less space (r19433 -37)
- large file commit speedups (r17861 -73 18867 -918 -29 -44 -45 -48 -49)
- reduce memory usage for large working copies (r19183 -538)
- increased working copy stability with merge, copy and move:
(fixes issues #845, #1516, #1553, #2135, #2144, #2148)
* new switches added:
- 'svn blame --force' (issue #2509)
- 'svn diff/merge -c/--change' (r17054 -6 -68 18568 -741)
- 'svn diff --summarize' (issue #2015)
- 'svn merge/blame -x' (r18716 -20) (r18602 -857)
* 'svn log' now supports peg revisions (issue #2287)
* 'svn export' now creates intermediate directories if needed (r20030)
* use switch/relocate when svn:externals updated (issue #2209)
* internal diff can ignore whitespace and eol style changes (issue #2121)
* conflict markers now match the file's eol style (issue #1325)
* new svn2cl, svn-viewdiff and svn-resolve contrib scripts
* numerous improvements to svnmerge.py, vc-svn and psvn
* translation updates for all languages
* Mac OS X: store cached passwords encrypted in Keychain (r17619 -43)
* fixed: 'svn ls' slow over ra_dav (issue #2151)
* fixed: 'svn import' not handling eol-style correctly (issue #2433)
* fixed: 'svn blame' should default operative rev range to peg rev (r18400)
* fixed: 'svn blame' ignores eol-style (issue #2431)
* fixed: 'svn checkout' should default operative rev to peg rev (r18422)
* fixed: 'svn diff' supports all eol styles (r17624 -8 -61 18195 -392)
* fixed: 'svn diff' multi-target memory leak (r17518)
* fixed: 'svn merge' showing wrong status with external diff3 (issue #1914)
* fixed: 'svn merge' not merging added dir into deleted dir (issue #2515)
* fixed: 'svn rm' of non-existent item should fail (issue #2440)
* fixed: 'svn status' should skip unversioned files (issue #2030)
* fixed: 'svn status' shows added and conflicted files as added (r20382)
* fixed: 'svn switch --relocate' may set wrong repos root (r17031)
* fixed: 'svn switch --relocate' memory leak (r19535)
* fixed: 'svn switch --relocate' not caching passwords (issue #2360)
* fixed: 'svn info' not showing locks sometimes (r19777)
* fixed: incorrect merge of add of binary file already in WC (issue #2403)
* fixed: possible dataloss if editing immediately after merge (r20609 -12)
* fixed: lots of diff wc<->repos bugs
* fixed: unfriendly error message on propget on nonexistent path (r19399)
* fixed: spurious revert report after manual conflict removal (issue #2517)
* fixed: don't allow -rPREV on schedule add path (issue #2315)
* fixed: keywords with dollar signs cause badness (issue #1780)
* fixed: really revert file with locally modified keywords (issue #1663)
* fixed: deleting schedule add file leaves working props file (issue #2419)
* fixed: svn:needs-lock and read-only-ness not always in sync (issue #2306)
* fixed: post-commit error output not sent to the client (issue #443)
* fixed: not locked error on commit of switched path (issue #2353)
* fixed: svn_apply_autoprops.py should trim whitespace from props (r20790)
* fixed: show locking notifications in local path style (r20927)
* fixed: encoding error on error messages from invalid options (r20883)
- Server:
* support for new 'svnsync' repository mirroring utility
* support for BDB 4.4, including automatic recovery (issue #2449)
* new contrib hook scripts:
- enforcer
- detect-merge-conflict.sh
- case-insensitive.py
* new tools script svn-backup-dumps.py
* new tools hook script log-police.py
* svnserve improvements:
- can now run as a native Windows service (r18855)
- new option --pid-file (r17836)
- allow the password database to be read-only (r16840)
* mod_dav_svn improvements:
- fixed: error conversion crash (r19516)
- fixed: unfriendly error when locking already locked path (issue #2275)
- fixed: xml escaping bugs (r19760 -85 -86)
* authorization improvements:
- new mod_dontdothat apache module (r19531)
- new mod_authz_svn directive AuthzSVNNoAuthWhenAnonymousAllowed (r18680)
- error out when authz rules contain unexpected characters (r19471)
* support .wsf hook scripts on Windows (r18972, 19076)
* lots of improvements to mailer.py and commit-email.pl
* FSFS back-end performance improvements (r17125 19119 -456 -58 -59)
* fixed: 'svnadmin verify' output not in native encoding (issue #1997)
* fixed: uuid file in FSFS could be destroyed on write error (issue #2193)
* fixed: FSFS path encoding bug (r17774)
* fixed: don't crash on corrupt repositories (r17625)
* fixed: expect error output from hook scripts in native encoding (r17101)
* fixed: catch errors starting hook scripts (r16891 17041 -81)
* fixed: svnserve and authz can cause broken WCs (issue #2566)
* fixed: the default hook script templates should be vanilla sh (r20796)
- Both:
* delta compression improvements:
- new delta encoding reduces size (r18363 -94 -66 -78 -98 -99 -457 -950)
- xdelta algorithm speed improvements (r18986, 19047)
* don't bail on invalid locale (r19445)
* improve speed of non-verbose svn ls (r17067 -71)
* fixed: delta combiner reading past EOF (r17743)
Developer-visible changes:
* require APR >= 0.9.7 to improve error detection for FSFS repos (r19915)
* require zlib, for svndiff1 delta encoding (r18363)
* support SWIG 1.3.29 (r19968)
* support autoconf 2.60-dev (r19919 20632 -36)
* removed no-longer-supported Red Hat 7.x RPMs (r20462)
* add support for building RPMs for x86-64 architecture (r20548 -552)
* numerous improvements to gen-make.py build system, especially on win32
* removed Visual Studio.NET APR 0.9 project files (r20170)
* numerous improvements to the test suite
* new public APIs:
- keyword / eol translation helpers and generic streams (see svn_subst.h)
- new generic stream helpers (see svn_io.h)
- authn providers made available to other clients (see svn_auth.h)
- svn_cmdline_setup_auth_baton
- svn_dso_initialize, svn_dso_load
- svn_client_diff_summarize and svn_client_diff_summarize_peg
- svn_client_list
- svn_config_has_section
- svn_txdelta_compose_windows and svn_txdelta_apply_instructions
- svn_txdelta_stream_create
- svn_diff_file_options_create and svn_diff_file_options_parse
- svn_err_best_message
- svn_compat_wrap_commit_callback
- svn_uuid_generate
- svn_user_get_name and svn_user_get_homedir
- svn_io_get_dir_filenames
- svn_ra_reparent
- svn_ra_replay
- svn_wc_revision_status
- several rev'd APIs, see doxygen docs
* flush stdout after each status/notification line (r19476 -656)
* new (experimental) ra_serf repository access module for pipelined DAV
* .svn/entries use a less verbose non-xml format (r19420)
* make recursive 'svn ls' streamy (issue #1809)
* remove svn-config script
* empty-file and README.txt removed from WC admin areas (r17181 -268 -364)
* replace cmdline client XML DTDs with RNG schemas (r16379 -80 -93 -571 17248)
* fixed: log --limit against old svnserve leaves unusable session (r19638)
* fixed: Solaris build problems (r19636)
* fixed: blame of WORKING revision shouldn't give BASE (r19558)
* fixed: svn_client_copy and _move should fail if target exists (issue #2188)
* fixed: svn_io_file_rename and readonlyness on Windows and UNIX (r17366 -69)
* fixed: ra_dav memory leak when reusing session (issue #2247)
* fixed: console character encoding problems when built with VS2005 (r20108)
* fixed: various problems with --enable-dso and global pools (r20996, r20999)
* fixed: installer file syntax error in new versions of Inno Setup (r21022)
* SWIG bindings:
- SWIG/python bindings:
- new support for svn_client_info (r19413)
- SWIG/ruby bindings:
- full support for Subversion 1.4 APIs, including :
svn_ra_replay and svn_diff_summarize
- numerous bug fixes
- add ruby documentation (make install-swig-rb-doc) (r20166)
- add APIs for adding a provider (r21079)
- SWIG/perl bindings:
- new support for svn_client_info (r18758)
- minor corrections to SVN::Fs (r19312)
* javahl bindings:
- APIs to get version info for the native libraries (r17604 -07)
- API for path validation (r18989, r19079)
- C++/Java code refactoring, cleanup, and consolidation
- fixed: handle possible errors from date/time conversions (r17213)
- fixed: SVNClient username/password JVM crash on null input (r19803 -13)
- fixed: specify default UUID load action (r18030)
- fixed: compile error on Visual Studio 2005 (r18054)
Version 1.3.2
(23 May 2006, from /branches/1.3.x)
http://svn.apache.org/repos/asf/subversion/tags/1.3.2
User-visible changes:
- Client:
* fixed: 'svn st -u' crash on missing subdirs (r19348, -71, issue #2551)
* fixed: leaving stray working copy locks on cancellation (r18893)
* fixed: svn_load_dirs.pl trying to import .svn and _svn dirs (r18549)
* svn_load_dirs.pl symlink support (issue #2478)
* translation updates to Japanese, Traditional Chinese.
- Server:
* fixed: mod_dav_svn memory leak when listing large dirs (r19528)
* fixed: mod_dav_svn crash on valid request (r19520)
* fixed: svnserve protocol error in lock, causing client hang (issue #2548)
* mailer.py: add Content-Transfer-Encoding header (r19319)
* mailer.py: fixed: named substitutions incorrectly ignored (r18114, -681)
* fixed: authz requires read access for root for writes (issue #2486)
* svnauthz-validate: add config file validation tool (r18504, -09)
Developer-visible changes:
* fixed: tests don't catch repository creation failure properly (r19149,-51)
* support SWIG 1.3.28
* support APR 0.9.x >= 0.9.10 (r19039, -57, -60)
* python bindings:
- fixed: link error on OpenBSD (r18983)
* ruby bindings:
- fixed: memory leak (r19493)
- fixed: NULL argument conversion bug (r19543)
Version 1.3.1
(25 March 2006, from /branches/1.3.x)
http://svn.apache.org/repos/asf/subversion/tags/1.3.1
User-visible changes:
- Client:
* fixed: segfault moving unversioned files (issue #2436)
* fixed: verbose list broken over ra_dav (issue #2442)
* fixed: 'svn ci -m path_name' not requiring '--force-log' (r17956)
* fixed: crash on mixed-case https URL scheme (r18042)
* fixed: crash in status with ignored directories (r18291)
* fixed: strip peg rev from default checkout directory (r18416)
* fixed: diff crash with non-recursive checkout (r17231, 18539, -41)
* fixed: 'svn ls' URL encoding bug with locks (r18665, -68)
* fixed: unlock circumvents lock token check (r18691, -94)
* fixed: repos-to-repos copy crash (r18451)
* fixed: 'svnmerge' utility improvements (r18811)
* translation updates for German, Swedish and Norwegian
- Server:
* fixed: set svn:date at the end of commit in fsfs (r18078)
* fixed: don't wait for hook script background jobs (r18146)
* fixed: mod_dav_svn should log the whole error chain (r18211)
* fixed: uncomment section headers in repos config files (r18247, -50)
* fixed: log scalability issues with many paths (r18395, -404)
* fixed: better path input validation in mod_dav_svn (r18660)
* fixed: assert in copy in fsfs and bdb (issue #2398)
* fixed: RPM package bad interaction with NFS servers (issue #1456)
- Both:
* fixed: copyright years updated to include 2006 (r18021, -127)
Developer-visible changes:
* fixed: missing #include (r18065)
* fixed: allow building with Neon 0.25.5 (r18215)
* fixed: error leaks (18196, -249)
* javahl bindings:
- fixed: compile error on Visual Studio 2005 (r18054, -55)
* python bindings:
- fixed: libsvn_swig_py link problem on Solaris 10 (r17910)
- fixed: pool lifetime bug (r17992)
- fixed: memory leak (r18230)
- fixed: race condition during application pool initialization (r18721)
- fixed: Make pool parameters optional (issue #2444)
* ruby bindings:
- fixed: pool management issue (r17795, -811)
- fixed: protect baton from garbage collection (r17627)
- fixed: conversion bug (r17726, -925)
- fixed: compile errors with SWIG 1.3.24 (r18456, -58)
Version 1.3.0
(30 December 2005, from /branches/1.3.x)
http://svn.apache.org/repos/asf/subversion/tags/1.3.0
User-visible changes:
- Client:
* 'svn ls -v' now shows remote locks (issue #2291)
* 'svn status' speedup (r15061, r15103)
* 'svn blame' speedup on files with long history (issue #1970)
* 'svnversion' now assumes default argument of '.' (r14892)
* support for neon 0.25.x, which fixes http:// control-c bug (issue #2297)
* support for more ISO-8601 date formats, compatible with GNU date (r14428)
* support for single-digit date components (r15459)
* on Windows, '_svn' admin dir now toggled by runtime env. variable (r16244)
* working copy size with empty propfiles reduced (r16855, see releasenotes)
* new switches added:
- 'svn blame --xml [--incremental]' (r14690)
- 'svn status --xml [--incremental]' (issue #2069)
- 'svn info --xml [--incremental]'
- 'svn add/import --no-ignore' (issue #2105)
- 'svnlook tree --full-paths' (r13976)
- 'svnlook diff --diff-copy-from' (r14855)
- 'svnlook changed --copy-info' (r16681)
* fixed: 'svn copy wc URL' might include deleted items (issue #2153)
* fixed: 'svn copy wc wc' allows cross-repository copies (issue #2404)
* fixed: 'svn up/merge' major property-merging bugs (issue #2035)
* fixed: 'svn merge' insisting on write access to '.' (issue #2411)
* fixed: 'svn merge' cross-device move problems (r16293, -329, -330)
* fixed: 'svn diff' outputs headers in wrong encoding (issue #1533)
* fixed: 'svn proplist/add/cat' dies on unversioned items (issue #2030)
* fixed: 'svn add' not honoring svn:ignore property (issue #2243)
* fixed: 'svn log -rN:M --limit X' error over http:// (issue #2396)
* fixed: 'svn switch --relocate' failure on 'deleted' dir (r16673)
* fixed: 'svn info' not always showing repos lock (issue #2276)
* fixed: 'svn info' might show lock on wrong path (r16626)
* fixed: 'svnlook' chokes on logs with inconsistent newlines (r14573)
* fixed: 'svnlook propget --revprop -t' failure (r15203)
* fixed: 'svnversion' wrongly traverses into externals (r15161)
* fixed: incorrect URI encoding passed to svn+ssh:// (issue #2406)
* fixed: properly handle filenames containing '@' (issue #2317)
* fixed: '--non-interactive' now suppresses launch of $EDITOR (r15277)
* fixed: conflict markers not in current encoding (r14621)
* fixed: commands ignoring extraneous -m or -F switches (issue #2285)
* fixed: poor error-checking when using revprops (r15542)
* fixed: stack-smashing bugs (r15948, r16037)
* fixed: incorrect parsing of mod_dav_svn XML responses (r17589)
* translation updates for all languages
- Server:
* svnserve improvements:
- can now restrict read/write access by path (see releasenotes)
- undeprecation of the --read-only (-R) option (r17614)
* mod_dav_svn improvements:
- 'SVNListParentPath on' shows all repositories in web browser (r16158)
- ability to log high-level client operations (see releasenotes)
- sets svn:mime-type on autoversioning commits (r14359)
* 'svn log' performance improvement (r14722)
* fixed: fs history algorithm might return wrong objects (issue #1970)
* fixed: repos deadlock when hooks output too much (issue #2078)
* fixed: mod_dav_svn displays errors with sensitive paths (r14792)
* fixed: anonymous reader could create empty commits (issue #2388)
* fixed: possible segfault to callers of trace_node_locations() (r16188)
* fixed: BDB-style locking actions on FSFS repositories (r16295, r16297)
* fixed: numerous bugs running BDB commands on FSFS (issue #2361, r16388)
* fixed: svndumpfilter incorrectly remapping dropped revs (issue #1911)
- Both:
* faster multiple (un)locks in a single svn:// request (issue #2264)
* the Subversion Book is no longer bundled (r17466)
Developer-visible changes:
* reorganization of automated tests, including ability to run on ramdisk
* lots of Doxygen/API documentation cleanup
* numerous improvements to gen-make.py build system, especially on win32
* working copy is now storing repos_root as separate field (issue #960)
* keywords are now stored in an internal hash (issue #890)
* client status APIs now makes more server-side info available (r16344)
* new public APIs:
- new transfer progress callback for DAV (r15948)
- svn_ra_initialize(), svn_client_open_ra_session()
- svn_fs_closest_copy(), svn_fs_type()
- several rev'd APIs, see doxygen docs
* SWIG bindings: No more compile-time or runtime SWIG dependencies
- SWIG/python bindings:
- automatic memory management: APIs no longer require pool arguments!
- improved stability, as shown by our new testsuite
- better error messages
- SWIG/ruby bindings:
- complete API coverage!
- automatic memory management
- greatly expanded test suite
- SWIG/perl bindings:
- new accessors for svn_lock_t, svn_fs_access_t
- a number of bugfixes
* javahl bindings:
- add streamy API for fetching file contents (r15584)
- fixed: let tests run before bindings are installed (issue #2040)
- fixed: lock command not raising errors properly (issue #2394)
- fixed: ignored errors from svn_client_blame2() (r16434)
Version 1.2.3
(19 August 2005, from /branches/1.2.x)
http://svn.apache.org/repos/asf/subversion/tags/1.2.3
User-visible changes:
- Client:
* fixed: 'svn status -u' fails against pre-1.2 mod_dav_svn (r15359, r15423)
* fixed: 'svn export' segfault (r15516)
* fixed: 'svn merge' memory leak (r15233)
* fixed: horrible rename-tracing performance against 1.0 servers (r15315)
* fixed: 'svn cat' over file:// -- small leak (r15253)
* fixed: crash with "svn lock" and authentication (r15703)
* improvements to 'svnmerge' utility (r14008,-458,-587,-632, r15329,-340)
* translation updates for French, German, Polish, Norwegian, Swedish,
Korean
- Server:
* fixed: mod_authz_svn being overly restrictive (r15463)
* fixed: fsfs directory caching bug (r15705, r15742)
- Both:
* fixed: crash when >50 options passed to any commandline app (r15251)
* fixed: memory leak in character translation handle caching (r15379,-398)
Developer-visible changes:
* fixed: crash when calling svn_client_(un)lock with no targets (r15734)
* rhel-4 RPM bugfix for python bindings (r15616)
* missing #include in SWIG bindings (r15683)
* javahl bindings:
- fixed: JNI library loading bug (r15552)
- fixed: JNI stack-name cut and paste error (r15337)
- fixed: crash when revisions have no dates (r15737)
* perl bindings:
- now compatible with SWIG 1.3.25 (r15248)
- allow SVN::Pool to be used as pool parameter (r15450)
- make SVN::Delta::Editor friendlier for debugging (r15609)
- fixed: wrap svn_ra_stat properly (r15713)
- fixed: bug in SVN::Core::Stream's read function (r15698, r15700)
* ruby bindings:
- now compatible with SWIG 1.3.25 (r14980, r15361)
Version 1.2.2
(Not released, see changes for 1.2.3.)
Version 1.2.1
(5 July 2005, from /branches/1.2.x)
http://svn.apache.org/repos/asf/subversion/tags/1.2.1
User-visible changes:
- Client:
* fixed: 'svn lock' on switched file locks wrong thing (issue #2307)
* fixed: 'svn (un)lock' errors on multiple targets (r14736, 14775)
* fixed: 'svn (un)lock' problems with URI-unsafe names (issue #2314)
* fixed: 'svn (un)lock' not caching authentication (r15088)
* fixed: 'svn unlock' loses executable bit (r14859, r14923, r14939)
* fixed: 'svn unlock URL' segfault (r14893)
* fixed: 'svn commit' failure on XML-unsafe locked paths (issue #2335)
* fixed: recursive directory copy bug (issue #2343)
* fixed: don't initialize RA library in 'svnversion' (r14755)
* fixed: svn-push segfault (r14732)
* various translation updates for localized client messages
- Server:
* fixed: 'svn log' performance regression, general (r14116, 14772, 14759)
* fixed: 'svn log -v' performance regression, FSFS-specific (r15016)
* fixed: mod_dav_svn bug sets content-type incorrectly (r15046)
Developer-visible changes:
* fixed: win32 innosetup's add/repair/remove features (r14830)
* fixed: OBOE with 'limit' parameter of svn_repos_get_logs3(). (r15119)
* redhat RPM fixes (r15050)
* perl bindings:
- accessors for svn_lock_t (r15082)
- call utf_initialize, adjust global pool usage (r15076, r15080,
r15081, r15117)
Version 1.2.0
(21 May 2005, from /branches/1.2.x)
http://svn.apache.org/repos/asf/subversion/tags/1.2.0
See the 1.2 release notes for a more verbose overview of the changes since
the 1.1 release: http://subversion.apache.org/docs/release-notes/1.2.html
User-visible changes:
- Client:
* add peg-rev syntax to co/blame/cat/ls/pget/plist/export (issue #1093)
* 'svn info' now works on URLs (r13123, 13144)
* 'svn* --version' now shows available repository back-ends (r13761)
* new fixed-length keywords (for placement in binary files) (issue #2095)
* on Windows, disk-cached passwords are now encrypted (r13888)
* performance improvements:
- 'svn status' does much less disk parsing (r11677, 11704)
- 'svn st -u' no longer asks server to generate textdeltas (issue #2259)
- 'svn revert -R' doing much less work (r13883)
- utf8<->native conversions are faster now (issue #2016)
* new switches added:
- 'svn commit --no-unlock - retain lock in wc upon commit
- 'svn log --limit N' - show only first N log messages
- 'svn info --revision' - show info on older object (r13265)
- 'svn list --xml' - output listing in XML
- 'svn propset --force' - allow unusual propsets (#2065)
- 'svn diff --force' - show diffs on binary files (#2099)
- 'svn co/up/st --ignore-externals' - skip over externals (#2189)
- 'svn export --non-recursive' - don't export subdirs (issue #2228)
- 'svnversion --help' - show help (r13128)
* fixed: 'svn merge' fails to add symlinks or expand keywords (issue #2064)
* fixed: 'svn merge --dry-run' shows spurious 'skip' messages (issue #1943)
* fixed: 'svn merge' file-not-found' error (issue #1673)
* fixed: 'svn merge' of propchanges into deleted file (issue #2132)
* fixed: 'svn merge' on implicit target with space (r13010)
* fixed: 'svn merge/diff URL URL' can cause httpd timeout (issue #2048)
* fixed: 'svn switch/update' failure might corrupt wc (issue #1825)
* fixed: 'svn up' should rm before add, helps case-insensitivity (r12616)
* fixed: 'svn up -rX' causes file to be unrestorable (issue #2250)
* fixed: 'svn copy wc wc' should keep .svn/ hidden (issue #1739)
* fixed: 'svn copy wc wc' of deleted=true doesn't delete (issue #2101)
* fixed: 'svn copy' shouldn't copy into schedule-delete area (issue #2020)
* fixed: 'svn copy dir dir' infinite recursion (issue #2224)
* fixed: 'svn log' throws error on unversioned target (issue #1551)
* fixed: 'svn log' in r0 working copy shows r1 log msg (issue #1950)
* fixed: 'svn export' bugs on deleted dirs or nonexistents (#2226, r13226)
* fixed: 'svn export' on single file from working copy (issue #1708)
* fixed: 'svn import' creating an empty revision (r14293)
* fixed: 'svn commit' ignores --encoding when editing externally (#2244)
* fixed: 'svn commit' log message lost if utf8-conversion failure (r13230)
* fixed: 'svn diff' output encoding bug (r11461)
* fixed: 'svn diff' showing prop-diffs on repos root dir (r13381-2)
* fixed: 'svn diff' label reversal (issue #2033)
* fixed: 'svn propget' prints extra newline in --strict mode (r14505)
* fixed: 'svn propset' should skip unversioned files (#2030)
* fixed: 'svn rm URL1 URL2 URL3...' huge memory usage (issue #2218)
* fixed: 'svn mkdir' cleanup after failure (r11883)
* fixed: 'svn status -u' crash in non-recursive wc's (issue #2122)
* fixed: 'svn revert' should skip unversioned items (issues #2030, 2133)
* fixed: 'svn revert' should suggest --recursive (issue #2114)
* fixed: 'svn add/import' better detects invalid paths (issue #1954)
* fixed: 'svn cleanup' should repair timestamps (r12012)
* fixed: 'svn cat -rBASE' contacts repository (issue #1361)
* fixed: fuzzily escape control-characters when sending over dav (#2147)
* fixed: prevent client from manipulating svn:wc:* properties (r12523)
* fixed: allow portnumber in svn+ssh://user@host:port/ URLs (r14373)
* fixed: xml-escaping bugs over dav (r11090)
* fixed: store symlinks as utf8, always work in non-utf8 locale (r11358-9)
* fixed: bug in special-file detranslation (r11441)
* fixed: show paths in local-style where we weren't (issue #1538)
* fixed: detect invalid propnames better (issue #1832)
* fixed: entire error stack not being printed (issue #1822)
* fixed: improper utf8 conversion of revision strings (issue #1999)
* fixed: use-commit-times timestamp bug (r12906)
* fixed: don't comment out section-names in default config file (r11771)
* more support for user-cancellation (r13083-4, 13086)
* improved error messages (r12920, 11392, 11599, 11913, #2154, #2214)
- Server:
* mod_dav_svn autoversioning feature now complete (see release notes)
* 'svnadmin create' now creates FSFS repositories by default (r13624)
* new pre/post-revprop hook argument to describe propchange (r12162)
* mod_authz_svn groups can now contain other groups (issue #2085)
* 'svnadmin recover' now creates default svnserve passwd file (r11589)
* increase default BDB cache size in DB_CONFIG (r13030)
* new switches added:
- 'svnlook diff --no-diff-added' - suppress added files (#2180)
- 'svnlook propget/proplist --revprop' - show revision props (#2181)
- 'svnadmin load --use-pre-commit-hook'
'svnadmin load --use-post-commit-hook'- invoke hooks when loading
* fixed: FSFS race condition on posix platforms (issue #2265)
* fixed: change FSFS revprops atomically and safely (issue #2193)
* fixed: FSFS should verify checksums (issue #2253)
* fixed: FSFS crash bug (r14333)
* fixed: 'svnadmin create' should clean up when it fails (r13200)
* fixed: 'svnadmin load' compatibility on pre-0.14 dumpfiles (r12075)
* fixed: 'svnadmin load' crashes on contentful rev 0 (issue #1674)
* fixed: 'svnadmin dump' should write in console encoding (issue #1997)
* fixed: check for null-streams in dump/load code (r10510)
* fixed: hook script ignored when symlink is broken (issue #1700)
* fixed: hook script may inherit server's stdin stream (r12155)
* fixed: potential svnserve segfault (r13199)
* fixed: svnserve handling mutually-exclusive options (issue #2251)
* fixed: mod_authz_svn should log errors to httpd errorlog (issue #2182)
* fixed: 'svnadmin hotcopy' failed to copy format files (r14678, r14683)
* mailer.py: add win32 compatibility, plus other bugfixes
- Both:
* new 'locking' feature (issue #1478, see release notes for details):
- new: 'svn lock/unlock', 'svnadmin lslocks/rmlocks', 'svnlook lock'
- new: 'svn:needs-lock' property to enable communication
- 'svn st [-u]' shows local or remote lock overview
- 'svn info wc | URL' shows local or remote lock details
- 'svn commit' sends locks, 'svn up' removes stale locks
- new hook scripts: pre-lock, pre-unlock, post-lock, post-unlock
* speedups for 'svn blame' and other commands (see xdelta in release notes)
* fixed: make both svnserve and svn:// urls work with IPv6 (r13235-6)
* fixed: updating xml-unsafe dirname over http (issue #2268)
* new translation of localized messages: French
* continued improvement of localized message translations:
- German, Spanish, Polish, Brazilian Portuguese, Norwegian Bokmål,
Swedish, Traditional Chinese, Simplified Chinese, Korean, Japanese
- more localized messages in all svn-related binaries
Developer-visible changes:
* binary diff algorithm now defaults to xdelta instead of vdelta
* huge number of new APIs:
- new locking APIs in svn_client.h, svn_ra.h, svn_repos.h, svn_fs.h
- new 'flattened' svn_ra.h API, which imitates svn_fs.h (issue #1931)
- new notification API in svn_client.h, svn_wc.h
- http://svn.haxx.se/dev/archive-2005-04/0319.shtml has all API changes
* fs now has its own 'format' file, independent of repos 'format' (r13387)
* improve efficiency of delta combining algorithm (r13016, r13063)
* make all BDB apis take explicit pool parameters (r13198, r13205)
* remove libsvn_fs_base caching of node revisions (r13299)
* libsvn_repos commit editor can now take incoming txn (r13733)
* fixed: mod_dav_svn sending illegal editor-drive (issue #2258)
* pool usage improvements (r12954, 12852, r13386, issue #1310)
* SWIG bindings: better API coverage overall.
- new ruby bindings!
- remove bitrotting swig-java bindings
- perl and python bindings: numerous improvements, see their own logs.
- bindings tests now within svntest framework
* javahl bindings: numerous improvements, see its own logs.
* many improvements to mailer.py and commit-email.pl
* rewrite/improvements to gen-make build system, including VS.NET support
* many improvements to the automated python testsuite (issue #2257)
* book moved to separate repository (http://svn.red-bean.com/svnbook)
Version 1.1.4
(1 April 2005, from /branches/1.1.x)
http://svn.apache.org/repos/asf/subversion/tags/1.1.4
User-visible changes:
- Client:
* fixed: win32 not ignoring versioned symlinks (issue #2173)
* fixed: 'svn merge' can cause broken working copy (issue #2222)
* fixed: 'svn commit' fails when schedule-delete dir has local mod (r11980)
* fixed: 'svn st -u nonexistent_file' segfault (issue #2127)
* fixed: 'svn cp wc wc' utf8 conversion error (r13111)
* fixed: confusing error message about "wc not locked" (issue #2174)
* many translation updates for localized client messages
- Server:
* fixed: nasty (though unusual) performance bug in FSFS commits (r13222-3)
* fixed: FSFS memory leak when auto-merging large tree (r13193)
* fixed: FSFS memory leak in 'svnadmin hotcopy' (r13218, 13465, 13468)
* fixed: FSFS segfault when encountering empty data reps (r13683)
* fixed: two dataloss bugs in svndumpfilter (r12630, r12636)
* fixed: wasteful memory usage in svndumpfilter (r12637, r12640)
* fixed: mod_dav_svn segfaults when client sends bogus paths (issue #2199)
* make mailer.py work on win32 (r12499, r12542, r12670)
- Both:
* fixed: (win32) retry file operation if sharing violation (r12983, r12986)
Developer-visible changes:
* add SWIG 1.3.24 and .25 compatibility (r12551, r12717-9, r12722, r13504)
* fixed: JavaHL run-time link error (r12576), path/url cleanups (r13090)
* fixed: python bindings log_receiver failure with SWIG 1.3.24 (r13487)
* build system tweaks: add install dependencies for fs & fs_base (r11050)
Version 1.1.3
(14 January 2005, from /branches/1.1.x)
http://svn.apache.org/repos/asf/subversion/tags/1.1.3
User-visible changes:
- Client:
* translation updates for localized client messages.
Developer-visible changes:
* Fix a compile error in the Perl bindings.
Version 1.1.2
(20 December 2004, from /branches/1.1.x)
http://svn.apache.org/repos/asf/subversion/tags/1.1.2
User-visible changes:
- Client:
* fixed: 'svn switch' interruption can break working copy (issue #1826)
* fixed: 'svn switch' memleak over ra_dav (issue #2106)
* fixed: 'svn blame' algorithm bug (r11527)
* fixed: invoke external diff/diff3 with local-style paths (r11689)
* fixed: 'svn status' handling of missing subdirs (r11936)
* fixed: 'svn ls -v' encoding bug (r11740)
* fixed: 'svn ls "file with space"' bug (r12273, r12393)
* fixed: 'svn merge' should URI-encode copyfrom URLs (issue #1905)
* fixed: 'svn merge' deletion output formatting (r12100, r12111, r12114)
* fixed: 'svnversion --version .' crash (r11438)
* fixed: UNC paths on Cygwin (issue #2108)
* fixed: win98 iconv bug -- uninitialized variable (issue #2091)
* improved 'svn status' performance:
- do fewer check_path calls (r11592)
- 'svn status file' shouldn't recursively lock tree (r11439, r11669)
* translation updates for localized client messages.
- Server:
* fixed: 'svnadmin load' race condition (r12327)
* fixed: fsfs memleak in commit finalization (r11706)
* fixed: fsfs memleak in inefficient directory removal (r11701)
* fixed: fsfs commits use insert-only perms on db/revs/ (r11665)
* fixed: fsfs creates lockfile at creation time, not at 1st commit (r12172)
* fixed: svndumpfilter mislabeling output as version 3 (issue #2142)
* fixed: 'svnserve -h' encoding bug (part of issue #1997)
* fixed: prevent cross-repository copies (r12003)
* fixed: increase log-region max size in default DB_CONFIG (issue #2159)
- Both:
* fixed: 'svn switch' quietly corrupting working copy (issue #2124)
* fixed: canonicalize paths sent by ra_svn/svnserve (issue #2119)
* fixed: memleak into UTF8 translation routines (r11689)
Developer-visible changes:
* add support for BerkeleyDB 4.3 (if using a compatible apr-util)
* add support for any apr/apr-util 1.X
* disallow incompatible SWIG versions (r12450)
* fixed: slight API/ABI incompatibility between 1.0.9 and 1.1.x (r12102)
* fixed: perl bindings pool usage & object refcounts (r11451, r11630)
* fixed: perl bindings pool usage and potential memleak (r12397)
* fixed: javahl crash trying to fetch nonexistent property (r12184)
* fixed: javahl build can fail due to missing dirs (issue #2032)
* fixed: RPM build breakage (issue #2111)
* fixed: i18n issues for windows installer (r11685)
* allow build system to update single .po file (r11763)
Version 1.1.1
(22 October 2004, from /branches/1.1.x)
http://svn.apache.org/repos/asf/subversion/tags/1.1.1
User-visible changes:
- Client:
* fixed: 'svn status' win32 performance regression (issue #2016)
* fixed: 'svn ls' dying on non-ascii paths over DAV (issue #2060)
* fixed: allow URI-encoded colon or pipe on win32 (issue #2012)
* fixed: broken win32 UNC paths (issue #2011)
* fixed: memory bloat when committing many files over DAV (r11284, -321)
* fixed: eol-style translation error for 'svn propget' (r11202, -243)
* fixed: 'svn propedit' does EOL conversion properly (issue #2063)
* fixed: 'svn log --xml' shouldn't be locale-dependent. (r11181)
* fixed: 'svn export' of symlinks with 'use-commit-times' (r11224)
* fixed: 'svn export -rBASE' when WC has added items (r11296, -415)
* many translation updates for localized client messages.
- Server:
* fixed: 'svn ls' HTTP performance regression (r11211, -232, -285)
* fixed: make it possible to set "SVNPathAuthz off" in httpd.conf (r11190)
* fixed: fsfs validating revisions when accessing revprops (issue #2076)
* fixed: 'svn log -v' hiding too much info on 'empty' revisions. (r11137)
* fixed: encoding bug with 'svnlook log'/'svnlook author' (r11172)
* fixed: allow mod_authz_svn to return '403 Forbidden', not 500 (r11064)
* fixed: XML-escape author and date strings before sending (issue #2071)
* fixed: invalid XML being sent over DAV (issue #2090)
Developer-visible changes:
* fixed: IRIX compile error (issue #2082)
* fixed: error in perl bindings (r11290)
* fixed: error leaks in mod_dav_svn (r11458)
* fixed: javahl should use default config directory (r11394)
Version 1.0.9
(13 October 2004, from /branches/1.0.9)
http://svn.apache.org/repos/asf/subversion/tags/1.0.9
User-visible changes:
- Server:
* fixed: 'svn ls' HTTP performance regression (r11211, -232, -285)
* fixed: 'svn log -v' hiding too much info on 'empty' revisions. (r11137)
Developer-visible changes:
* fixed: make redhat 7/8 rpm scripts build the book correctly (11143)
Version 1.1.0
(29 September 2004, from /branches/1.1.x)
http://svn.apache.org/repos/asf/subversion/tags/1.1.0
See the 1.1 release notes for a more verbose overview of the changes since
1.0.x: http://subversion.apache.org/docs/release-notes/1.1.html
User-visible changes:
* new non-database repository back-end (libsvn_fs_fs)
* symlinks can now be placed under version control (unix systems only)
* cmdline client now supports psuedo-IRIs and autoescapes chars (issue #1910)
* 'svnadmin recover' no longer waits forever for a lock (new '--wait' option)
* new $Revision$ synonym for $Rev$ and $LastChangedRevision$
* new runtime option 'store-passwords = ' gives finer control (r10794)x
* fixed: working copies now shareable by multiple users (issue #1509)
* fixed: diff and other subcommands correctly follow renames (issue #1093)
- new 'peg' syntax for diff/merge: 'svn diff -r X:Y TARGET@REV'
- now able to compare working copy with URL: 'svn diff --old WC --new URL'
* new framework for localized error/info/help messages, initial translations:
- German, Polish, Swedish, Norwegian Bokmål, Traditional Chinese,
Japanese, Brazilian Portuguese.
* speed improvements:
- faster 'svn up' on complex working copies -- no more repos txns (r8840)
- faster 'svn status' -- fewer stat() calls (r9182)
- faster 'svn checkout' -- fewer sleep() calls (r9123)
- faster 'svn blame' -- new RA->get_file_revs() func (issue #1715)
* new switches added:
- 'svn blame --verbose' - show extra annotation info
- 'svn export --native-eol TYPE' - export using TYPE line-endings
- 'svn add --force' - recurse into version-controlled dirs
- 'svnadmin dump --deltas' - include binary diffs in dumpfile
- 'svnadmin create --fs-type fsfs' - create fs_fs repos (default is bdb)
- 'svnserve --tunnel-user=NAME' - assume authenticated NAME over tunnel
- 'svndumpfilter [cmd] --quiet' - less chatty dumpfiltering
- 'svnserve --version' - show program's version
'svnversion --version'
'svndumpfilter --version'
* svnadmin dump/deltify now understand -r{DATE} (r9805)
* allow update of non-existent target entry (partial issue #1902 fix)
* 'svnadmin create' now sets sgid bit on repos/db/ (unix systems only)
* increase default neon (ra_dav) timeout from 120 to 3600 seconds (r9568)
* print verbose BDB error messages (r10557, r10566)
* fixed: don't bail when 'svn up' refuses to delete local mods (issue #1806)
* fixed: process svn:externals in defined order (issue #1788)
* fixed: pass new propval to stdin of pre-revprop-change hook (issue #952)
* fixed: svndumpfilter logic/memory/display bugs (r8691, 8831, 9061)
* fixed: 'svnadmin hotcopy PATH .' (r8659)
* fixed: copy crash bug (r8863)
* fixed: 'svn st -u' crash bug (r10841)
* fixed: 'svn commit' segfault (r10676)
* fixed: allow cleanup on .svn/ dirs containing KILLME file (r8891)
* fixed: 'svn revert' detects corrupted text-base (r8897)
* fixed: 'svn status -N' no longer locks entire tree (r8906)
* fixed: several different 'svn switch' bugs (r9192, 9203, 9238, 9698)
* fixed: some 'svn copy' bugs (r9193, 9274)
* fixed: obscure update-deletion bug (r8976)
* fixed: utf8 conversion 'hang' (r9233)
* fixed: missing UTF8->native recoding in 'svn log' output (r10652, 10673)
* fixed: 'svn blame' now defaults to rev (r9440)
* fixed: 'svn blame' closing files before deleting them (issue #1969)
* fixed: 'svn diff' shows truncated paths (r9693)
* fixed: 'svn diff --notice-ancestry' bug (r9699)
* fixed: 'svn subcommand -r{DATE} URL' works if URL not in HEAD (issue #1840)
* fixed: 'svn blame' on non-ascii path truncation (issue #1770)
* fixed: svn:external 'wc not locked' bug (issue #1897)
* fixed: proper mod_dav_svn html/xml escaping (issue #1209)
* fixed: memleak in 'svn propset -R URL' (issue #1928)
* fixed: stop 'svn up' from deleting schedule-add target dir (issue #1793)
* fixed: 'svn merge' adding a directory already 'deleted' (issue #1769)
* fixed: excessive memory use when fs deltifies revision 2^N (r10070)
* fixed: disallow non-recursive directory commit (issue #1797)
* fixed: allow propget of props with colon in name (issue #1807)
* fixed: 'svnadmin load' computation of copyfrom-rev (issue #1795)
* fixed: runtime config files created with proper line-endings (issue #1803)
* fixed: make svnserve's authn work on usernames with spaces (r10385)
* fixed: have svnserve use repos UUID as default authn realm (r10394)
* fixed: segfault when history-following hits 'empty' revision (r10368)
* fixed: overzealous out-of-dateness checks in 'svn cp wc URL' (issue 1994)
* fixed: don't URI-encode path in mod_dav_svn XML listings (r10461)
* fixed: 'svn info' should refuse URL targets (r10760)
* fixed: incomplete-directory handling bug (r10956)
* fixed: allow cancellation between files during recursive dir add (r10894)
* general improvement and normalization of error messages
* many improvements to contributed tools: mailer.py, psvn.el, etc.
Developer-visible changes:
* libsvn_fs now loads either bdb (libsvn_fs_base) or fsfs (libsvn_fs_fs)
* new console-printing API: svn_cmdline_printf() family checks for errors.
* new library-version querying API:
- new svn_[libname]_version() in each library
- svn_ver_*() family of functions
* 2nd generation APIs, from svn_foo() --> svn_foo2(). old APIs deprecated.
- svn_wc_adm_open2() & friends, svn_wc_export2(), svn_client_add2()
svn_wc_parse_externals_description2(), svn_hash_read/write2(),
svn_repos_dump/load_fs2() & friends, svn_wc_diff2(),
svn_subst_copy_and_translate2()
* other new APIs:
- svn_stream_copy(), svn_txdelta_target_push(), svn_opt_parse_path(),
svn_io_file_flush_to_disk, svn_repos_trace_node_locations(),
svn_repos_get_file_revs(), RA->get_locations(), RA->get_file_revs,
RA->get_version(), svn_sort_compare_paths(), svn_utf_initialize()
* SVN_REVNUM_FMT_T usage replaced with %ld (r9691)
* cache mod_authz_svn authz file per connection (r8867)
* validate hex digits in % escape (issue #1947)
* hashes now written to disk in sorted order (r9910)
* do cancellation checks before loops, not after (r8918)
* fixed: bug in svn_repos_dir_delta replacement logic (r8078)
* fixed: tiny memory access bugs (r8229, 8230, 8313)
* fixed: several commit buglets (r8955, 9658, 9757, 9855)
* fixed: don't recursively lock all prop commands (r9172)
* fixed: svnserve memory usage on many-file commits (r9185)
* fixed: close svnserve child's listen-socket after forking (r10050)
* fixed: 'svnadmin hotcopy' integrity improvements (issues #1817, #1818)
* fixed: only verify media type of svn:mime-type, not encoding (r10126)
* fixed: handle '//' and '..' in svn_path_canonicalize (issue #1779)
* fixed: double URI escaping (issue #1814)
* fixed: editor-driver bug (don't delete before every copy) (r10851)
* fixed: potential mod_dav_svn crashes/memleaks (r10478)
* fixed: better 'svnadmin verify verification (r10508, r10509)
* fixed: encoding of get_repos_url_result (r10353, 10375)
* fixed: prevent canonicalized URIs from ending in '/' (r10317)
* stop using -std=c89 gcc flag (r11054)
* sync with apr 1.0's find_apr.m4 and find_apu.m4 files (r10560)
* win32 installer improvements (r10978)
* huge improvements to python, perl, java bindings
* huge changes to win32 build system
Version 1.0.8
(22 September 2004, from /branches/1.0.8)
http://svn.apache.org/repos/asf/subversion/tags/1.0.8
User-visible changes:
* fixed: mod_authz_svn path and log-message metadata leaks.
See CAN-2004-0749, and descriptive advisory at
http://subversion.apache.org/security/CAN-2004-0749-advisory.txt
Version 1.0.7
(17 September 2004, from /branches/1.0.x)
http://svn.apache.org/repos/asf/subversion/tags/1.0.7
User-visible changes:
* fixed: win32 'file not found' error [issue #1862]
* fixed: 'svn st -u' crash (r10841)
* fixed: potential repos corruption; ensure stdin/out/err always open (r10819)
* fixed: allow propnames containing ":" to be fetched via http:// (r10190)
* fixed: allow user to interrupt between authentication prompts (see r11014)
* fixed: work around +t directory-creation bug in APR (r10616, 10638, 10642)
* various small fixes to Book
Developer-visible changes:
* fix library dependencies for bindings (r9338, 9340)
* java bindings: fix a crash and other bugs (r9883, 9905, 8027)
* perl bindings: various fixes (see r11023)
Version 1.0.6
(19 July 2004, from /branches/1.0.x)
http://svn.apache.org/repos/asf/subversion/tags/1.0.6
User-visible changes:
* fixed: crash in status command, caused by race (r10144)
* fixed: crashes when deleting a revision-prop (r10148, r10185, r10192)
* fixed: mod_authz_svn allows COPY method on repos with space in name (#1837)
* fixed: mod_authz_svn COPY security hole: authorize whole tree (issue #1949)
Developer-visible changes:
* neon 0.24.7 now required (fixes wire compression bugs) (r10159, 10176)
Version 1.0.5
(10 Jun 2004, from /branches/1.0.5)
http://svn.apache.org/repos/asf/subversion/tags/1.0.5
User-visible changes:
* fixed: security bug in svn protocol string parsing. (CAN-2004-0413)
Version 1.0.4
(21 May 2004, from /branches/1.0.x)
http://svn.apache.org/repos/asf/subversion/tags/1.0.4
User-visible changes:
* fixed: 'svn up' can delete unversioned data on win32 fs (issue #1854)
* fixed: pool leaks in 'svnlook diff/changed/dirs-changed'
* fixed: insecure script example in pre-commit-hook template
* fixed: inability to do a checkout to '/'
* officially recommend neon 0.24.6 in all docs.
Developer-visible changes:
* fixed: RPM build for Fedora & WBEL3/RHEL3
* fixed: SWIG-java building problem
* fixed: javahl bug which can crash JVM
* fixed: change formatting codes in svn_swig_pl_callback_thunk
* fixed: properly wrap svn_txdelta_parse_svndiff for perl
Version 1.0.3
(19 May 2004, from /branches/1.0.3)
http://svn.apache.org/repos/asf/subversion/tags/1.0.3
User-visible changes:
* fixed: security bug in date parsing. (CAN-2004-0397)
Version 1.0.2
(15 April 2004, from /branches/1.0.x)
http://svn.apache.org/repos/asf/subversion/tags/1.0.2
User-visible changes:
* fixed: segfault when remotely deleting svn:author property.
* fixed: mod_dav_svn accepting too many authors. (issue #1786)
* fixed: create runtime config files with native EOLs. (Issue #1802)
* fixed: recursive propset can corrupt .svn/entries (issue #1794)
* fixed: allow shared working copies [mostly working now] (issue #1509)
* fixed: mod_authz_svn should ignore uri on MERGE request (partial #1821)
* fixed: svnserve assertion failure on empty error messages
* fixed: commit/update memory leaks when working on many targets (issue #1635)
* fixed: don't display repos-paths or URLs with '\' on win32.
* new example script: svnserve 'sgid' wrapper.
* minor book fixes, new 'best-practices' doc.
Developer-visible changes:
* fixed: deprecation warning from SWIG 1.3.20_
* fixed: broken win32 python-swig bindings compilation.
* fixed: bug in libsvn_fs changes-table change-folding code.
* fixed: perl bindings: wrap root->paths_changed, apply_txdelta return values
* added VC7 support and defines for including debug symbol files.
Version 1.0.1
(12 March 2004, from /branches/1.0.x)
http://svn.apache.org/repos/asf/subversion/tags/1.0.1
User-visible changes:
* allow anonymous access checking in mod_authz_svn
* fixed: mod_authz_svn now works with SVNParentPath (issue #1588)
* fixed: potential segfault in mod_dav_svn.
* fixed: improper BDB cursor shutdown in libsvn_fs, which can wedge repos.
* fixed: allow checkout of repository with space in path. (issue #1694)
* fixed: make 'svn propget URL' work correctly over svn://. (issue #1752)
* fixed: failed 'svn merge URL' when URL contains user@host. (issue #1759)
* fixed: invalid REPORT response when updating a deleted wc. (issue #1721)
* fixed: allow deletes below copied wc dirs.
* fixed: merge --dry-run bug on added-files with props. (issue #1738)
* fixed: svnlook no longer requires write access to '.'
* fixed: ensure 'svn blame' fails on files marked as binary. (issue #1733)
* fixed: make failed direct-URL commits clean up their fs txns. (issue #1726)
* fixed: obscure bugs in time/date string formatting. (issue #1692)
* fixed: svn export doesn't export svn:externals. (issue #1750)
* fixed: svn import doesn't handle EOL or keyword translation. (issue #1756)
* fixed: svn status -v shows unwanted status of externals (issue #1741)
* fixed: allow revert of schedule-replace file that has no props (issue #1775)
* fixed: svnserve segfault on invalid --listen-host argument.
* fixed: switch bug which caused wrong URL to be left in wc.
* detect invalid UTF8 filenames when native locale is UTF8.
* improve presentation of directory property conflicts.
* improve presentation of errors from svnadmin & svnlook.
* clarify output of 'svnadmin help deltify'.
* augment copyright notice to --version output.
* more book updates.
Developer-visible changes:
* remove obsolete auth provider examples.
* prevent potential ra_dav commit race-condition.
* fix svn_io_dir_walk 'dot-first' ordering required by 'svnadmin hotcopy'.
* fix error leaks in dav_svn_convert_err()
* upgrade win32 innosettup tools and redhat RPMs.
* fix compile warning: compressed streams on LP64 architecture.
* use cpio to generate tarballs instead of GNU tar.
* tweaks to dist.sh.
* fix bindings on win32.
* fix perl bindings build on OS X.
* fix perl bindings: bug which rejects string revnums.
Version 1.0.0
(branching 23 February 2004, from /branches/1.0.x)
http://svn.apache.org/repos/asf/subversion/tags/1.0.0
User-visible changes:
* fixes to the shbang lines in tools/hook-scripts/.
* vast improvements to cvs2svn.py (NOTE: now a separate project!)
* general documentation cleanup:
- clarify built-in help text for 'svn switch' and 'svn status'.
- fix docs within the hook templates.
- cleanups to README, INSTALL, HACKING, svn-ref.tex, bash_completion.
- bring www/ pages up-to-date for 1.0.
- many changes to the Book
Developer-visible changes:
* updates to the win32 installer packaging code.
* cleanups to SWIG bindings:
- disable svn_io_* functions.
- svn_filesize_t and apr_time_t fixes.
- remove debugging print statements and various warnings.
- make svn_repos_dir_delta() function correctly
- add support for repos authz callback.
Version 0.37.0 [Beta Interim 2]
(branching 24 January 2004, from /branches/1.0-stabilization)
http://svn.apache.org/repos/asf/subversion/tags/0.37.0
User-visible changes:
* bugfix: buffer overflow for AIX client
* 'svn merge' now notices ancestry by default. (r8390)
* bugfix: double Ctrl-C on windows no longer wedges repository.
* New date formats (see API change: Rewrite of date parser below)
* bugfix: Errors in authentication when --no-interactive is turned on (r8139)
* bugfix: Fix some 'access denied' errors on Windows (r8341, r8352)
Developer-visible changes:
* API change: Rewrite of date parser (r8327, r8328, r8329) (issue #408)
* bugfix: svn_fs__bdb_changes_fetch() fouls up change ordering (issue #1695)
* require SWIG >=1.3.19 (issue #1690)
* numerous changes to language bindings, to keep up with C API.
* fix: apr build issues (r8279, r8280, r8318) (issue #1666)
* changed the auth-provider C API to use 'realmstring' on all funcs
* check the ra plugin ABI versions.
* fix: ABI problem with blame. (r8494) (issue #1705)
* remove svn_io_file_printf from public API. (r8492) (issue #1653)
* extensive changes in the perl client bindings. (r8270)
* too many big and small internal code cleanups and fixes to mention here
Version 0.36.0 [Beta Interim 1]
(branching 13 January 2004, from /branches/1.0-stabilization)
http://svn.apache.org/repos/asf/subversion/tags/0.36.0
User-visible changes:
* add cancellation support to svnadmin and svnlook (r8222)
* runtime 'store-password' option renamed to 'store-auth-creds' (r8014)
* 'svn blame' changes:
- now shows correct revision info (r8035-6)
- responds to cancellation better (r8129)
* svnserve changes:
- added '--inetd' option; now required to speak with stdin/stdout (r8205)
- added '--listen-port' and '--listen-host' options (r8001-2)
- removed '-u' option (r8003)
- ignore SIGPIPE (no more repos lockups when you terminate a pipe) (r8140)
* lots of Book work (many newly-documented Apache and svnserve topics)
Developer-visible changes:
* bugfix: svnserve network crash (r8142)
* bugfix: return result_rev from svn_client_checkout correctly (r8096)
* bugfix: fs history harvesting code (r8154)
* bugfix: memory leak in mod_dav_svn (r8223)
* bugfixes in edge-cases of status and update (r8114-5)
* make 'svn blame' work with 18n and uri-escaped filenames (r8023, 8030, 8040)
* small bugfixes to authentication system (r8006, r8235)
* standardize error message formatting (r8218)
* load RA modules as foo.so.0, not foo.so (r8098)
* various core API changes:
- use constructor for svn_client_cxt_t (r8053-4)
- anchor/target may use NULL for target (r8216)
- stop using apr_ symbols (r8219)
- rename to 'svn_repos_authz_func_t' (r8213)
- add pool parameter to finish_report and abort_report (r8215)
* numerous changes to Perl and Java bindings, to keep up with C API.
Version 0.35.1 [Beta] (branching 19 December 2003, from /tags/0.35.0)
http://svn.apache.org/repos/asf/subversion/tags/0.35.1
NOTICES:
This release is to correct for the problems in the 0.35.0
release and affects Windows users only:
* fix: file handle leak (r8048)
* fix: UTF-8 path problem (issue #1660)
Version 0.35.0 (branching 12 December 2003, from revision 7994)
http://svn.apache.org/repos/asf/subversion/branches/0.35.0
NOTICES:
1. As of this release, Subversion once again does deltification
automatically. This means that the deltification step most
repositories introduced into their post-commit hooks as of
release 0.33.0 should now be reverted. Look for a line with
"svnadmin deltify" in hooks/post-commit, and remove it.
2. We now recommend using Berkeley DB 4.2.52 or higher for SVN
repositories. See http://sleepycat.com/download/index.shtml.
User-visible changes:
* BDB log files are automatically pruned, with BDB 4.2.50 and higher (#1615)
* deltification is automatic again (issue #1601)
* fix: svn diff -rX:Y wcpath' may lie (issue #1616)
* fix: URI-decoding problem on 'svn import' (issue #1622)
* many other enhancements, minor features, and bugfixes not listed here
Developer-visible changes:
* misc. improvements on Perl and Java bindings
* improved diff handling (r7985)
* many other changes not listed here
Merged revisions after release branching:
* r8009, r8010 and r8011 - Java bindings
* r8041 - typo/bugfix
Version 0.34.0 (released 3 December 2003, from revision r7859)
http://svn.apache.org/repos/asf/subversion/tags/0.34.0
#####################################################################
## WARNING WARNING WARNING WARNING WARNING WARNING WARNING ##
#####################################################################
## ##
## This release makes an incompatible change to the Subversion ##
## database. Repositories created with versions of Subversion ##
## prior to 0.34 will not work with Subversion 0.34. ##
## To upgrade, first use 'svnadmin dump' with your existing ##
## Subversion binaries. Then upgrade your binaries to 0.34, and ##
## use 'svnadmin load' to create a new repository from your ##
## dumpfile. ##
## Don't forget to copy any custom configuration/hooks from the ##
## old to the new repository. ##
## ##
#####################################################################
Please see notes/repos_upgrade_HOWTO for documentation on migrating
pre-0.34.0 repos to 0.34.0.
That document is also located here:
http://svn.apache.org/repos/asf/subversion/trunk/notes/repos_upgrade_HOWTO
User-visible changes:
* fs schema change (#1578, #1595) **NOTE: repos dump/load cycle required!**
* Berkeley DB 4.2.50 is now the recommended Berkeley version
* Fix: 'svn status' thought replaced items were unversioned (#1609)
* SSL server cert error prompt improvement (r7849)
* many error message improvements (r7745, r7763, r7824 and 7827 - #897)
* don't show update-completion message until all wc work completes (#1556)
* many other enhancements, minor features, and bugfixes not listed here
Developer-visible changes:
* public client APIs changes (r7799) after fixing #1556
* many improvements and fixes on Perl bindings (perl => 5.8.0 are required)
* improvements, fixes on misc. test scripts
* many other changes not listed here
Merged revisions after release branching:
* r7868 - Java bindings
* r7888 - Security fix for svnserve
Version 0.33.1 (released 17 November 2003, revision r7782)
http://svn.apache.org/repos/asf/subversion/tags/0.33.1
NOTICE: This is a bugfix release. The bug is fixed if *either*
the client or server uses the new code.
User-visible changes:
* major performance fix for updates
Version 0.33.0 (released 13 November 2003, revision r7737)
http://svn.apache.org/repos/asf/subversion/tags/0.33.0
NOTICES:
1. This client may be incompatible with ra_dav servers <= 0.31.
2. In order to make commits more responsive, repository
deltification is no longer automatic. However, you may want
to run deltification as a background process in your repository
post-commit hook. For example, the new post-commit.tmpl file
recommends 'nice -2 svnadmin deltify "$REPOS" -r "$REV" &'.
User-visible changes:
* now require APR/APU 0.9.5 (ships in Apache 2.0.48)
* lose automatic deltification, but recommend it in post-commit (r7695, #1573)
* new configuration and authn/authz support in ra_svn (r7604, r7601)
* much faster checkouts and updates, over both svn:// and http:// (#1429)
* new partial-authz feature: checkouts/updates just skip unauthorized items
* new 'use-commit-times = yes' config option to use commit-time timestamps
* new 'svnadmin hotcopy' command, like hot-backup.py (#1567)
* fix Win32 "access denied" error in renames (r7598, #1576)
* unnecessary working copy tree locks now avoided, to save time (#1245)
* Compatibility changes:
- lose ra_dav compatibility with servers 0.31 and earlier
- lose support for working copy format "1" (not created for over a year)
* 'svn diff' and other read-only actions now work in read-only working copies
* 'svn blame -rX' now does the intuitive thing
* 'svn log' output headers now say "rXXXX | " instead of "rev XXXX: "
* 'svnversion' no longer stymied by svn:externals
* new 'svn pd' alias for 'svn propdel'
* '-rCOMMITTED' keyword now works on more commands
* minor changes to output of 'svn ls -v' and 'svn st -v' (r7530)
* 'svn log --xml' now obeys the '-q' flag (r7555)
* cvs2svn.py bugfixes, especially issue #1440
* book and documentation updates
* removed server config options ssl-ignore-invalid-date and
ssl-override-cert-hostname (r7644)
* many other enhancements, minor features, and bugfixes not listed here
Developer-visible changes:
* repair text- and prop-time in .svn/entries if spuriously wrong (r7565)
* speed up keyword translation (r7502)
* two new editor functions, absent_file() and absent_directory()
* ra_dav checkouts/updates no longer do O(n) number of GET, PROPFIND requests
* new svn_io_temp_dir function, will morph to apr_temp_dir_get soon
* new svn_io_file_close wrapper for apr_file_close
* tools/test-scripts/svntest/ scripts now support ra_dav and ramdisk
* many other changes not listed here
Version 0.32.1 (released 23 October 2003, revision 7497)
http://svn.apache.org/repos/asf/subversion/tags/0.32.1
NOTICE: This release is to correct for the problems in the 0.32.0
release. There are no user or developer changes in this release
other than the subversion/include/svn_version.h now reflects
the correct version number.
NOTICE: This release of Subversion causes an ra_dav client/server
compatibility break with Subversions older than 0.28.0.
Version 0.32.0 (released 22 October 2003, revision 7480)
http://svn.apache.org/repos/asf/subversion/tags/0.32.0
NOTICE: This release of Subversion causes an ra_dav client/server
compatibility break with Subversions older than 0.28.0.
User-visible changes:
* new 'svn blame' subcommand. (r7389, 7438, #508)
* fix huge ra_dav 'svn import' memory leak. (r7381)
* other bugfixes: proper line endings in diff headers (r7450, #1533),
stop auto-props from removing all whitespace (r7358), 'svn st' UI
consistency fix (r7364), various 'svn switch' fixes (r7366),
mini-manpages for svnadmin, svnserve, svnversion (r7421), remove
'P' field from 'svn ls -v' (r7432), 'svn merge' double-notification
bug (r7447), prevent 'svn:externals' infinite loop (r7459), 'svn
merge' segfault (r7458).
Developer-visible changes:
* 'svn diff' is now reasonably streamy. (r7393, 7439, #1481)
* fix many ra_dav pool abuses. (r7370-3, 7380, 7368, 7400, ...)
* fix mini leaks: clear unused svn_error_t's. (r7378-9, 7405, 7408, 7429)
* tons of code, doc, API cleanup. (from julianfoad!)
* new RA->get_repos_root() API. (r7428)
* swig/python, swig/perl and native JNI updates and improvements.
* more work on build depenedency generator. (r7412-8)
* svn_repos_finish_report() now aborts txns on error. (r7424)
* remove crufty old ra_dav compatibility code (r7466, 7468)
* other changes: new SVN_DEBUG_ERROR tool macro, new 'davautocheck'
and 'contrib' makefile targets, new --enable-gprof configure option
(r7437), new scramble-tree.py testing tool, auth provider
reorganization, make RA->get_dir fetch props correctly over ra_dav
(r7431), notice permission error when creating unique tmpfile (r7434).
Version 0.31.0 (released 08 October 2003, revision 7355)
http://svn.apache.org/repos/asf/subversion/tags/0.31.0
User-visible changes:
* new 'svnlook history' command (and removal of 'svnadmin lscr').
* new 'auto-props' feature can set file properties during 'svn add/import'
* win32 client now properly converts UTF8 to console-locale. (r7168, #872)
* 'svn up' now notices when svn:externals value changes. (r7256, #1519)
* authentication changes:
- client caches auth-creds in memory for a single session (r7292, #1526)
- SSL cert caches keyed on host+port, not any SSL connection. (r7174)
* faster 'svn log' (see new fs-history algorithm) (#1499)
* faster repos read-operations (caching gives ~20% speedup) (rXXXX, #1499)
* faster updates (fewer entries-file writes gives ~20% speedup) (r7170, #1490)
* more work on psvn.el and svn_load_dirs.pl
* more cvs2svn bugfixes
* obsolete manpages truncated to point to 'help' and book URLs. (r7340, #1508)
* other bugfixes: no more revision keywords "FIRST" or "CHANGED" (r7250),
fix 'svn cp URL URL' $EDITOR msg generation (r7264), fix regression
bug in 'svnadmin load' (r7273), 'svnadmin setlog' now triggers
repository hooks (r7322), 'svn cp -rHEAD wc' now works correctly (r7331),
post-commit-hook failures correctly ignored by client (r7342, #906)
Developer-visible changes:
* tons of filesystem improvements (#1499):
- new fast fs-history algorithm: allows stable VR urls (r7283, #1499)
- new dag-node caching (r7163)
- skip-deltas now run in individual trails (r7138)
- no-op svn_fs_copy()s don't write to the database (r7158)
* mod_dav_svn MERGE response is faster (using svn_repos_replay()) (r7191)
* ensure consistent wc 'dead entry' cleanup (r7197, r7204, #1075)
* lots of work on gen_win.py, gen_make.py, gen_base.py tools
* lots of work on making SWIG-java bindings build.
* updates/improvements to javahl bindings and SWIG-perl bindings
* updates/improvements to Mandrake RPM builds
* other bugfixes: python testsuite now uses local path separators (r7224),
svn:externals no longer keeps connections open (r7312, #1448),
UTF8-to-local date conversion (r7316, #1534), API consistification
changes (r7298, r7302, r7304, r7307).
Version 0.30.0 (released 24 Sep 2003, revision 7178)
http://svn.apache.org/repos/asf/subversion/tags/0.30.0
User-visible changes:
* SSL changes: (r7134, #1330)
- client now prompts to cache server certificates
- no more 'ssl-ignore-unknown-ca' option
- 'ssl-ignore-host-mismatch' is renamed to 'ssl-override-cert-hostname'
- new 'ssl-trust-default-ca' option to trust 'default' openssl CAs
* 'svn log' no longer dies on unversioned args (r6989, #777)
* local mods now obstruct 'svn up' deletions (r7050, #1196)
* 'svnserve' now notices (unauthenticated) --username arg (r7060)
* no more 'svnadmin createtxn' subcommand. (r7062)
* 'svn ls -v' shows years when appropriate
* document some new things in Book (r7014), plus minor technical fixes
* website changes: new sidebar, new 'svn links' page, new tigris.org!!
* other bugfixes: hooks use proper stdout handles on win32 (r7001),
prevent copies of copies in wc (r7077, #1259), display failed
revprop change over ra_dav (r7081, #1520), 'svn st -u' throws RA
error properly (r7094, #1506)
Developer-visible changes:
* ra_dav now requires neon-0.24.X
* many gen_make.py/gen-base.py improvements, especially for win32 builds
* many improvements to swig/perl bindings
* improvements to contrib/: psvn.el, and new svn-push program.
* more cvs2svn bugfixes: issue #1504, #1421, #1514, and new --username arg.
* python testsuite only raises exceptions, never status codes. (#1192)
* various libsvn_fs re-org (prepwork) for issue #1499.
* other bugfixes: code-complete timestamp feature (r6983, #1445), add
op-counting features to trails (r6984, #655), fs UUID caching
(r7037), almost finish win32 iconv issues (#872), restored-file
entry-timestamp bugfix (r7090, #1523), always print CWD as '.' (r7097)
Version 0.29.0 (released 05 Sep 2003, revision 6976)
http://svn.apache.org/repos/asf/subversion/tags/0.29.0
User-visible changes:
* 'svn status' now streams its response. (r6913, #1426)
* 'svn status' now recurses into externals (r6913, #1428)
* new 'svnadmin verify' command to verify repository data (r6851, #1074)
* SSL changes: (r6958, #1371)
- dropped support for PEM-encoded client certs, only accept PKCS12 now.
- 'ssl-authority-files' is now a list of CA files
- no more 'ssl-client-cert-type' and 'ssl-client-key-file' variables.
* new svndumpfilter option: '--preserve-revprops' to keep props on empty revs
* mailer.py improvement: handle multiple match groups (r6940)
* remove in-repos/on-disk repository template features, till post-1.0 (r6965)
* various cleanups to the Book
* other bugfixes: switch deletion bug (r6890, #1496), status
repos-delete bug (r6913, #1469), reversion of '.' (r6953, #854).
Developer-visible changes:
* GUI developers take note: prompting API changed (r6928, #1214)
* now compile against neon-0.24; 0.23.9 support to be dropped soon. (r6958)
* various improvements to Perl/SWIG bindings
* tree re-org: non-core utilities split into 'tools' and 'contrib' areas.
* some gen_make.py/gen-base.py improvements
* configure.in CFLAGS bugfix (r6963)
* stop calling deprecated APIs in APR, in preparation for upcoming APR-1.0.
Version 0.28.2 (released 29 Aug 2003, revision 6946)
http://svn.apache.org/repos/asf/subversion/tags/0.28.2
User-visible changes:
* MAJOR BUGFIX: revert revision 6764.
The new history-searching code was over-stressing our use
of BerkeleyDB transactions, causing checkouts to go
twice as slow and lose all concurrent-client scalability.
This is a temporary fix for a larger design problem. See issue
http://subversion.tigris.org/issues/show_bug.cgi?id=1499
Version 0.28.1
(released 28 Aug 2003,http://svn.apache.org/repos/asf/subversion/tags/0.28.1)
There are no changes in this release.
It is strictly an updated release, build with the correct version
of autoconf; autconf-2.57
Version 0.28.0 (released 27 August 2003, rev 6894, branches/release-0.28)
(http://svn.apache.org/repos/asf/subversion/tags/0.28)
#####################################################################
## WARNING WARNING WARNING WARNING WARNING WARNING WARNING ##
#####################################################################
## ##
## This release makes an incompatible change to the Subversion ##
## repository filesystem schema. Repositories created with ##
## Subversion code prior to this release will unable to operate ##
## with this new code. To maintain the ability to use said ##
## repositories, you must use a version 'svnadmin dump' prior to ##
## this change to dump your repository to a dumpfile, then use ##
## this new Subversion code to create, and load your dumpfile ##
## a new repository using 'svnadmin load'. And don't forget to ##
## copy over any custom configuration/hooks from the old to the ##
## new repository. ##
## ##
#####################################################################
Please see notes/repos_upgrade_HOWTO for documentation on migrating
pre-0.28.0 repos to 0.28.0.
That document is also located here:
http://svn.apache.org/repos/asf/subversion/trunk/notes/repos_upgrade_HOWTO
User-visible changes:
* fs schema change, see issue #1003 **NOTE: repos dump/load cycle required!**
* command-line options
- changed 'lsdblogs' is now 2 commands 'list-dblogs', 'list-unused-dblogs'
- removed '--only-unread' option
- new 'list-unused-dblogs'
- new '--config-dir' allows svn config to live outside ~/.subversion
- new (r6811)
- svn-status-get-specific-revision (interface to svn cat)
- svn-ediff-with-revision (run ediff w/ a file wc and a specified rev)
* fixed 'mod_dav_svn' segfault bug caused by foreign DeltaV requests (r6725)
* fixed 'svn switch' bug which could result in corrupted repo (#1316,r6746)
* fixed items now marked as 'deleted' if they no longer exist (#919,r6748)
* fixed 'merge' no longer adds file/dir if scheme differs from wc. (#1321)
* fixed Handle \r correctly when prompting on Windows. (r6792,#1307)
* 'svn merge' now 'skip's when it hits unversioned obstructions (r6810,#1425)
* fixed repos->wc of file w/ svn:keywords set caused segfault (r6818,#1473)
* fixed 'svn diff -r PREV:HEAD' failed if tmp/ exists in cwd (r6838,#1487)
Developer-visible changes:
* database schema changed (see warning above!) (r6752,#1003,#1248,#1438)
* svn Perl bindings are ready - see swig/INSTALL
* internal changes to treat swig libraries more like normal libraries (r6761)
* improved handling of errors opening a repository over ra_svn (r6841)
Version 0.27.0 (released 12 August 2003, rev 6707, branches/release-0.27.0)
User-visible changes:
* fixed ra_svn:
- (r6588) avoid hangs due to ra_svn tunnel creation errors
- (r6696, r6697, #1465) svnserve crash due to pre-commit hook failure
* fixed 'svn log':
- (r6642, #1423) log on a deleted path over ra-dav
- (r6684, #1438) log performance bug
* fixed 'svn diff' and 'svn merge':
- (r6604, #1311) diff URL URL on files now works
- (r6668, #1142) diff comparing wc to repos branch
- (r6687, #1297) diff/merge interaction in file adds
- (r6703, #1319) merge problem with adding subtrees
- (#6607) new default ancestry-following behavior for diff, merge
* fixed 'svn status':
- (r6688, r6691, r6692, #1289) status on nodes deleted in repos
- (r6637) status now always uses "I" for ignored directories
* fixed 'svn copy':
- (r6704, #1313) copy between 2 repositories errors cleanly now
- (r6649, #1444) seg fault when copying empty dir from repos to wc
* fixed 'svn export':
- (r6652, #1461) exporting an empty directory
- (r6664, #1296) path->path exports
* fixed - gracefully handle failure to get uid on Win98 (r6695, #1470)
* fixed - avoid spurious conflicts when merging binary files (r6621, #1319)
* fixed - merge of a single file into implicit '.' (r6630, #1150)
* fixed - various Win32 innosetup improvements/fixes (r6693, r6656, #1133)
* fixed - disallow ".." in svn:externals target paths (r6639, #1449)
* fixed - use 'env' instead of hard-coded paths in scripts (r6626, #1413)
* fixed - bug in loading incremental dumpfiles (r6595)
* fixed - performance issue in svn_load_dirs.pl fixed (r6623, r6627, #1455)
* fixed - handle IPv6 addresses in URLs (r6638, r6654, #1338)
* changed - 'svn resolve' renamed to 'svn resolved' (r6597)
* changed - 'svnlook tree' takes new optional path-in-repos argument (r6583)
* changed - renamed 'svnadmin archive' to 'svnadmin lsdblogs'; offer
either all logs, or just the unused ones (r6661)
* changed - now offer full-text search in Windows documentation file (r6658)
* changed - much documentation updated, especially the book
* Many other fixes and changes, too numerous to mention individually.
Developer-visible changes:
* fixed - many improvements to Perl and Python bindings, including some
Win32-specific improvements (r6619, r6686, r6590, r6580, r6579)
* All other important dev changes are implied by the user-visible changes.
Version 0.26.0 (released 24 July 2003, revision 6550, branches/release-0.26.0)
User-visible changes:
* fixed - --parent-dir option to svnadmin load (r6436)
* fixed - 'svnlook diff' now properly displays diffs (r6408, #1241)
* fixed - 'svn cat' no longer expands values from the wrong revision.
* fixed 'svn merge':
- (r6447,#1402) -r FOO:PREV works correctly
- (r6452,#1379) no longer prints confusing no-op lines
- (r6500/6503,#1399) warn user when a tree-delta chunk can't be applied
* turn compression off to work around to mod_deflate timeouts (r6509)
Developer-visible changes:
* cvs2svn.py: lots of bugfixing related to branch/tag support
* diff code refactored to allow use by other tools (r6407)
* make 'svn export' set commit-timestamps (not ready yet) (r6420)
* fixed - memory leaks in libsvn_ra_dav commits (r6422)
* fixed - cvs2svn.py handles branch files rooted in dead revs (r6482,#1417)
* fixed - new lines now detected in svn:author property (r6497,#1401)
* fixed - svn_load_dirs works w/ absolute paths (r6507, Debian bug #187331)
* changed - build infrastructure now supports Perl SWIG bindings (r6441)
* removed - PORTING document no longer necessary (r6472)
Version 0.25.0 (released 11 July 2003, revision 6394, branches/release-0.25.0)
User-visible changes:
* command line options:
- new --force option for svn export (r6327,#1296)
- new --force-log for commit, copy, delete, import, mkdir, move (r6294)
- no longer need --force for commit
* commands
- new - svnadmin archive (r6310)
- changed - svn import syntax now 'svn import [PATH] URL' (r6288,#933,#735)
- fixed - Search PATH for external diff commands (r6373)
- fixed - 'svn switch' memory bug (r6296)
- fixed - 'svn mkdir' coredump (r6388,#1369)
* python bindings now in -tools rpm for Mandrake 9.1 (r6374)
* allow parent-into-child copies, provided they are not WC->WC. (r6348,#1367)
* fixed - Apache module installation order (r6382-6,#1381)
* now require apache 2.0.47 (and apr 0.9.4)
* fix 2 commit leaks
* fix mod_dav_svn path-escaping bug
Developer-visible changes:
* Win32 build system
- new - .vcproj files for svn_config project and APR (r6311)
- fixed - SWIG bindings for Win32 (r6304)
- vcproj generator now works (r6316)
- swig's generated .c files now dependent on headers in .i files (r6379)
- refactored code common to dsp & vcproj into gen_win.py (r6328)
* fixed
- SEGFAULTs in SWIG bindings (r6339)
- potential SEGFAULTs in 'REPORT vcc' backward-compatibility code (r6377)
- mod_dav_svn's autoversioning failure on PUT (r6312)
- 'svn switch' memory bug (r6296)
* changed - mailer.py now uses svn_repos_replay()
Version 0.24.2 (released 18 June 2003, revision 6284, branches/release-0.24.2)
User-visible changes:
* fix 'svn export' potential segfault
* fix occasional diff test failures
* fix 'svnadmin dump' memory hog
* fix new-dir-with-spaces bug
Developer-visible changes:
* none
Version 0.24.1 (released 16 June 2003, revision 6249, branches/release-0.24.1)
User-visible changes:
* Fix bug in 'svn log'.
Developer-visible changes:
* none
Version 0.24.0 (released 15 June 2003, revision 6234, branches/release-0.24.0)
User-visible changes:
* new 'svn diff [--old OLD] [--new NEW]' syntax (#1142)
* new --relocate option for svn switch (#951)
* new --version option for svnadmin and svnlook
* new path-based authorization module for apache
* make 'svn checkout' and not just 'svn update' resume a checkout
* .svn directories now hidden on Windows
* config variable 'store-password = no' now actually works
* fix 'svn merge --dry-run'
* fix 'properties hanging after a rename' issue (#1303)
* fix odd behavior of 'svn up -r PREV filename' (#1304)
* fix delete-tracking bug (#1348)
* fix dump and load corner cases (#1290)
* ra_dav server more resilient for foreign deltaV clients
* numerous ra_svn changes - must update clients and servers
* fix export over ra_svn (#1318)
* fix ra_svn error transmission bug (#1146)
* fix ra_svn corruption in tunnel mode (#1145)
* make svnserve multi-threaded on non-fork platforms (now works on Windows)
* remove svnserve -F and -S options
* various memory use improvements (#1323)
* various performance improvements for all protocols
* various performance improvements for 'svnadmin dump' and svnlook
* various subversion book updates (you have read the book right?)
* more cvs2svn.py improvements (and more to follow)
* new debugging script normalize-dump.py to normalize dump output
Developer-visible changes:
* path-based editor drivers
* no more RA->do_checkout()
* update python and java bindings
* various windows build fixes
Version 0.23.0 (released 16 May 2003, revision 5962, branches/release-0.23.0)
User-visible changes:
* 'svn cat' now performs keyword expansion (#399)
* 'svn export' keyword expansion fixed
* checkouts are now restartable (#730)
* ssh ra_svn tunnel agent specified with svn+ssh://hostname/path syntax.
* remove dependency on external diff program
* don't error out early on unversioned files (#774)
* fix commands where REPORT fails if item isn't in HEAD (#891)
* updates now receive checksums like checkouts (#1101)
* 'svn revert dir' now resets property timestamp (#1163)
* fix instances of client showing help instead of error message (#1265)
* fix incorrect path in 'not a working copy' error messages (#1291)
* fix cvs2svn.py file added on branch problem (#1302)
* fix various vc-svn.el problems (#1257, #1268)
* fix various psvn problems (#1270)
* various Win32 build fixes
Developer-visible changes:
* fix various gcc 3.3 warnings (#1031)
* fix various memory errors/leaks
* remove java/jni bindings
Version 0.22.2 (released 13 May 2003, revision 5918, branches/release-0.22.2)
User-visible changes:
* fix Win32 build
* properly handle on-disk template errors
* fix bogus uuid bug in cvs2svn.py
Developer-visible changes:
* none
Version 0.22.1 (released 9 May 2003, revision 5874, branches/release-0.22.1)
User-visible changes:
* fix shared library installation problem
* update cvs2svn.py script
Developer-visible changes:
* none
Version 0.22.0 (released 7 May 2003, revision 5842, branches/release-0.22)
User-visible changes:
* svn diff -r BASE:HEAD and other edge cases fixed (#977)
* svn diff and merge now have --ignore-ancestry option (#1034)
* svn ci -N DIR no longer errors during post-commit (#1239)
* ra_dav now optional (#617)
* update vn-svn.el (#1250, #1253)
* improvements to svn_load_dirs.pl (#1223, #1215)
* misc ra_svn bug fixes and protocol change
* log-encoding option now properly only applied to logs
* fix mmap failures on HP-UX
* fix some client memory leaks
Developer-visible changes:
* finish transition to new xml prop namespaces for mod_dav_svn (#840)
* minimize full tree locks and number of system calls (#1245)
* auto-generated .dsp files (#850)
* fix ETag of directory (#1251)
* added export editor (#1230)
Version 0.21.0 (released 15 Apr 2003, revision 5639, branches/release-0.21)
User-visible changes:
* SSL client and server certificate verification
* authentication info now stored in ~/.subversion/auth/
* svn diff on a copied file only shows local mods, not the whole file
* svn propget now takes a --strict option to control output
* svnadmin load now takes a --parent-dir option
* added the new 'svndumpfilter' program
* svnlook now has 'cat', 'propget', and 'proplist' commands to enable
viewing this information on transactions
* 'svn copy' from another repository now adds without history
* tag/branch conversion disabled in cvs2svn until it gets fixed
* the 'anonymous' user is no longer used; we simply avoid attaching an
author property when an author is not available
* improvements to ignored-file handling
* Python ConfigParser-style variable expansion for config file (#1165)
Developer-visible changes:
* introduced the svn_filesize_t type (#639)
* realmstring added to the svn_auth framework
* the "result checksum" moved to the editor.close_file function
* more checksumming here and there
* initial work to enable binary properties via ra_dav
* initial, internal support for compressed streams
* test framework shifting to exception-based failure recording (#1193)
* improved options and handling in the C test framework
* java and python binding work
* libsvn_auth folded into libsvn_subr
* bug fixes: 'svnadmin load' parse bug; ra_svn crashes (#1160); 'svn
log' on a switched wc (#1108); 'svn ci -N' on named files (#1195)
Version 0.20.1 (released 26 Mar 2003, revision 5467, branches/release-0.20.1)
User-visible changes:
* fix svnadmin load bug so that property deletions actually occur
* fix checksum compatibility issue for older repositories
Developer-visible changes:
* none
Version 0.20.0 (released 20 Mar 2003, revision 5410, branches/release-0.20)
User-visible changes:
* new compatibility rule: require only that each interim release be
compatible with the one before it (see HACKING)
* ra_svn is still new so above rule doesn't yet apply
(i.e. 0.20 over ra_svn is NOT compatible with previous releases)
* merge infers the target path (see book chapters 4 & 8)
* merge continues in presence of missing target file
* merge's add notifications are no longer duplicated
* commands can be safely interrupted (Ctrl-C)
* --encoding global default in ~/.subversion/config
* new option --editor-cmd
* begin multi-release transition to escape binary properties over DAV
* misc performance improvements
Developer-visible changes:
* RA vtable functions take pool argument
* svn-config --includes path fixed
* uuid at creation now complete
* start having test failures throw exceptions rather than return errors
* test suite option --cleanup with --verbose being default mode
* continued diff library development
* minor revprop hook changes
* bug fixes: no diff on binary files (#1019), consistent error messages
(#1181), version numbers in hook scripts (#1182), win98 codepage (#1186)
Version 0.19.1 (released 12 Mar 2003, revision 5303, branches/release-0.19.1)
User-visible changes:
* fix svnserve tunnel mode pipe close bug
Developer-visible changes:
* none
Version 0.19.0 (released 10 Mar 2003, revision 5262, branches/release-0.19)
User-visible changes:
* svn ls works on wc paths (#1130)
* new cvs2svn.py features and bug fixes (1105)
* new svnlook subcommand 'uuid'
* new svnadmin create option '--bdb-txn-nosync' (use with care)
* fix svnserve help output
* SVN_EDITOR now overrides svn-editor in ~/.subversion/config
* miscellaneous performance improvements (memory and speed)
* more work on the Book
Developer-visible changes:
* start implementing cancellation of long-running functions
* misc windows build fixes and features (DSP generator)
* -W and -P options to stress.pl
* start adding support for multiple fs backends
* work on bindings and bindings build system (#1132, #1149)
* bug fixes: ra_dav import/checkout memory usage (#995), control chars
in commit messages (#1025), svn merge memory usage (#1069, #1077),
pre-existing ~/.subversion (#1121), keyword expansion (#1151), line
number in config error message (#1157), svn-tunnel-agent in [default]
(#1158), RA->close RIP (#1164), config-test non-source (#1172)
Version 0.18.1 (released 26 Feb 2003, revision 5118, branches/release-0.18.1)
User-visible changes:
* editor environment variables no longer incorrectly required
* 'svn help import' now displays correct usage
* fix crashes in the internal diff library and ra_dav
* fix Win9x/Me console issue
* cvs2svn.py api fix
* hot_backup.py now correctly removes old backups
Developer-visible changes:
* various rpm package fixes
Version 0.18.0 (released 19 Feb 2003, revision 4968, branches/release-0.18)
User-visible changes:
* renamed the [default] section to [global] in the servers config file
* compression option is now http-compression and lives in servers file
* use internal diff by default rather than external program (#405 in progress)
* symlinked hook scripts now run
* read-only access flag (-R) for svnserve
* quiet flag (--quiet) for svnadmin dump
* --ignore-uuid and --force-uuid for svnadmin load
* miscellaneous performance improvements
* more work on the Book
Developer-visible changes:
* new authentication library libsvn_auth (#724)
* new bdb table uuids
* client context object in libsvn_client
* more work on java and other language bindings
* test framework now has a quiet option (-q)
* miscellaneous small code cleanups
* bug fixes: more valgrind memory bugs, apr xlate i18n mess (#872),
non-existent URL checkout (#946), props on to-be-deleted files (#1066),
ra_svn move/copy (#1084), eol translation (#1085), ra_svn
checksumming (#1099), cat command corrupt output (#1104), cvs2svn
memory consumption (#1107), merge of property add (#1109),
'..' relative path (#1111), commit/cleanup/diff3 (#1119),
.svn/entries checksum (#1120), svn commit in / (#1122),
status on uncontrolled directory (#1124), commit message eol
characters (#1126), cat -r PREV (#1134), ra_dav wcprops (#1136)
split XML cdata/attribute encoding (#1118)
Version 0.17.1 (released 22 Jan 2003, revision 4503, branches/0.17.1)
User-visible changes:
* changed non-baseline build version number display.
* compatibility change: make sure old clients can talk to newest servers.
* some changes to the Book
Developer-visible changes:
* dumper/loader now use checksums (#1102)
* miscellaneous small code cleanups
* bug fixes: eol-style timestamp changes (#1086), valgrind mem bug,
better checksum error reporting,
Version 0.17.0 (released 20 Jan 2003, revision 4468, branches/0.17)
User-visible changes:
* 'svn add' is now recursive by default, -N to disable (#1073)
* new 'svnversion' program summarizes mixed-revs of a working copy
* huge improvements to the mailer.py tool
* more work on the Book and man page
* default global-ignores now built-in, new runtime-config file commented out
Developer-visible changes:
* checksums, checksums everywhere (issues #649, #689):
- filesystem stores them, and verifies them when reading/writing
- working copy stores them, and verifies them when reading/writing
- checksums transferred both ways over network, RA layers verify them
* finish draft of internal diff/diff3 library -- ready for testing/optimizing
* more utf8<->apr conversion work (#872)
* more work on swig/python and ruby bindings
* improvements to win32-innosetup package
* 'svnserve' now has an official IANA-assigned portnumber.
* mod_dav_svn now only sends/understands new xml prop namespaces (#840)
* bug fixes: stop needless fs rep data copies (#1067), wc auth
caching bugs (#1064), use APR_BUFFERED to open files (#1071), lots
of wc 'missing dir' edge-case bugs (#962), prevent wc from
receiving '.svn' (#1068), don't commit symlinks (#1081), better
diff labels (#1080), better fulltext tmpfile names in conflicts (#1079),
prevent ra_dav from deleting out-of-date items (#1017), segfault (#1092),
don't attempt checksum on missing tmp textbase (#1091), allow diffs
during update again (yikes!)
Version 0.16.1 (released 6 Jan 2003, revision 4276)
User-visible changes:
* ra_svn network layer (apache alternative) now tested & ssh-tunnelable
* new (experimental) mod_dav_svn autoversioning feature (SVNAutoversioning)
* reorganization of the ~/.subversion/ run-time config files.
* more entry caching: approx. 3x speedup on checkouts & updates.
* option rename: --non-recursive instead of --nonrecursive
* option rename: --no-diff-deleted instead of --no-diff-on-delete
* new 'svn log --quiet'
* new 'svn diff --no-diff-deleted'
* fix keyword expansion behaviors ($keyword:$ / $keyword$ / $keyword: $)
* handle win32 non-ascii config-file paths (#968, #1048, part of #872)
Developer-visible changes:
* most public header files now using doxygen markup
* new (untested) internal difflib (#405)
* neon debugging now tweakable via run-time config file
* more progress on Subversion Book rewrite.
* new ./configure --with-diffutils
* begin work on client/server checksums (#649)
* regression tests now depend on svnadmin dump/load
* lose src_err field of svn_error_t
* many fs function renames: begins fs database back-end abstraction.
* new libsvn_repos prop-validating wrappers
* lots of work on build-system dependency graph generation (for SWIG building)
* swig binding work:
- python svn calls can now run as independent threads
- new java-binding build system
- improved swig building features: --prefix, LDFLAGS behaviors
* many, many bug fixes: wc->repos copies (#1029), #943 followup
(#1023), copies of copies (#830), 'svn resolve' cleans up entries
file (#1021), prop merging (#1012), segfault fixes (#1027, #1055),
autoconf 2.5X (#886), O(1) copies (#717), new 'failed revert'
signal (#714), detect missing schedule-add conflicts (#899, #863),
begin dav namespace switchover (#840), status bugs, url auth
inference (#1038), log bug (#1028), newline prompt (#1039),
svnadmin errorchecking, url syntax errors (#1057, #1058), apr/utf8
work (start #872), and many more.
Version 0.16 (released 4 Dec 2002, revision 3987)
User-visible changes:
* new 'svn cat' subcommand
* new --revprop flag to access revision props, -r for versioned props (#943)
* new "compression" runtime option in ~/.subversion/config
* svnadmin/svnlook now use help system, and some subcommands deleted or moved.
* tool changes:
- new svnshell.py tool
- new mirror_dir_through_svn.cgi script
- new svn_load_dirs.pl features
- updates to vc-svn.el
* --message-encoding is now just --encoding, and affects svn: propvals too.
* major rewrites of chapters 3, 4, 5 of the Subversion Book.
Developer-visible changes:
* new network layer, libsvn_ra_svn! still experimental.
* all svn_error_t's now allocated in subpool of global pool.
* reorganize svnlook/svnadmin subcommands & option-parsing (#540, #915, #910)
* all log messages and svn: props now stored as UTF8/LF endings (#896)
* huge cleanup/reorg of all svn_path_* routines
* svn_client_status sends feedback, distinguishes unversioned vs. ignored
* improvements to swig typemappings and build processes
* fixes to pool cleanup handlers
* begin abstraction of gen_make.py
* entry-caching improvements
* stop using global apr_xlate objects
* win32-innosetup code added to packages/
* new work on ruby bindings and swig-java bindings
* many, many bug fixes: various small coredumps, svn_error_t leaks,
copy props correctly (#976), copy executable bits correctly (#982),
test-system fix (#882), accidentally imported tmpfile (#964),
ra_local checkout memleak (#985), accidental wc deletion (#988),
better text vs. binary detection (#994), dav log-report error
handling, bad 'svn switch' dav caching (#1000), don't call NULL
callbacks (#1005), bogus switch feedback (#1007), eol-style file
corruption (#920), getdate.y fix (#1009), ra_local error reporting (#900),
start of work on issues #830 and #869.
Version 0.15 (released 7 Nov 2002, revision 3687)
User-visible changes:
* New 'S' indicator in 'svn status' shows switched subdirs
* New --dry-run option added for 'svn merge' (issue #953)
* Fix 'svn update .' to handle svn:externals correctly
* Memory usage of 'svn import' reduced (issue #860)
* Allow 'svn revert' on missing directories scheduled for deletion
* Assorted bug fixes in several exciting flavors
* Documentation improvements
Developer-visible changes:
* #911 (apr and apr-util version at build time)
* Fixed issues #851, #894,
* Testing scripts accept --url=URL and BASE_URL=URL
* Issue #881 (--enable-all-static)
* Delta editors all converted to new-style, and editor composition is gone
* Improve libsvn_wc wcprop handling (issue #806)
* SWIG binding improvements
* Various pool usage improvements
Version 0.14.5 [Alpha Interim 5] (released 30 Oct 2002, revision 3578)
User-visible changes:
* allow --incremental option for 'svn log' xml output
Developer-visible changes:
* autoconf bugfix for berkeley-db detection
* clean up property interface mess (part of #806)
* dish.sh bugfix: build the new docbook docs correctly
* python tests now log commands
* gen-make.py now assumes 'build.conf'
Version 0.14.4 [Alpha Interim 4] (released 29 Oct 2002, revision 3553)
User-visible changes:
* new working-copy entry-caching: speeds many ops up to 5x (#749)
* new 'svnadmin recover', instead of db_recover
* client can now view & change server-side revision props (e.g. log messages)
* new --non-interactive switch for commandline client
* new --incremental option to 'svn log'
* new -r {date} syntax for specifying dated revs; works over network too.
* automatically set svn:executable prop when adding or importing (#870)
* initial $EDITOR text now ignores all log data below special token
* consistify behavior of text & prop columns in 'svn status' output.
* .svn/auth/* files now chmod 700, to stop scaring people. :-)
* improved labels in 'svn diff' output (#936)
* run-time adjustable neon timeout in newly renamed 'servers' config file
* big improvements to cvs2svn script: bugfixes and basic branch/tag support
* new python access-control hook script
* no more implicit dot-target for 'svn propedit' or 'svn propset' (#924)
* Win32 improvements:
- use system-wide config-file/registry
- run-time configurable diff/diff3 binary locations (#668)
* remove obsolete --xml-file support
* Handbook is now ported to Docbook, 2 new chapters.
Developer-visible changes:
* abstracted option/help-parsing code, now shared between svn and svnadmin
* require apache 2.0.42
* use neon 0.23.5: fix XML entity derefs, SSL server certs, HP-UX build, etc.
* support Berkeley DB 4.0 *or* 4.1
* many SWIG binding improvements:
- better overall coverage of apr and libsvn_* library symbols
- new 'make swig-py-ext' and 'make install-swig-py-ext' targets
* finish conversion of all editor/drivers to "new" style (#737)
* removed xml-delta editors and editor drivers and related tests
* new predicate-logic system added to automated-test system ("skip" support)
* more work on mailer.py
* no more lost commit messages (#761)
* eradication of misused stringbufs, obsolete code removal (#909)
* mem-leak fixes in libsvn_fs (#860)
* improved atomicity of working-file translations (#914)
* improve ./configure --help output (#949)
* MANY bugfixes, especially for entry-locks (#931, #932, #847, #938),
merges (#880, ), auth storage (#934); also #921 (svnadmin
segfault), #907 (xml quoting), #918 (post-commit processing), #935
(path canonicalization), #779 (diff errors)
Version 0.14.3 [Alpha Interim 3] (released 20 Sept 2002, revision 3200)
User-visible changes:
* new ~/.subversion/config file
* new $Id$ keyword
* new client --no-auth-cache option
* empty values in the Windows Registry are no longer ignored (issue #671)
* report details of repository start-commit or pre-commit hook errors
* fix locking behaviour when using current directory as a target
* updated man page
* new front-page logo. :-)
Developer-visible changes:
* continuing work on python SWIG bindings
* continuing work on new access-baton system for libsvn_wc
* upgrade to neon 0.23.4 to fix Windows build issues and seg faults
* add XFAIL to the C testing framework
* prevent setting of certain svn: props on incorrect file types
* cleanup libsvn_subr's path library behavior
* new 'fast-clean' vs. 'clean' Makefile targets
* various bugfixes, tweaks, cleanups.
Version 0.14.2 [Alpha Interim 2] (released 22 Aug 2002, revision 3033)
User-visible changes:
* fs schema change, see issue #842. **NOTE: repos dump/load cycle required!**
* new 'svn ls -R' option
* new status code `~', for type changes
* add --username and --password options to 'svn ls'
* new script tools/client-side/svn_all_diffs.pl
* new script tools/examples/blame.py (draft)
Developer-visible changes:
* test suite now does XFAIL and XPASS
* test suite over DAV now uses SVNParentPath, no longer depends on symlinks
* DAV tests now work on Windows
* upgrade to neon 0.22.0
* 'make install' notices the $(DESTDIR) parameter
* new dav prop namespaces, but old still sent for compat; see issue #840
* error code space reorganized, see issue #702
* many cleanups to path handling
* more use of access batons in libsvn_wc, see issue #749
* working props now stored with ".svn-work" extension, see issue #618
* the usual round of bug fixes, new regression tests, etc
Version 0.14.1 [Alpha Interim 1] (released 9 August 2002, revision 2927)
User-visible changes:
* show copy-ancestry in 'svn log -v'
* 'svn co' can take multiple URLs now
* new 'svn ls' command
* new 'svn st --no-ignore' option
* new 'svn --version --quiet' option
* more conservative 'svn help' usage error-message
* more graceful degradation from charset conversion failure
* standardize policy of -q switch behavior
* less intimidating error output
* new SVNParentPath directive for mod_dav_svn <Location>s
* svnlook now correctly displays copied subtrees
* Handbook: additions, tweaks, cleanups, and new French Translation :-)
* svn_load_dirs.pl: auto propset on files matching specified regex, bug fixes
Developer-visible changes:
* integrated the delta-combiner! (issue #531)
* integration of libsvn_wc-baton-locking branch (issue #749)
* new "skip-deltas" added to delta-combiner
* properly URI-encode/decode path components throughout our code
* RA->do_diff() made independent from RA->do_switch().
* stricter setting/parsing of svn:mime-type property in client and server.
* new 'install-static' make target
* extend SWIG bindings to libsvn_wc and libsvn_client
* BerkeleyDB usage tweaking: in preparation for auto-recovery features.
* work on #850 (.dsp generator)
* Better support for incremental dumps (see revision 2920)
* started fs branch work on #842 (copyID inheritance), #830 (copies of
copies), #790 (copy table uses txnID), #815 (custom sorting)
* numerous bugfixes: #709 (better error handling), #813/814
(apr_filepath_merge), #685 (showing dir propdiffs), OS X dumper
bugfix, #561 (property conflict detection), mod_dav_svn path bugs,
svn_wc_status() bugs, path canonicalization bugs, #816 (svn log -r),
#843 (URL keyword), #846 (kind-change replacement), #809 ($EDITOR dir),
#855 (module updates not cooperating with new wc access batons),
improvements to test suite sensitivity,
Version 0.14.0 [Alpha] (released 23 July 2002, revision 2667)
User-visible changes:
* finally some documentation: The Subversion Handbook
* i18n support for paths, prop names, and log messages; (not on Win32 yet)
* support for URI-escaped paths
* "-R" is now short for --recursive, and "-N" replaces "-n"
* add the -R option to 'svn info' and 'svn resolve'
* new syntax for 'svn switch' and 'svn co'
* new 'svn-config' file installed
* new commit-access-control.pl utility (feature #775)
* new vc-svn.el, first pass at Emacs VC support for Subversion
* lots of work on svn_load_dirs.pl (provides vendor-branch-like features)
* new --message-encoding option for logfiles given by -F
* support win32 drive-letters in file:/// urls
* improved date output syntax: ISO-8601 prefix, then human-friendly suffix
* the usual round of bug fixes
Developer-visible changes:
* UTF-8 changes
- all libraries now assume UTF-8 input paths and log msgs
- many apr calls are now abstracted into new svn_io_* wrappers
* fs schema change
- cache each revision's changed-paths in a new 'changes' table
- another repository dump/load is required
* a number of fs-dumper bugfixes and redesigns
* test suite is now all python, so it can run on win32
* reduce huge memory consumption of mod_dav_svn during checkouts
* memory optimizations for prop-reading and 'svn diff'
* bugfixes for commit-email.pl and tweak-log.cgi
* lots of branch work on the delta-combiner and on libsvn_wc rewrite
* numerous bugfixes: 'svn merge .' bug (#748), bug #764, two new
ghudson-dirversioning bugs, #756, #675, #783, #796, wc-root bugs,
#799, #800, #797, directory-removal bugs (#611, #687)
Version 0.13.2 [Pre-Alpha] (released 28 June 2002, revision 2376)
User-visible changes:
* fixed various buggy commandline outputs
* allow global/local config-files on win32
* prevent overwrites with 'svn cp URL URL'
* improvements to svn_load_dirs.pl
* mod_dav_svn can generate xml output for directory GETs
* new svnadmin(1) man page
Developer-visible changes:
* finished notification callback system, no more buggy output
* fs-changes:
- revisions table nothing but an index to txns table
- branch work-in-progress: new 'changes' table to store changed paths
* more work on svn_time_* funcs and formats (moving towards ISO8601)
* property reversion bugs fixed, dumper bug fixed
* add version number to svndiff database storage
* new regression tests for 'svn merge'
* fix 'svn diff -rX:Y' server bug
* fix bugs in python test system
* bring win32 build up-to-date, get most python tests working on win32
Version 0.13.1 [Pre-Alpha] (released 20 June 2002, revision 2291)
User-visible changes:
* "modules" are now implemented
* new 'svn export' command
* 'svn log' now traverses copy history and can print changed paths
* 'svn merge' now (temporarily) only merges into '.'
* 'svnadmin lscr' now traverses copy history
* changes to the 'svn:executable' prop take effect immediately now
* server is more tolerant of wc's with old-style version resource URLs
* new Handbook started
* commit-email.pl fixes/improvements -- now shows prop mods and copy history
* bug fixes to cp, rm, merge, revert, admin dump and load, svnlook
Developer-visible changes:
* headers now install in subdir and libs are named libsvn_FOO-1.so
* improvements to the Python test suite
* delta combiner implemented (unused for now, though)
* Python SWIG binding improvements: ability to write an editor in Python
* new example: tools/examples/svnlook.py
* start moving libsvn_client to new notification system (no composed editors!)
* upgrade to neon 0.21.2, fixing deflated communication with apache
* Moved Berkeley-specific code to libsvn_fs/bdb/, skels into libsvn_fs/util/
* changes to the RPM packaging
Version 0.13.0 [Pre-Alpha] (released 10 June 2002, revision 2140)
User-visible changes:
* repositories have a new database schema; existing ones must be upgraded!
- new svnadmin 'dump'/'load' commands to migrate repositories
- read http://svn.apache.org/repos/asf/subversion/trunk/notes/repos_upgrade_HOWTO
Developer-visible changes:
* complete rewrite of filesystem schema!
- skels are abstracted away, opening the door to SQL backends
- node-ids now have copy IDs
* huge progress on module system [only checkouts work at the moment]
* massive conversion of stringbufs to char* in our public APIs
* vsn-rsc-urls are now based on created-rev/path instead of fs_id_t's.
* reinstate 'deleted' flag on entries, to ensure accurate update reports
* dir_delta learns how to send copy history
- svnlook no longer sends 10MB emails when we make a branch
- dumpfiles get much smaller
* memory consumption reduced via new apr-pool code that reuses/frees mem
* client can now parse ISO-8601 timestamps (start of issue 614)
* added script for stress-testing concurrent repository access
* auto-locate apache's apr libraries at build-time
* beginnings of ra_pipe library
* progress on delta combiner code
* many memleaks fixed, thanks to valgrind!
* upgrade to newest neon, allow deflated communication with apache
* many bugfixes to merge, switch, checkout, rm; tackling of issues 704,
705, 698, 711, 713, 721, 718 and many others
Version 0.12.0 (released 3 May 2002, revision 1868)
User-visible changes:
* 'svn diff' can now compare two arbitrary URLs
* 'svn diff' now displays property changes
* 'svn rm' requires --force for unversioned and/or modified items
* 'svn rm' immediately removes files & uncommitted dirs
* 'svn mv' for WC->WC behaves like 'svn rm' with respect to the source
* checkouts, updates, switches now print received revision on final line.
* new 'svn info' command prints information about a versioned resource.
* switch to 2-part conflict markers (diff3 -E) instead of 3-part (diff3 -A)
* new bash programmable completion file
* file's executable bit can be versioned (svn:executable prop)
* commits and imports now support --nonrecursive option
* new --xml option for 'svn log'
* new 'svnadmin dump' command
Developer-visible changes:
* updates correctly deal with disjoint urls.
* libsvn_wc now checksums text-bases, to detect working copy corruption
* cached wcprops (vsn-rsc-urls) now auto-regenerate if invalid
* python testsuite now runs on Win32.
* new switch_tests.py added to testsuite
* NEW internalized diff/diff3 library. Not yet integrated/tested.
* dir_delta sends entry props; pipe-editor removed.
* no more expat/ tree; use apr-util's expat instead.
* fs deltificaton happens outside commit process, using fewer db locks
* privatize svn_fs_id_t structure
* start abstracting skels out of libsvn_fs
* new docs: secure coding tips, quickref card
* memory bugfixes for import/commit/mass removals
* many bugfixes: issues 644, 646, 691, 693, 694, 543, 684
Version 0.11.1 (released 12 April 2002, revision 1692, branches/0.11.0)
User-visible changes:
* completion of 'svn merge' (issue 504)
* added SVNReposName directive to mod_dav_svn
* insist on a diff binary that supports "-u"
* fix and unify pop-up $EDITOR behaviors (issues 638, 633, 615)
Developer-visible changes:
* finish rewrite of commit system to handle disjoint urls (issue 575)
* finish proxy support via config files (esp. on win32) (issue 579)
* fix svn_ra_dav__get_baseline_info and related bugs (issue 581)
* reorganization of libsvn_wc header files & API
* new getopt_tests.py to test commandline option processing
* 'make check' now more portable -- tests invoked via python, not sh
* miscellaneous bugfixes in imports, svndiff, db linkage.
Version 0.11.0 (unreleased)
Version 0.10.2 (released 25 Mar 2002, revision 1587)
User-visible changes:
* new ~/.subversion configuration directory
* proxy support via ~/.subversion/proxies file
Developer-visible changes:
* rewrite of client-side commit process partially done
* beginnings of 'svn merge'
* mod_dav_svn now generates "streamy" report responses
* stringbuf cleanups and bugfixes
* interface to svn_wc_entry_t cleaned up
* tweaks to build system and freebsd port
* miscellaneous bugfixes in path escaping, pool usage, hp-ux compilation
Version 0.10.1 (released 17 Mar 2002, revision 1537)
User-visible changes:
* New --targets command-line option for some commands.
* conflicts now create conflict-markers in files, and 3 fulltext backups.
* new 'svn resolve' command removes conflicted state (by removing backups)
Developer-visible changes:
* no more dependency on 'patch'; only on GNU diff3 and some version of 'diff'
* complete rewrite of svn_wc_entry_t interface
* begin abstracting svn_fs API by hiding implementation details
* consolidate RA layer callbacks
* start work on commit-driver rewrite
* start work on ~/.subversion/ configuration directory, and proxy support
* move a lot of svn_wc.h into private wc.h
* bugfixes relating to commits, network prop xfers, 'svn log', 'svn co -q'
* major deletion bug fixed
(see email WARNING:
http://subversion.tigris.org/servlets/ReadMsg?msgId=64442&listName=dev)
Version 0.10.0 (released 08 Mar 2002, revision 1467)
User-visible changes:
* fewer out-of-memory errors: (see "memory consumption" below)
* clearer user errors:
- detailed marshalling of server errors to client
- better errors from ra_dav
- better commandline-client-specific error messages
* 'svn log' now works on single paths correctly
* show locked directories in 'svn status'
* 'svnadmin lstxns' improvements, and new --long switch
* commits show "Replacing" instead of "Deleting/Adding" (#571)
* commits show progress on postfix txdeltas.
* WARNING: existing repositories need to be upgraded;
read tools/enable-dupkeys.sh.
Developer-visible changes:
* reduced memory consumption
- new Editor interface that manages pools automatically
- conversion of most existing editors to new system
- have libsvn_fs write data to DB streamily
- reduce DB logfile growth via 'duplicate keys'
- stop using one pool for post-commit processing
- stop using one pool for sending all textdeltas
- many, many other pool-usage improvements in libsvn_wc, ra_dav, etc.
* start of work on 'svn merge": issue 504, and diff3 integration
* start of work on disjoint-url detection: issue 575
* start removing stringbuf path library funcs; use new const char * funcs
* better python 2.X detection in test suite
* svnlook uses single tempdir
* build system evolution
- upgrade to neon 0.19.[2-3]
- lots of work on FreeBSD port
* many small bugfixes:
- propedit, file merges, revert, dir_delta, keywords
- memory leaks in 'svn add', 'svn import/commit', and svnlook
- date-parsing and readonly bugs
Version 0.9 (released 15 Feb 2002, revision 1302)
User-visible changes:
* 'svn switch', for switching part of a working copy to a branch
* 'svn status -v' now shows created-rev and last-author info
* 'svn help <subcommand>' now shows proper switches
* if no log message passed to commit, $EDITOR pops up
* greatly improved/re-organized README, INSTALL, and HACKING docs
* big progress on cvs2svn repository converter
* faster retrieval of old revisions: turn off fs directory deltification
* fixed broken behaviors in 'svn diff' and 'svn log'
Developer-visible changes:
* new fs code for detecting differences and relatedness
* new cancellation editor, for event-driven users of libsvn_client
* make .svn/ area readonly
* continued development of ruby, java, and python (swig) bindings
* new config-file parser
* code reorganization and cleanup
- huge conversion of svn_stringbuf_t --> char *
- standardized on commit_info return structure
- no more 'path styles' in path library
- rewrite bootstrapping code for python test framework
- rewrite commandline app's help-system and alias-system
- feedback table replaced with notfication callback
- rewrite sorting of hashes
- svnadmin internal rewrite
- faster post-update processing
- using SVN_ERR macros where they weren't
- new svn_client_revision_t mechanism
- txdelta windows are readonly now
- pool debugging code moved to APR
- various pool-usage fixes
* build system evolution
- apr-util now required
- upgrade to neon 0.18.5
- much apr m4 macro churn
- win32 updates, no longer needs precompiled neon
- 'make check' when builddir != srcdir
* fixes for many issues, including #624, 627, 580, 598, 591,
607. 609, 590, 565
[Versions 0.8 and older are only brief summaries]
Version 0.8 (released 15 Jan 2002, revision 909)
* newline conversion and keyword substitution (#524)
* rewrite ra_local commit system to commit against HEAD (#463)
* mod_dav_svn sends svndiffs now (#518)
* code migration from libsvn_fs to libsvn_repos (#428)
Version 0.7 (released 03 Dec 2001, revision 587)
* 'svn cp/mv' completed:
- can copy from wc/repos to wc/repos
- This how we create branches/tags
* 'svn mkdir' [WC_PATH|REPOS_URL]
* 'svn delete' [REPOS_URL]
Version 0.6 (released 12 Nov 2001, revision 444)
* 'svn log'
* 'svn cp/mv' from wc to wc
Milestones M4/M5 (released 19 Oct 2001, revision 271)
* network layer bugfixes
* filesystem deltification
Milestone M3 (released 30 Aug 2001, revision 1)
* self-hosting begins, all history left behind in CVS repository.
Milestone M2 (released 15 May 2001, from CVS, "milestone-2" tag)
* filesystem library (libsvn_fs)
* network layer (libsvn_ra_dav and mod_dav_svn)
Milestone M1 (released 20 Oct 2000, from CVS, "milestone-1" tag)
* working-copy library (libsvn_wc), using XML files
Birth (05 June 2000)
* CVS repository created.
Index: vendor/subversion/dist/Makefile.in
===================================================================
--- vendor/subversion/dist/Makefile.in (revision 286500)
+++ vendor/subversion/dist/Makefile.in (revision 286501)
@@ -1,924 +1,928 @@
#
# Makefile.in: template Makefile for Subversion
#
# ====================================================================
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ====================================================================
#
top_builddir = .
top_srcdir = @top_srcdir@
abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
VPATH = @top_srcdir@
SVN_RA_LIB_DEPS = @SVN_RA_LIB_DEPS@
SVN_RA_LIB_INSTALL_DEPS = @SVN_RA_LIB_INSTALL_DEPS@
SVN_RA_LIB_LINK = @SVN_RA_LIB_LINK@
SVN_FS_LIB_DEPS = @SVN_FS_LIB_DEPS@
SVN_FS_LIB_INSTALL_DEPS = @SVN_FS_LIB_INSTALL_DEPS@
SVN_FS_LIB_LINK = @SVN_FS_LIB_LINK@
SWIG_SRC_DIR = $(abs_srcdir)/subversion/bindings/swig
SWIG_BUILD_DIR = $(abs_builddir)/subversion/bindings/swig
SCHEMA_DIR = subversion/svn/schema
SVN_APR_LIBS = @SVN_APR_LIBS@
SVN_APRUTIL_LIBS = @SVN_APRUTIL_LIBS@
SVN_APR_MEMCACHE_LIBS = @SVN_APR_MEMCACHE_LIBS@
SVN_DB_LIBS = @SVN_DB_LIBS@
SVN_GPG_AGENT_LIBS = @SVN_GPG_AGENT_LIBS@
SVN_GNOME_KEYRING_LIBS = @SVN_GNOME_KEYRING_LIBS@
SVN_KWALLET_LIBS = @SVN_KWALLET_LIBS@
SVN_MAGIC_LIBS = @SVN_MAGIC_LIBS@
SVN_SASL_LIBS = @SVN_SASL_LIBS@
SVN_SERF_LIBS = @SVN_SERF_LIBS@
SVN_SQLITE_LIBS = @SVN_SQLITE_LIBS@
SVN_XML_LIBS = @SVN_XML_LIBS@
SVN_ZLIB_LIBS = @SVN_ZLIB_LIBS@
LIBS = @LIBS@
prefix = @prefix@
exec_prefix = @exec_prefix@
libdir = @libdir@
fsmod_libdir = @libdir@
ramod_libdir = @libdir@
bdb_libdir = @libdir@
gnome_keyring_libdir = @libdir@
gpg_agent_libdir = @libdir@
kwallet_libdir = @libdir@
serf_libdir = @libdir@
bindir = @bindir@
includedir = @includedir@
mandir = @mandir@
srcdir = @srcdir@
canonicalized_srcdir = @canonicalized_srcdir@
datadir = @datadir@
datarootdir = @datarootdir@
localedir = @localedir@
# where to install libsvn_swig_*
swig_py_libdir = @libdir@
swig_pl_libdir = @libdir@
swig_rb_libdir = @libdir@
### these possibly need further discussion
swig_pydir = @libdir@/svn-python/libsvn
swig_pydir_extra = @libdir@/svn-python/svn
swig_pldir = @libdir@/svn-perl
swig_rbdir = $(SWIG_RB_SITE_ARCH_DIR)/svn/ext
toolsdir = @bindir@/svn-tools
javahl_javadir = @libdir@/svn-javahl
javahl_javahdir = @libdir@/svn-javahl/include
javahl_libdir = @libdir@
javahl_test_rootdir=$(abs_builddir)/subversion/bindings/javahl/test-work
javahl_test_srcdir=$(abs_srcdir)/subversion/bindings/javahl
gnome_auth_dir = $(abs_builddir)/subversion/libsvn_auth_gnome_keyring/.libs
kwallet_auth_dir = $(abs_builddir)/subversion/libsvn_auth_kwallet/.libs
auth_plugin_dirs = $(gnome_auth_dir):$(kwallet_auth_dir)
MSGFMT = @MSGFMT@
MSGFMTFLAGS = @MSGFMTFLAGS@
MSGMERGE = @MSGMERGE@
XGETTEXT = @XGETTEXT@
TRANG = @TRANG@
PACKAGE_NAME=@PACKAGE_NAME@
PACKAGE_VERSION=@PACKAGE_VERSION@
CC = @CC@
CXX = @CXX@
CPP = @CPP@
EXEEXT = @EXEEXT@
SHELL = @SHELL@
LIBTOOL = @SVN_LIBTOOL@
LTFLAGS = --tag=CC --silent
LTCXXFLAGS = --tag=CXX --silent
LT_CFLAGS = @LT_CFLAGS@
LT_LDFLAGS = @LT_LDFLAGS@
LT_SO_VERSION = @SVN_LT_SOVERSION@
LT_NO_UNDEFINED = @LT_NO_UNDEFINED@
LT_CXX_LIBADD = @LT_CXX_LIBADD@
INCLUDES = -I$(top_srcdir)/subversion/include -I$(top_builddir)/subversion \
@SVN_APR_INCLUDES@ @SVN_APRUTIL_INCLUDES@ @SVN_APR_MEMCACHE_INCLUDES@ \
@SVN_DB_INCLUDES@ @SVN_GNOME_KEYRING_INCLUDES@ \
@SVN_KWALLET_INCLUDES@ @SVN_MAGIC_INCLUDES@ \
@SVN_SASL_INCLUDES@ @SVN_SERF_INCLUDES@ @SVN_SQLITE_INCLUDES@ \
@SVN_XML_INCLUDES@ @SVN_ZLIB_INCLUDES@
APACHE_INCLUDES = @APACHE_INCLUDES@
APACHE_LIBEXECDIR = $(DESTDIR)@APACHE_LIBEXECDIR@
APACHE_LDFLAGS = @APACHE_LDFLAGS@
SWIG = @SWIG@
SWIG_PY_INCLUDES = @SWIG_PY_INCLUDES@ -I$(SWIG_SRC_DIR)/python/libsvn_swig_py
SWIG_PY_COMPILE = @SWIG_PY_COMPILE@
SWIG_PY_LINK = @SWIG_PY_LINK@
SWIG_PY_LIBS = @SWIG_PY_LIBS@
SWIG_PL_INCLUDES = @SWIG_PL_INCLUDES@
SWIG_RB_INCLUDES = @SWIG_RB_INCLUDES@ -I$(SWIG_SRC_DIR)/ruby/libsvn_swig_ruby
SWIG_RB_COMPILE = @SWIG_RB_COMPILE@
SWIG_RB_LINK = @SWIG_RB_LINK@
SWIG_RB_LIBS = @SWIG_RB_LIBS@
SWIG_RB_SITE_LIB_DIR = @SWIG_RB_SITE_LIB_DIR@
SWIG_RB_SITE_ARCH_DIR = @SWIG_RB_SITE_ARCH_DIR@
SWIG_RB_TEST_VERBOSE = @SWIG_RB_TEST_VERBOSE@
SWIG_RB_RI_DATADIR = $(DESTDIR)$(datadir)/ri/$(RUBY_MAJOR).$(RUBY_MINOR)/site
CTYPESGEN = @CTYPESGEN@
CTYPES_PYTHON_SRC_DIR = $(abs_srcdir)/subversion/bindings/ctypes-python
JAVAHL_JAR=subversion/bindings/javahl/svn-javahl.jar
JAVAHL_INCLUDES= @JNI_INCLUDES@ -I$(abs_builddir)/subversion/bindings/javahl/include
CXXHL_INCLUDES = -I$(abs_srcdir)/subversion/bindings/cxxhl/include
SVN_APR_CONFIG = @SVN_APR_CONFIG@
SVN_APR_INCLUDES = @SVN_APR_INCLUDES@
SVN_APRUTIL_CONFIG = @SVN_APRUTIL_CONFIG@
SVN_APRUTIL_INCLUDES = @SVN_APRUTIL_INCLUDES@
MKDIR = @MKDIR@
DOXYGEN = @DOXYGEN@
# The EXTRA_ parameters can be used to pass extra flags at 'make' time.
CFLAGS = @CFLAGS@ $(EXTRA_CFLAGS)
CMODEFLAGS = @CMODEFLAGS@
CMAINTAINERFLAGS = @CMAINTAINERFLAGS@
CXXFLAGS = @CXXFLAGS@ $(EXTRA_CXXFLAGS)
CXXMODEFLAGS = @CXXMODEFLAGS@
CXXMAINTAINERFLAGS = @CXXMAINTAINERFLAGS@
### A few of the CFLAGS (e.g. -Wmissing-prototypes, -Wstrict-prototypes,
### -Wmissing-declarations) are not valid for C++, and should be somehow
### suppressed (but they may come from httpd or APR).
CPPFLAGS = @CPPFLAGS@ $(EXTRA_CPPFLAGS)
LDFLAGS = @LDFLAGS@ $(EXTRA_LDFLAGS)
SWIG_LDFLAGS = @SWIG_LDFLAGS@ $(EXTRA_SWIG_LDFLAGS)
SWIG_CPPFLAGS = @SWIG_CPPFLAGS@ $(EXTRA_CPPFLAGS)
COMPILE = $(CC) $(CMODEFLAGS) $(CPPFLAGS) $(CMAINTAINERFLAGS) $(CFLAGS) $(INCLUDES)
COMPILE_CXX = $(CXX) $(CXXMODEFLAGS) $(CPPFLAGS) $(CXXMAINTAINERFLAGS) $(CXXFLAGS) $(INCLUDES)
LT_COMPILE = $(LIBTOOL) $(LTFLAGS) --mode=compile $(COMPILE) $(LT_CFLAGS)
LT_COMPILE_CXX = $(LIBTOOL) $(LTCXXFLAGS) --mode=compile $(COMPILE_CXX) $(LT_CFLAGS)
# Execute a command that loads libraries from the build dir
LT_EXECUTE = $(LIBTOOL) $(LTFLAGS) --mode=execute `for f in $(abs_builddir)/subversion/*/*.la; do echo -dlopen $$f; done`
# special compilation for files destined for mod_dav_svn
COMPILE_APACHE_MOD = $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) $(CMODEFLAGS) $(CPPFLAGS) $(CFLAGS) $(CMAINTAINERFLAGS) $(LT_CFLAGS) $(APACHE_INCLUDES) $(INCLUDES) -o $@ -c
# special compilation for files destined for libsvn_swig_* (e.g. swigutil_*.c)
COMPILE_SWIG_PY = $(LIBTOOL) $(LTFLAGS) --mode=compile $(SWIG_PY_COMPILE) $(CPPFLAGS) $(LT_CFLAGS) -DSWIGPYTHON $(SWIG_PY_INCLUDES) $(INCLUDES) -o $@ -c
COMPILE_SWIG_PL = $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) $(CPPFLAGS) $(CFLAGS) $(LT_CFLAGS) $(SWIG_PL_INCLUDES) $(INCLUDES) -o $@ -c
COMPILE_SWIG_RB = $(LIBTOOL) $(LTFLAGS) --mode=compile $(SWIG_RB_COMPILE) $(CPPFLAGS) $(LT_CFLAGS) $(SWIG_RB_INCLUDES) $(INCLUDES) -o $@ -c
# special compilation for files destined for javahl (i.e. C++)
COMPILE_JAVAHL_CXX = $(LIBTOOL) $(LTCXXFLAGS) --mode=compile $(COMPILE_CXX) $(LT_CFLAGS) $(JAVAHL_INCLUDES) -o $@ -c
COMPILE_JAVAHL_JAVAC = $(JAVAC) $(JAVAC_FLAGS)
COMPILE_JAVAHL_JAVAH = $(JAVAH)
# export an env variable so that the tests can run without being installed
TEST_SHLIB_VAR_JAVAHL=\
if [ "@SVN_APR_SHLIB_PATH_VAR@" = "DYLD_LIBRARY_PATH" ]; then \
for d in $(abs_builddir)/subversion/libsvn_*; do \
if [ -n "$$DYLD_LIBRARY_PATH" ]; then \
@SVN_APR_SHLIB_PATH_VAR@="$$@SVN_APR_SHLIB_PATH_VAR@:$$d/.libs"; \
else \
@SVN_APR_SHLIB_PATH_VAR@="$$d/.libs"; \
fi; \
done; \
export @SVN_APR_SHLIB_PATH_VAR@; \
fi;
# special compilation for files destined for cxxhl
COMPILE_CXXHL_CXX = $(LIBTOOL) $(LTCXXFLAGS) --mode=compile $(COMPILE_CXX) $(LT_CFLAGS) $(CXXHL_INCLUDES) -o $@ -c
LINK = $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) $(LT_LDFLAGS) $(CFLAGS) $(LDFLAGS) -rpath $(libdir)
LINK_LIB = $(LINK) $(LT_SO_VERSION)
LINK_CXX = $(LIBTOOL) $(LTCXXFLAGS) --mode=link $(CXX) $(LT_LDFLAGS) $(CXXFLAGS) $(LDFLAGS) -rpath $(libdir)
LINK_CXX_LIB = $(LINK_CXX) $(LT_SO_VERSION)
# special link rule for mod_dav_svn
LINK_APACHE_MOD = $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) $(LT_LDFLAGS) $(CFLAGS) $(LDFLAGS) -rpath $(APACHE_LIBEXECDIR) -avoid-version -module $(APACHE_LDFLAGS)
# Special LDFLAGS for some libraries
libsvn_auth_gnome_keyring_LDFLAGS = @libsvn_auth_gnome_keyring_LDFLAGS@
libsvn_auth_kwallet_LDFLAGS = @libsvn_auth_kwallet_LDFLAGS@
libsvn_client_LDFLAGS = @libsvn_client_LDFLAGS@
libsvn_delta_LDFLAGS = @libsvn_delta_LDFLAGS@
libsvn_diff_LDFLAGS = @libsvn_diff_LDFLAGS@
libsvn_fs_LDFLAGS = @libsvn_fs_LDFLAGS@
libsvn_fs_base_LDFLAGS = @libsvn_fs_base_LDFLAGS@
libsvn_fs_fs_LDFLAGS = @libsvn_fs_fs_LDFLAGS@
libsvn_fs_util_LDFLAGS = @libsvn_fs_util_LDFLAGS@
libsvn_ra_LDFLAGS = @libsvn_ra_LDFLAGS@
libsvn_ra_local_LDFLAGS = @libsvn_ra_local_LDFLAGS@
libsvn_ra_serf_LDFLAGS = @libsvn_ra_serf_LDFLAGS@
libsvn_ra_svn_LDFLAGS = @libsvn_ra_svn_LDFLAGS@
libsvn_repos_LDFLAGS = @libsvn_repos_LDFLAGS@
libsvn_subr_LDFLAGS = @libsvn_subr_LDFLAGS@
libsvn_wc_LDFLAGS = @libsvn_wc_LDFLAGS@
# Compilation of SWIG-generated C source code
COMPILE_PY_WRAPPER = $(LIBTOOL) $(LTFLAGS) --mode=compile $(SWIG_PY_COMPILE) $(LT_CFLAGS) $(CPPFLAGS) $(SWIG_PY_INCLUDES) -prefer-pic -c -o $@
COMPILE_RB_WRAPPER = $(LIBTOOL) $(LTFLAGS) --mode=compile $(SWIG_RB_COMPILE) $(LT_CFLAGS) $(CPPFLAGS) $(SWIG_RB_INCLUDES) -prefer-pic -c -o $@
# these commands link the wrapper objects into an extension library/module
LINK_PY_WRAPPER = $(LIBTOOL) $(LTFLAGS) --mode=link $(SWIG_PY_LINK) $(SWIG_LDFLAGS) -rpath $(swig_pydir) -avoid-version -module
LINK_RB_WRAPPER = $(LIBTOOL) $(LTFLAGS) --mode=link $(SWIG_RB_LINK) $(SWIG_LDFLAGS) -rpath $(swig_rbdir) -avoid-version -module
LINK_JAVAHL_CXX = $(LIBTOOL) $(LTCXXFLAGS) --mode=link $(CXX) $(LT_LDFLAGS) $(CXXFLAGS) $(LDFLAGS) $(LT_CXX_LIBADD) -rpath $(libdir)
INSTALL = @INSTALL@
INSTALL_LIB = $(LIBTOOL) --mode=install $(INSTALL)
INSTALL_FSMOD_LIB = $(INSTALL_LIB)
INSTALL_RAMOD_LIB = $(INSTALL_LIB)
INSTALL_APR_MEMCACHE_LIB = $(INSTALL_LIB)
INSTALL_BDB_LIB = $(INSTALL_LIB)
INSTALL_GPG_AGENT_LIB = $(INSTALL_LIB)
INSTALL_GNOME_KEYRING_LIB = $(INSTALL_LIB)
INSTALL_KWALLET_LIB = $(INSTALL_LIB)
INSTALL_SERF_LIB = $(INSTALL_LIB)
INSTALL_BIN = $(LIBTOOL) --mode=install $(INSTALL)
INSTALL_CONTRIB = $(LIBTOOL) --mode=install $(INSTALL)
INSTALL_TOOLS = $(LIBTOOL) --mode=install $(INSTALL)
INSTALL_INCLUDE = $(INSTALL) -m 644
INSTALL_MOD_SHARED = @APXS@ -i -S LIBEXECDIR="$(APACHE_LIBEXECDIR)" @MOD_ACTIVATION@
INSTALL_DATA = $(INSTALL) -m 644
INSTALL_LOCALE = $(INSTALL_DATA)
INSTALL_APACHE_MODS = @INSTALL_APACHE_MODS@
### this isn't correct yet
INSTALL_SWIG_PY = $(INSTALL_LIB)
INSTALL_SWIG_PY_LIB = $(INSTALL_LIB)
INSTALL_SWIG_PL_LIB = $(INSTALL_LIB)
INSTALL_SWIG_RB = $(INSTALL_LIB)
INSTALL_SWIG_RB_LIB = $(INSTALL_LIB)
INSTALL_JAVAHL_LIB = $(INSTALL_LIB)
# additional installation rules for the SWIG wrappers
INSTALL_EXTRA_SWIG_PY=\
$(MKDIR) $(DESTDIR)$(swig_pydir); \
$(MKDIR) $(DESTDIR)$(swig_pydir_extra); \
for i in $(abs_srcdir)/subversion/bindings/swig/python/svn/*.py; do \
$(INSTALL_DATA) "$$i" $(DESTDIR)$(swig_pydir_extra); \
done; \
for i in $(abs_srcdir)/subversion/bindings/swig/python/*.py; do \
$(INSTALL_DATA) "$$i" $(DESTDIR)$(swig_pydir); \
done; \
if test "$(abs_srcdir)" != "$(abs_builddir)"; then \
for i in $(abs_builddir)/subversion/bindings/swig/python/*.py; do \
$(INSTALL_DATA) "$$i" $(DESTDIR)$(swig_pydir); \
done; \
fi; \
$(PYTHON) -c 'import compileall; \
compileall.compile_dir("$(DESTDIR)$(swig_pydir)", 1, "$(swig_pydir)"); \
compileall.compile_dir("$(DESTDIR)$(swig_pydir_extra)", 1, \
"$(swig_pydir_extra)");'
# export an env variable so that the tests can run without being installed
TEST_SHLIB_VAR_SWIG_PY=\
if [ "@SVN_APR_SHLIB_PATH_VAR@" = "DYLD_LIBRARY_PATH" ]; then \
for d in $(SWIG_PY_DIR)/libsvn_swig_py $(SWIG_PY_DIR)/../../../libsvn_*; do \
if [ -n "$$DYLD_LIBRARY_PATH" ]; then \
@SVN_APR_SHLIB_PATH_VAR@="$$@SVN_APR_SHLIB_PATH_VAR@:$$d/.libs"; \
else \
@SVN_APR_SHLIB_PATH_VAR@="$$d/.libs"; \
fi; \
done; \
export @SVN_APR_SHLIB_PATH_VAR@; \
fi;
# The path to generated and complementary source files for the SWIG
# bindings.
SWIG_PL_DIR = $(abs_builddir)/subversion/bindings/swig/perl
SWIG_PY_DIR = $(abs_builddir)/subversion/bindings/swig/python
SWIG_RB_DIR = $(abs_builddir)/subversion/bindings/swig/ruby
# The path to the source files for the SWIG bindings
SWIG_PL_SRC_DIR = $(abs_srcdir)/subversion/bindings/swig/perl
SWIG_PY_SRC_DIR = $(abs_srcdir)/subversion/bindings/swig/python
SWIG_RB_SRC_DIR = $(abs_srcdir)/subversion/bindings/swig/ruby
### Automate JAR creation using Makefile generator's javahl-java.jar
### property. Enhance generator to support JAR installation.
JAVAHL_MANIFEST_IN = $(abs_srcdir)/subversion/bindings/javahl/Manifest.in
JAVAHL_MANIFEST = subversion/bindings/javahl/Manifest
INSTALL_EXTRA_JAVAHL_JAVA=\
sed s/%bundleVersion/$(PACKAGE_VERSION)/ $(JAVAHL_MANIFEST_IN) > $(JAVAHL_MANIFEST); \
$(JAR) cfm $(JAVAHL_JAR) $(JAVAHL_MANIFEST) -C subversion/bindings/javahl/classes org; \
$(INSTALL_DATA) $(JAVAHL_JAR) $(DESTDIR)$(javahl_javadir);
INSTALL_EXTRA_JAVAHL_LIB=@INSTALL_EXTRA_JAVAHL_LIB@
INSTALL_EXTRA_SWIG_RB=\
@echo $(MKDIR) $(DESTDIR)$(SWIG_RB_SITE_LIB_DIR)/svn; \
$(MKDIR) $(DESTDIR)$(SWIG_RB_SITE_LIB_DIR)/svn; \
for i in $(abs_srcdir)/subversion/bindings/swig/ruby/svn/*.rb; do \
echo $(INSTALL_DATA) "$$i" $(DESTDIR)$(SWIG_RB_SITE_LIB_DIR)/svn; \
$(INSTALL_DATA) "$$i" $(DESTDIR)$(SWIG_RB_SITE_LIB_DIR)/svn; \
done
# export an env variable so that the tests can run without being installed
TEST_SHLIB_VAR_SWIG_RB=\
if [ "@SVN_APR_SHLIB_PATH_VAR@" = "DYLD_LIBRARY_PATH" ]; then \
for d in $(SWIG_RB_DIR)/libsvn_swig_ruby $(SWIG_RB_DIR)/../../../libsvn_*; do \
if [ -n "$$DYLD_LIBRARY_PATH" ]; then \
@SVN_APR_SHLIB_PATH_VAR@="$$@SVN_APR_SHLIB_PATH_VAR@:$$d/.libs"; \
else \
@SVN_APR_SHLIB_PATH_VAR@="$$d/.libs"; \
fi; \
done; \
export @SVN_APR_SHLIB_PATH_VAR@; \
fi;
APXS = @APXS@
+HTTPD_VERSION = @HTTPD_VERSION@
PYTHON = @PYTHON@
PERL = @PERL@
JDK = @JDK@
JAVA = @JAVA@
JAVAC = @JAVAC@
JAVADOC = @JAVADOC@
JAVAC_FLAGS = @JAVAC_FLAGS@
JAVAH = @JAVAH@
JAR = @JAR@
JAVA_CLASSPATH=$(abs_srcdir)/subversion/bindings/javahl/src:@JAVA_CLASSPATH@
javahl_java_CLASSPATH=$(JAVA_CLASSPATH)
javahl_compat_CLASSPATH=$(JAVA_CLASSPATH)
javahl_tests_CLASSPATH=$(JAVA_CLASSPATH)
javahl_compat_tests_CLASSPATH=$(JAVA_CLASSPATH)
RUBY = @RUBY@
RUBY_MAJOR = @RUBY_MAJOR@
RUBY_MINOR = @RUBY_MINOR@
RDOC = @RDOC@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
TESTS = $(TEST_PROGRAMS) @BDB_TEST_PROGRAMS@
all: mkdir-init local-all
clean: local-clean
distclean: local-distclean
extraclean: local-extraclean
install: local-install revision-install
@INCLUDE_OUTPUTS@
local-all: @BUILD_RULES@ @TRANSFORM_LIBTOOL_SCRIPTS@
transform-libtool-scripts: @BUILD_RULES@
@$(SHELL) $(top_srcdir)/build/transform_libtool_scripts.sh
locale-gnu-pot:
cd $(abs_srcdir) && XGETTEXT="$(XGETTEXT)" MSGMERGE="$(MSGMERGE)" \
$(SHELL) tools/po/po-update.sh pot
# "make locale-gnu-po-update" updates all translations.
# "make locale-gnu-po-update PO=ll" updates only the ll.po file.
locale-gnu-po-update:
cd $(abs_srcdir) && XGETTEXT="$(XGETTEXT)" MSGMERGE="$(MSGMERGE)" \
$(SHELL) tools/po/po-update.sh $(PO)
# clean everything but the bulky test output, returning the system back
# to before 'make' was run.
fast-clean: doc-clean
@list='$(BUILD_DIRS)'; for i in $$list; do \
if [ -d $$i ]; then \
(cd $$i && rm -f *.o *.lo *.la *.la-a *.spo *.mo && \
rm -rf .libs); \
fi \
done
echo $(CLEAN_FILES) | xargs rm -f --
find $(CTYPES_PYTHON_SRC_DIR) $(SWIG_PY_SRC_DIR) $(SWIG_PY_DIR) \
$(abs_srcdir)/build $(top_srcdir)/subversion/tests/cmdline/svntest \
-name "*.pyc" -exec rm {} ';'
# clean everything, returning to before './configure' was run.
SVN_CONFIG_SCRIPT_FILES = @SVN_CONFIG_SCRIPT_FILES@
local-distclean: local-clean
rm -fr config.cache config.log config.nice config.status \
libtool mkmf.log subversion/svn_private_config.h \
subversion/bindings/javahl/classes \
subversion/bindings/javahl/include \
$(SVN_CONFIG_SCRIPT_FILES)
rm -f Makefile
# clean everything out, returning to before './autogen.sh' was run.
local-extraclean: extraclean-bindings local-distclean
rm -f $(top_srcdir)/build-outputs.mk \
$(top_srcdir)/subversion/svn_private_config.h.in \
$(top_srcdir)/configure \
$(top_srcdir)/gen-make.opts \
$(top_srcdir)/build/config.guess \
$(top_srcdir)/build/config.sub \
$(top_srcdir)/build/libtool.m4 \
$(top_srcdir)/build/ltoptions.m4 \
$(top_srcdir)/build/ltsugar.m4 \
$(top_srcdir)/build/ltversion.m4 \
$(top_srcdir)/build/lt~obsolete.m4 \
$(top_srcdir)/build/ltmain.sh \
$(top_srcdir)/build/transform_libtool_scripts.sh \
$(EXTRACLEAN_FILES)
# clean everything, including test output.
local-clean: check-clean clean-bindings fast-clean
local-install: @INSTALL_RULES@
revision-install:
test -d $(DESTDIR)$(includedir)/subversion-1 || \
$(MKDIR) $(DESTDIR)$(includedir)/subversion-1
(subversion/svnversion/svnversion $(top_srcdir) 2> /dev/null || \
svnversion $(top_srcdir) 2> /dev/null || \
echo "unknown"; \
) > $(DESTDIR)$(includedir)/subversion-1/svn-revision.txt
install-static: @INSTALL_STATIC_RULES@
# JavaHL target aliases
javahl: mkdir-init javahl-java javahl-javah javahl-callback-javah javahl-types-javah javahl-lib @JAVAHL_TESTS_TARGET@ javahl-compat
install-javahl: javahl install-javahl-java install-javahl-javah install-javahl-lib
javahl-compat: javahl-compat-java @JAVAHL_COMPAT_TESTS_TARGET@
clean-javahl:
rm -rf $(javahl_java_PATH) $(javahl_javah_PATH) @JAVAHL_OBJDIR@
rm -fr $(javahl_test_rootdir)
rm -f $(libsvnjavahl_PATH)/*.la $(JAVAHL_JAR)
rm -f $(libsvnjavahl_PATH)/*.lo
rm -f $(libsvnjavahl_PATH)/*.o
check-tigris-javahl: javahl-compat
@FIX_JAVAHL_LIB@
$(TEST_SHLIB_VAR_JAVAHL) \
$(JAVA) "-Dtest.rootdir=$(javahl_test_rootdir)" "-Dtest.srcdir=$(javahl_test_srcdir)" "-Dtest.rooturl=$(BASE_URL)" "-Dtest.fstype=$(FS_TYPE)" "-Djava.library.path=@JAVAHL_OBJDIR@:$(libdir)" -classpath "$(javahl_compat_tests_PATH):$(javahl_tests_CLASSPATH)" "-Dtest.tests=$(JAVAHL_TESTS)" org.tigris.subversion.javahl.RunTests
check-apache-javahl: javahl
@FIX_JAVAHL_LIB@
$(TEST_SHLIB_VAR_JAVAHL) \
$(JAVA) "-Dtest.rootdir=$(javahl_test_rootdir)" "-Dtest.srcdir=$(javahl_test_srcdir)" "-Dtest.rooturl=$(BASE_URL)" "-Dtest.fstype=$(FS_TYPE)" "-Djava.library.path=@JAVAHL_OBJDIR@:$(libdir)" -classpath "$(javahl_tests_PATH):$(javahl_tests_CLASSPATH)" "-Dtest.tests=$(JAVAHL_TESTS)" org.apache.subversion.javahl.RunTests
check-javahl: check-apache-javahl
check-all-javahl: check-apache-javahl check-tigris-javahl
# "make check CLEANUP=true" will clean up directories for successful tests.
# "make check TESTS=subversion/tests/cmdline/basic_tests.py"
# will perform only basic tests (likewise for other tests).
check: bin @TRANSFORM_LIBTOOL_SCRIPTS@ $(TEST_DEPS) @BDB_TEST_DEPS@
@if test "$(PYTHON)" != "none"; then \
flags="--verbose"; \
if test "$(CLEANUP)" != ""; then \
flags="--cleanup $$flags"; \
fi; \
if test "$(BASE_URL)" != ""; then \
flags="--url $(BASE_URL) $$flags"; \
fi; \
if test "$(FS_TYPE)" != ""; then \
flags="--fs-type $(FS_TYPE) $$flags"; \
fi; \
if test "$(HTTP_LIBRARY)" != ""; then \
flags="--http-library $(HTTP_LIBRARY) $$flags"; \
+ fi; \
+ if test "$(HTTPD_VERSION)" != ""; then \
+ flags="--httpd-version $(HTTPD_VERSION) $$flags"; \
fi; \
if test "$(SERVER_MINOR_VERSION)" != ""; then \
flags="--server-minor-version $(SERVER_MINOR_VERSION) $$flags"; \
fi; \
if test "$(ENABLE_SASL)" != ""; then \
flags="--enable-sasl $$flags"; \
fi; \
if test "$(FSFS_SHARDING)" != ""; then \
flags="--fsfs-sharding $(FSFS_SHARDING) $$flags"; \
fi; \
if test "$(FSFS_PACKING)" != ""; then \
flags="--fsfs-packing $$flags"; \
fi; \
if test "$(PARALLEL)" != ""; then \
flags="--parallel $$flags"; \
fi; \
if test "$(LOG_TO_STDOUT)" != ""; then \
flags="--log-to-stdout $$flags"; \
fi; \
if test "$(MILESTONE_FILTER)" != ""; then \
flags="--list --milestone-filter=$(MILESTONE_FILTER) \
--mode-filter=$(MODE_FILTER) --log-to-stdout $$flags"; \
fi; \
if test "$(SET_LOG_LEVEL)" != ""; then \
flags="--set-log-level $(SET_LOG_LEVEL) $$flags"; \
fi; \
if test "$(SSL_CERT)" != ""; then \
flags="--ssl-cert $(SSL_CERT) $$flags"; \
fi; \
if test "$(HTTP_PROXY)" != ""; then \
flags="--http-proxy $(HTTP_PROXY) $$flags"; \
fi; \
LD_LIBRARY_PATH='$(auth_plugin_dirs):$(LD_LIBRARY_PATH)' \
$(PYTHON) $(top_srcdir)/build/run_tests.py \
--config-file $(top_srcdir)/subversion/tests/tests.conf \
$$flags \
'$(abs_srcdir)' '$(abs_builddir)' $(TESTS); \
else \
echo "make check: Python 2.5 or greater is required,"; \
echo " but was not detected during configure"; \
exit 1; \
fi;
# First, set up Apache as documented in
# subversion/tests/cmdline/README.
davcheck: bin $(TEST_DEPS) @BDB_TEST_DEPS@ apache-mod
@$(MAKE) check BASE_URL=http://localhost
# Automatically configure and run Apache httpd on a random port, and then
# run make check.
davautocheck: bin $(TEST_DEPS) @BDB_TEST_DEPS@ apache-mod
@# Takes MODULE_PATH, USE_HTTPV1 and SVN_PATH_AUTHZ in the environment.
@APXS=$(APXS) $(top_srcdir)/subversion/tests/cmdline/davautocheck.sh
# First, run:
# subversion/svnserve/svnserve -d -r `pwd`/subversion/tests/cmdline
svncheck: bin $(TEST_DEPS) @BDB_TEST_DEPS@
@$(MAKE) check BASE_URL=svn://127.0.0.1
# 'make svnserveautocheck' runs svnserve for you and kills it.
svnserveautocheck: svnserve bin $(TEST_DEPS) @BDB_TEST_DEPS@
@env PYTHON=$(PYTHON) THREADED=$(THREADED) \
$(top_srcdir)/subversion/tests/cmdline/svnserveautocheck.sh
# First, run:
# subversion/svnserve/svnserve --listen-host "::1" -d -r `pwd`/subversion/tests/cmdline
svncheck6: bin $(TEST_DEPS) @BDB_TEST_DEPS@
@$(MAKE) check BASE_URL=svn://\[::1\]
# First make sure you can ssh to localhost and that "svnserve" is in
# the path of the resulting shell.
svnsshcheck: bin $(TEST_DEPS) @BDB_TEST_DEPS@
@$(MAKE) check \
BASE_URL=svn+ssh://localhost`pwd`/subversion/tests/cmdline
bdbcheck: bin $(TEST_DEPS) @BDB_TEST_DEPS@
@$(MAKE) check FS_TYPE=bdb
# Create an execution coverage report from the data collected during
# all execution since the last reset.
gcov:
lcov --capture -d . -b . -o gcov-lcov.dat > gcov-lcov.log
genhtml gcov-lcov.dat -o gcov-report > gcov-genhtml.log
# Reset all execution coverage counters to zero.
gcov-reset:
lcov --zerocounters -d .
# Remove the execution coverage data and the report.
gcov-clean:
rm -f gcov-lcov.dat gcov-lcov.log gcov-genhtml.log
rm -rf gcov-report
find . -name "*.gcda" -o -name "*.gcno" -print0 | xargs -0 rm -f --
check-clean: gcov-clean
if [ -d subversion/tests/cmdline/svn-test-work ]; then \
find subversion/tests/cmdline/svn-test-work -mindepth 1 -maxdepth 1 \
-print0 | xargs -0 rm -rf --; \
fi
rm -rf subversion/tests/libsvn_fs/test-repo-* \
subversion/tests/libsvn_fs_base/test-repo-* \
subversion/tests/libsvn_fs_fs/test-repo-* \
subversion/tests/libsvn_ra_local/test-repo-* \
subversion/tests/libsvn_repos/test-repo-* \
subversion/tests/libsvn_subr/z \
subversion/tests/libsvn_wc/fake-wc \
subversion/tests/libsvn_client/test-patch* \
subversion/tests/libsvn_client/test-*/ \
subversion/tests/libsvn_diff/B2 \
subversion/tests/libsvn_diff/T1 \
subversion/tests/libsvn_diff/T2 \
subversion/tests/libsvn_diff/T3 \
subversion/tests/svnserveautocheck.pid \
tests.log fails.log
mkdir-init:
@list='$(BUILD_DIRS) $(SCHEMA_DIR) doc'; \
for i in $$list; do \
if [ ! -d $$i ]; then \
$(MKDIR) $$i ; \
fi; \
done
# DOCUMENTATION RULES
# Every single document in every format.
doc: doc-api doc-javahl
# Generate API documentation for the C libraries.
### This could also generate POD for swig-perl, etc.
doc-api: mkdir-init
( cd $(top_srcdir) && \
sed "s,\(OUTPUT_DIRECTORY *= *\),\1$(abs_builddir)/," \
doc/doxygen.conf | $(DOXYGEN) - )
# Generate API documentation for the JavaHL package.
doc-javahl:
$(JAVADOC) -d $(abs_builddir)/doc/javadoc \
-sourcepath $(top_srcdir)/subversion/bindings/javahl/src \
-link http://java.sun.com/javase/6/docs/api/ \
org.tigris.subversion.javahl \
org.apache.subversion.javahl \
org.apache.subversion.javahl.callback \
org.apache.subversion.javahl.types
doc-clean:
rm -rf $(top_srcdir)/doc/doxygen
rm -rf $(top_srcdir)/doc/javadoc
# Converting from the .rnc XML shcemas to various other schema formats.
SCHEMAS_DTD = $(SCHEMA_DIR)/blame.dtd $(SCHEMA_DIR)/info.dtd \
$(SCHEMA_DIR)/list.dtd $(SCHEMA_DIR)/log.dtd \
$(SCHEMA_DIR)/status.dtd $(SCHEMA_DIR)/props.dtd
SCHEMAS_RNG = $(SCHEMA_DIR)/blame.rng $(SCHEMA_DIR)/info.rng \
$(SCHEMA_DIR)/list.rng $(SCHEMA_DIR)/log.rng \
$(SCHEMA_DIR)/status.rng $(SCHEMA_DIR)/props.rng
SCHEMAS_XSD = $(SCHEMA_DIR)/blame.xsd $(SCHEMA_DIR)/info.xsd \
$(SCHEMA_DIR)/list.xsd $(SCHEMA_DIR)/log.xsd \
$(SCHEMA_DIR)/status.xsd $(SCHEMA_DIR)/props.xsd
schema: schema-rng schema-dtd schema-xsd
schema-rng: $(SCHEMAS_RNG)
schema-dtd: $(SCHEMAS_DTD)
schema-xsd: $(SCHEMAS_XSD)
$(SCHEMAS_RNG) $(SCHEMAS_DTD) $(SCHEMAS_XSD): $(SCHEMA_DIR)/common.rnc
schema-clean:
(cd $(SCHEMA_DIR) && rm -f *.rng *.dtd *.xsd)
#
# Implicit rules for creating outputs from input files
#
.SUFFIXES:
.SUFFIXES: .c .cpp .lo .o .la-a .la \
.po .spo .mo .rnc .rng .dtd .xsd .sql .h
.sql.h:
$(PYTHON) $(top_srcdir)/build/transform_sql.py $< $(top_srcdir)/$@
.c.o:
$(COMPILE) -o $@ -c $<
.cpp.o:
$(COMPILE_CXX) -o $@ -c $<
.c.lo:
$(LT_COMPILE) -o $@ -c $<
.cpp.lo:
$(LT_COMPILE_CXX) -o $@ -c $<
.la.la-a:
sed "/library_names/s/'.*'/''/" $< > $@
# Strip the Content-Type: header from the po file if we don't have a
# gettext that supports bind_textdomain_codeset, so it doesn't try
# to convert our UTF-8 .po files to the locale encoding.
@NO_GETTEXT_CODESET@.po.spo:
@NO_GETTEXT_CODESET@ sed \
@NO_GETTEXT_CODESET@ '/^"Content-Type: text\/plain; charset=UTF-8\\n"$$/d' \
@NO_GETTEXT_CODESET@ $< > $@
@NO_GETTEXT_CODESET@.spo.mo:
@NO_GETTEXT_CODESET@ $(MSGFMT) $(MSGFMTFLAGS) -o $@ $<
# For systems with bind_textdomain_codeset, just leave the Content-Type:
# header alone.
@GETTEXT_CODESET@.po.mo:
@GETTEXT_CODESET@ $(MSGFMT) $(MSGFMTFLAGS) -o $@ $<
.rnc.rng:
@TRANG@ $< $@
.rnc.dtd:
@TRANG@ $< $@
.rnc.xsd:
@TRANG@ $< $@
install-docs: install-man
manroot = $(mandir)/man
install-man:
@list='$(MANPAGES)'; \
for i in $$list; do \
if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
else file=$$i; fi; \
ext=`echo $$i | sed -e 's/^.*\\.//'`; \
$(MKDIR) $(DESTDIR)$(manroot)$$ext; \
inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
inst=`echo $$inst | sed -e 's/^.*\///'`; \
inst=`echo $$inst`.$$ext; \
echo "$(INSTALL_DATA) $$file $(DESTDIR)$(manroot)$$ext/$$inst"; \
$(INSTALL_DATA) $$file $(DESTDIR)$(manroot)$$ext/$$inst; \
done
install-swig-py: install-swig-py-lib
install-swig-rb: install-swig-rb-lib
clean-bindings: clean-swig clean-ctypes-python clean-javahl
extraclean-bindings: clean-swig extraclean-swig-headers \
extraclean-swig-py extraclean-swig-rb \
extraclean-swig-pl \
clean-ctypes-python clean-javahl \
clean-swig: clean-swig-headers clean-swig-py clean-swig-rb clean-swig-pl
@rm -f .swig_checked
EXTRACLEAN_SWIG_HEADERS=rm -f $(SWIG_SRC_DIR)/proxy/*.swg
clean-swig-headers:
if test -z "$(RELEASE_MODE)"; then \
$(EXTRACLEAN_SWIG_HEADERS); \
fi
extraclean-swig-headers: clean-swig-headers
$(EXTRACLEAN_SWIG_HEADERS)
$(SWIG_PL_DIR)/native/Makefile.PL: $(SWIG_SRC_DIR)/perl/native/Makefile.PL.in
./config.status subversion/bindings/swig/perl/native/Makefile.PL
$(SWIG_PL_DIR)/native/Makefile: $(SWIG_PL_DIR)/native/Makefile.PL
cd $(SWIG_PL_DIR)/native; $(PERL) Makefile.PL
# There is a "readlink -f" command on some systems for the same purpose,
# but it's not as portable (e.g. Mac OS X doesn't have it). These should
# only be used where Python/Perl are known to be available.
READLINK_PY=$(PYTHON) -c 'import sys,os; print(os.path.realpath(sys.argv[1]))'
READLINK_PL=$(PERL) -e 'use Cwd; print Cwd::realpath(shift)'
swig-pl_DEPS = autogen-swig-pl libsvn_swig_perl \
$(SWIG_PL_DIR)/native/Makefile
swig-pl: $(swig-pl_DEPS)
if test "`$(READLINK_PL) $(SWIG_PL_DIR)`" != "`$(READLINK_PL) $(SWIG_PL_SRC_DIR)`"; then \
ln -sf $(SWIG_PL_SRC_DIR)/native/*.c $(SWIG_PL_DIR)/native; \
fi
cd $(SWIG_PL_DIR)/native; $(MAKE) OPTIMIZE="" OTHERLDFLAGS="$(SWIG_LDFLAGS)"
check-swig-pl: swig-pl swig-pl-lib
cd $(SWIG_PL_DIR)/native; $(MAKE) test
install-swig-pl: swig-pl install-swig-pl-lib
cd $(SWIG_PL_DIR)/native; $(MAKE) install
EXTRACLEAN_SWIG_PL=rm -f $(SWIG_PL_SRC_DIR)/native/svn_*.c \
$(SWIG_PL_SRC_DIR)/native/core.c
# Running Makefile.PL at this point *fails* (cannot find ..../.libs) so if the
# Makefile does not exist, DO NOT try to make it. But, if it doesn't exist,
# then the directory is probably clean anyway.
clean-swig-pl:
if test -z "$(RELEASE_MODE)"; then \
$(EXTRACLEAN_SWIG_PL); \
fi
for d in $(SWIG_PL_DIR)/libsvn_swig_perl; \
do \
cd $$d; \
rm -rf *.lo *.la *.o .libs; \
done
if [ -f "$(SWIG_PL_DIR)/native/Makefile" ]; then \
cd $(SWIG_PL_DIR)/native; $(MAKE) clean; \
fi
extraclean-swig-pl: clean-swig-pl
$(EXTRACLEAN_SWIG_PL)
$(SWIG_PY_DIR)/libsvn:
mkdir $(SWIG_PY_DIR)/libsvn
copy-swig-py: autogen-swig-py $(SWIG_PY_DIR)/libsvn
@for f in $(SWIG_PY_SRC_DIR)/*.py $(SWIG_PY_DIR)/*.py; do \
! [ -f "$$f" ] || cp -pf $$f $(SWIG_PY_DIR)/libsvn; \
done
@touch $(SWIG_PY_DIR)/libsvn/__init__.py
swig-py: autogen-swig-py copy-swig-py
check-swig-py: swig-py
$(TEST_SHLIB_VAR_SWIG_PY) \
cd $(SWIG_PY_DIR); \
$(PYTHON) $(SWIG_PY_SRC_DIR)/tests/run_all.py
EXTRACLEAN_SWIG_PY=rm -rf $(SWIG_PY_SRC_DIR)/svn_*.c $(SWIG_PY_SRC_DIR)/core.c \
$(SWIG_PY_SRC_DIR)/[a-z]*.py
clean-swig-py:
rm -rf $(SWIG_PY_DIR)/libsvn
if test -z "$(RELEASE_MODE)"; then \
$(EXTRACLEAN_SWIG_PY); \
fi
for d in $(SWIG_PY_DIR) $(SWIG_PY_DIR)/libsvn_swig_py; \
do \
cd $$d && rm -rf *.lo *.la *.o *.pyc .libs; \
done
find $(SWIG_PY_SRC_DIR) $(SWIG_PY_DIR) -name "*.pyc" -exec rm {} ';'
extraclean-swig-py: clean-swig-py
$(EXTRACLEAN_SWIG_PY)
swig-rb: autogen-swig-rb
check-swig-rb: swig-rb svnserve
$(TEST_SHLIB_VAR_SWIG_RB) \
cd $(SWIG_RB_DIR); \
if [ "$(RUBY_MAJOR)" -eq 1 -a "$(RUBY_MINOR)" -lt 9 ] ; then \
$(RUBY) -I $(SWIG_RB_SRC_DIR) \
$(SWIG_RB_SRC_DIR)/test/run-test.rb \
--verbose=$(SWIG_RB_TEST_VERBOSE); \
else \
$(RUBY) -I $(SWIG_RB_SRC_DIR) \
$(SWIG_RB_SRC_DIR)/test/run-test.rb; \
fi
EXTRACLEAN_SWIG_RB=rm -f $(SWIG_RB_SRC_DIR)/svn_*.c $(SWIG_RB_SRC_DIR)/core.c
clean-swig-rb:
rm -rf $(SWIG_RB_DIR)/test/repos $(SWIG_RB_DIR)/test/wc
if test -z "$(RELEASE_MODE)"; then \
$(EXTRACLEAN_SWIG_RB); \
fi
for d in $(SWIG_RB_DIR) $(SWIG_RB_DIR)/libsvn_swig_ruby; \
do \
cd $$d; \
rm -rf *.lo *.la *.o .libs; \
done
extraclean-swig-rb: clean-swig-rb
$(EXTRACLEAN_SWIG_RB)
install-swig-rb-doc:
$(RDOC) --all --ri --op "$(SWIG_RB_RI_DATADIR)" "$(SWIG_RB_SRC_DIR)/svn"
# ctypes-python make targets
ctypes-python: local-all
$(SHELL) $(abs_srcdir)/build/run_ctypesgen.sh "$(LT_EXECUTE)" "$(CPPFLAGS)" "$(EXTRA_CTYPES_LDFLAGS)" "$(PYTHON)" "$(CTYPESGEN)" "$(abs_srcdir)" "$(abs_builddir)" "$(libdir)" "$(SVN_APR_CONFIG)" "$(SVN_APRUTIL_CONFIG)" "$(CPP)"
install-ctypes-python: ctypes-python
cd $(CTYPES_PYTHON_SRC_DIR); \
$(PYTHON) setup.py install --prefix="$(DESTDIR)$(prefix)"
check-ctypes-python: ctypes-python
cd $(CTYPES_PYTHON_SRC_DIR); \
$(LT_EXECUTE) $(PYTHON) test/run_all.py
# If any of those files exists, run ctypes' 'setup.py clean'. We don't run
# it otherwise because it spams stdout+stderr; see r1479326.
clean-ctypes-python:
cd $(CTYPES_PYTHON_SRC_DIR); \
for b in build csvn/core/functions.py svn_all.py svn_all2.py ; do \
if [ -e "$$b" ] ; then \
$(PYTHON) setup.py clean --all; \
break; \
fi; \
done
# manually describe a dependency, which we won't otherwise detect
subversion/libsvn_wc/wc-queries.h: $(abs_srcdir)/subversion/libsvn_wc/wc-metadata.sql
subversion/libsvn_wc/wc-queries.h: $(abs_srcdir)/subversion/libsvn_wc/wc-checks.sql
# Compatibility symlink.
# This runs after the target of the same name in build-outputs.mk.
INSTALL_EXTRA_TOOLS=\
$(MKDIR) $(DESTDIR)$(bindir); \
test -n "$$SVN_SVNMUCC_IS_SVNSYITF" && \
ln -sf svnmucc$(EXEEXT) $(DESTDIR)$(bindir)/svnsyitf$(EXEEXT); \
if test "$(DESTDIR)$(bindir)" != "$(DESTDIR)$(toolsdir)"; then \
ln -sf $(bindir)/svnmucc$(EXEEXT) $(DESTDIR)$(toolsdir)/svnmucc$(EXEEXT); \
fi
Index: vendor/subversion/dist/NOTICE
===================================================================
--- vendor/subversion/dist/NOTICE (revision 286500)
+++ vendor/subversion/dist/NOTICE (revision 286501)
@@ -1,23 +1,23 @@
Apache Subversion
-Copyright 2014 The Apache Software Foundation
+Copyright 2015 The Apache Software Foundation
This product includes software developed by many people, and distributed
under Contributor License Agreements to The Apache Software Foundation
(http://www.apache.org/). See the accompanying COMMITTERS file and the
revision logs for an exact contribution history.
Portions of the test suite for Subversion's Python bindings are copyrighted
by Edgewall Software, Jonas Borgström and Christopher Lenz.
For more information, see LICENSE.
This product includes software developed under the X Consortium License
see: build/install-sh
This product includes software developed by Markus Kuhn under a permissive
license, see LICENSE.
This software contains code derived from the RSA Data Security
Inc. MD5 Message-Digest Algorithm, including various
modifications by Spyglass Inc., Carnegie Mellon University, and
Bell Communications Research, Inc (Bellcore).
Index: vendor/subversion/dist/autogen.sh
===================================================================
--- vendor/subversion/dist/autogen.sh (revision 286500)
+++ vendor/subversion/dist/autogen.sh (revision 286501)
@@ -1,210 +1,246 @@
#!/bin/sh
#
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
#
### Run this to produce everything needed for configuration. ###
+# Some shells can produce output when running 'cd' which interferes
+# with the construct 'abs=`cd dir && pwd`'.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
# Run tests to ensure that our build requirements are met
RELEASE_MODE=""
RELEASE_ARGS=""
SKIP_DEPS=""
while test $# != 0; do
case "$1" in
--release)
RELEASE_MODE="$1"
RELEASE_ARGS="--release"
shift
;;
-s)
SKIP_DEPS="yes"
shift
;;
--) # end of option parsing
break
;;
*)
echo "invalid parameter: '$1'"
exit 1
;;
esac
done
# ### The order of parameters is important; buildcheck.sh depends on it and
# ### we don't want to copy the fancy option parsing loop there. For the
# ### same reason, all parameters should be quoted, so that buildcheck.sh
# ### sees an empty arg rather than missing one.
./build/buildcheck.sh "$RELEASE_MODE" || exit 1
# Handle some libtool helper files
#
# ### eventually, we can/should toss this in favor of simply using
# ### APR's libtool. deferring to a second round of change...
#
libtoolize="`./build/PrintPath glibtoolize libtoolize libtoolize15`"
lt_major_version=`$libtoolize --version 2>/dev/null | sed -e 's/^[^0-9]*//' -e 's/\..*//' -e '/^$/d' -e 1q`
if [ "x$libtoolize" = "x" ]; then
echo "libtoolize not found in path"
exit 1
fi
rm -f build/config.guess build/config.sub
$libtoolize --copy --automake --force
ltpath="`dirname $libtoolize`"
-ltfile=${LIBTOOL_M4-`cd $ltpath/../share/aclocal ; pwd`/libtool.m4}
-if [ ! -f $ltfile ]; then
- echo "$ltfile not found (try setting the LIBTOOL_M4 environment variable)"
+if [ "x$LIBTOOL_M4" = "x" ]; then
+ ltm4_error='(try setting the LIBTOOL_M4 environment variable)'
+ if [ -d "$ltpath/../share/aclocal/." ]; then
+ ltm4=`cd "$ltpath/../share/aclocal" && pwd`
+ else
+ echo "Libtool helper path not found $ltm4_error"
+ echo " expected at: '$ltpath/../share/aclocal'"
+ exit 1
+ fi
+else
+ ltm4_error="(the LIBTOOL_M4 environment variable is: $LIBTOOL_M4)"
+ ltm4="$LIBTOOL_M4"
+fi
+
+ltfile="$ltm4/libtool.m4"
+if [ ! -f "$ltfile" ]; then
+ echo "$ltfile not found $ltm4_error"
exit 1
fi
-echo "Copying libtool helper: $ltfile"
+echo "Copying libtool helper: $ltfile"
# An ancient helper might already be present from previous builds,
# and it might be write-protected (e.g. mode 444, seen on FreeBSD).
# This would cause cp to fail and print an error message, but leave
# behind a potentially outdated libtool helper. So, remove before
# copying:
rm -f build/libtool.m4
-cp $ltfile build/libtool.m4
+cp "$ltfile" build/libtool.m4
for file in ltoptions.m4 ltsugar.m4 ltversion.m4 lt~obsolete.m4; do
rm -f build/$file
if [ $lt_major_version -ge 2 ]; then
- ltfile=${LIBTOOL_M4-`cd $ltpath/../share/aclocal ; pwd`/$file}
+ ltfile="$ltm4/$file"
- if [ ! -f $ltfile ]; then
- echo "$ltfile not found (try setting the LIBTOOL_M4 environment variable)"
+ if [ ! -f "$ltfile" ]; then
+ echo "$ltfile not found $ltm4_error"
exit 1
fi
- echo "Copying libtool helper: $ltfile"
- cp $ltfile build/$file
+ echo "Copying libtool helper: $ltfile"
+ cp "$ltfile" "build/$file"
fi
done
if [ $lt_major_version -ge 2 ]; then
+ if [ "x$LIBTOOL_CONFIG" = "x" ]; then
+ ltconfig_error='(try setting the LIBTOOL_CONFIG environment variable)'
+ if [ -d "$ltpath/../share/libtool/config/." ]; then
+ ltconfig=`cd "$ltpath/../share/libtool/config" && pwd`
+ elif [ -d "$ltpath/../share/libtool/build-aux/." ]; then
+ ltconfig=`cd "$ltpath/../share/libtool/build-aux" && pwd`
+ else
+ echo "Autoconf helper path not found $ltconfig_error"
+ echo " expected at: '$ltpath/../share/libtool/config'"
+ echo " or: '$ltpath/../share/libtool/build-aux'"
+ exit 1
+ fi
+ else
+ ltconfig_error="(the LIBTOOL_CONFIG environment variable is: $LIBTOOL_CONFIG)"
+ ltconfig="$LIBTOOL_CONFIG"
+ fi
+
for file in config.guess config.sub; do
- configfile=${LIBTOOL_CONFIG-`cd $ltpath/../share/libtool/config ; pwd`/$file}
+ configfile="$ltconfig/$file"
- if [ ! -f $configfile ]; then
- echo "$configfile not found (try setting the LIBTOOL_CONFIG environment variable)"
+ if [ ! -f "$configfile" ]; then
+ echo "$configfile not found $ltconfig_error"
exit 1
fi
- cp $configfile build/$file
+ echo "Copying autoconf helper: $configfile"
+ cp "$configfile" build/$file
done
fi
# Create the file detailing all of the build outputs for SVN.
#
# Note: this dependency on Python is fine: only SVN developers use autogen.sh
# and we can state that dev people need Python on their machine. Note
# that running gen-make.py requires Python 2.5 or newer.
PYTHON="`./build/find_python.sh`"
if test -z "$PYTHON"; then
echo "Python 2.5 or later is required to run autogen.sh"
echo "If you have a suitable Python installed, but not on the"
echo "PATH, set the environment variable PYTHON to the full path"
echo "to the Python executable, and re-run autogen.sh"
exit 1
fi
# Compile SWIG headers into standalone C files if we are in release mode
if test -n "$RELEASE_MODE"; then
echo "Generating SWIG code..."
# Generate build-outputs.mk in non-release-mode, so that we can
# build the SWIG-related files
"$PYTHON" ./gen-make.py build.conf || gen_failed=1
# Build the SWIG-related files
make -f autogen-standalone.mk autogen-swig
# Remove the .swig_checked file
rm -f .swig_checked
fi
if test -n "$SKIP_DEPS"; then
echo "Creating build-outputs.mk (no dependencies)..."
"$PYTHON" ./gen-make.py $RELEASE_ARGS -s build.conf || gen_failed=1
else
echo "Creating build-outputs.mk..."
"$PYTHON" ./gen-make.py $RELEASE_ARGS build.conf || gen_failed=1
fi
if test -n "$RELEASE_MODE"; then
find build/ -name '*.pyc' -exec rm {} \;
fi
rm autogen-standalone.mk
if test -n "$gen_failed"; then
echo "ERROR: gen-make.py failed"
exit 1
fi
# Produce config.h.in
echo "Creating svn_private_config.h.in..."
${AUTOHEADER:-autoheader}
# If there's a config.cache file, we may need to delete it.
# If we have an existing configure script, save a copy for comparison.
if [ -f config.cache ] && [ -f configure ]; then
cp configure configure.$$.tmp
fi
# Produce ./configure
echo "Creating configure..."
${AUTOCONF:-autoconf}
# If we have a config.cache file, toss it if the configure script has
# changed, or if we just built it for the first time.
if [ -f config.cache ]; then
(
[ -f configure.$$.tmp ] && cmp configure configure.$$.tmp > /dev/null 2>&1
) || (
echo "Tossing config.cache, since configure has changed."
rm config.cache
)
rm -f configure.$$.tmp
fi
# Remove autoconf 2.5x's cache directory
rm -rf autom4te*.cache
echo ""
echo "You can run ./configure now."
echo ""
echo "Running autogen.sh implies you are a maintainer. You may prefer"
echo "to run configure in one of the following ways:"
echo ""
echo "./configure --enable-maintainer-mode"
echo "./configure --disable-shared"
echo "./configure --enable-maintainer-mode --disable-shared"
echo "./configure --disable-optimize --enable-debug"
echo "./configure CUSERFLAGS='--flags-for-C' CXXUSERFLAGS='--flags-for-C++'"
echo ""
echo "Note: If you wish to run a Subversion HTTP server, you will need"
echo "Apache 2.x. See the INSTALL file for details."
echo ""
Index: vendor/subversion/dist/build-outputs.mk
===================================================================
--- vendor/subversion/dist/build-outputs.mk (revision 286500)
+++ vendor/subversion/dist/build-outputs.mk (revision 286501)
@@ -1,2894 +1,2894 @@
# DO NOT EDIT -- AUTOMATICALLY GENERATED BY build/generator/gen_make.py
# FROM build/generator/templates/build-outputs.mk.ezt
########################################
# Section 1: Global make variables
########################################
FS_BASE_DEPS = subversion/libsvn_fs_base/libsvn_fs_base-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/libsvn_fs_util/libsvn_fs_util-1.la
FS_BASE_LINK = ../../subversion/libsvn_fs_base/libsvn_fs_base-1.la ../../subversion/libsvn_delta/libsvn_delta-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la ../../subversion/libsvn_fs_util/libsvn_fs_util-1.la
FS_FS_DEPS = subversion/libsvn_fs_fs/libsvn_fs_fs-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/libsvn_fs_util/libsvn_fs_util-1.la
FS_FS_LINK = ../../subversion/libsvn_fs_fs/libsvn_fs_fs-1.la ../../subversion/libsvn_delta/libsvn_delta-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la ../../subversion/libsvn_fs_util/libsvn_fs_util-1.la
RA_LOCAL_DEPS = subversion/libsvn_ra_local/libsvn_ra_local-1.la subversion/libsvn_repos/libsvn_repos-1.la subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
RA_LOCAL_LINK = ../../subversion/libsvn_ra_local/libsvn_ra_local-1.la ../../subversion/libsvn_repos/libsvn_repos-1.la ../../subversion/libsvn_fs/libsvn_fs-1.la ../../subversion/libsvn_delta/libsvn_delta-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la
RA_SERF_DEPS = subversion/libsvn_ra_serf/libsvn_ra_serf-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
RA_SERF_LINK = ../../subversion/libsvn_ra_serf/libsvn_ra_serf-1.la ../../subversion/libsvn_delta/libsvn_delta-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la
RA_SVN_DEPS = subversion/libsvn_ra_svn/libsvn_ra_svn-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
RA_SVN_LINK = ../../subversion/libsvn_ra_svn/libsvn_ra_svn-1.la ../../subversion/libsvn_delta/libsvn_delta-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la
BUILD_DIRS = subversion/tests/cmdline subversion/tests/libsvn_subr subversion/tests/libsvn_fs_base subversion/tests/libsvn_client subversion/tests/libsvn_wc subversion/bindings/cxxhl subversion/bindings/cxxhl/tests tools/diff subversion/tests/libsvn_diff subversion/tests/libsvn_fs_fs subversion/tests/libsvn_fs tools/dev tools/server-side subversion/bindings/javahl/src/org/apache/subversion/javahl/callback subversion/bindings/javahl/classes subversion/bindings/javahl/include subversion/bindings/javahl/src/org/tigris/subversion/javahl subversion/bindings/javahl/tests/org/tigris/subversion/javahl subversion/bindings/javahl/src/org/apache/subversion/javahl subversion/bindings/javahl/src/org/apache/subversion/javahl/types subversion/bindings/javahl/tests/org/apache/subversion/javahl subversion/libsvn_auth_gnome_keyring subversion/libsvn_auth_kwallet subversion/libsvn_client subversion/libsvn_delta subversion/libsvn_diff subversion/libsvn_fs subversion/libsvn_fs_base subversion/libsvn_fs_base/bdb subversion/libsvn_fs_base/util subversion/libsvn_fs_fs subversion/libsvn_fs_util subversion/libsvn_ra subversion/libsvn_ra_local subversion/libsvn_ra_serf subversion/libsvn_ra_svn subversion/libsvn_repos subversion/libsvn_subr subversion/bindings/swig/perl/libsvn_swig_perl subversion/bindings/swig/python/libsvn_swig_py subversion/bindings/swig/ruby/libsvn_swig_ruby subversion/tests subversion/libsvn_wc subversion/bindings/cxxhl/src subversion/bindings/javahl/native subversion/po subversion/mod_authz_svn subversion/mod_dav_svn subversion/mod_dav_svn/reports subversion/mod_dav_svn/posts tools/server-side/mod_dontdothat subversion/tests/libsvn_ra_local subversion/tests/libsvn_ra subversion/tests/libsvn_delta subversion/tests/libsvn_repos subversion/svn tools/client-side/svn-bench subversion/svnadmin subversion/svndumpfilter subversion/svnlook subversion/svnmucc tools/dev/svnraisetreeconflict subversion/svnrdump subversion/svnserve subversion/svnsync subversion/svnversion subversion/bindings/swig subversion/bindings/swig/python subversion/bindings/swig/perl subversion/bindings/swig/ruby subversion/bindings/swig/proxy
BDB_TEST_DEPS = subversion/tests/libsvn_fs_base/changes-test$(EXEEXT) subversion/tests/libsvn_fs_base/fs-base-test$(EXEEXT) subversion/tests/libsvn_fs_base/strings-reps-test$(EXEEXT)
BDB_TEST_PROGRAMS = subversion/tests/libsvn_fs_base/changes-test$(EXEEXT) subversion/tests/libsvn_fs_base/fs-base-test$(EXEEXT) subversion/tests/libsvn_fs_base/strings-reps-test$(EXEEXT)
-TEST_DEPS = subversion/tests/cmdline/atomic-ra-revprop-change$(EXEEXT) subversion/tests/libsvn_subr/auth-test$(EXEEXT) subversion/tests/libsvn_subr/cache-test$(EXEEXT) subversion/tests/libsvn_subr/checksum-test$(EXEEXT) subversion/tests/libsvn_client/client-test$(EXEEXT) subversion/tests/libsvn_subr/compat-test$(EXEEXT) subversion/tests/libsvn_subr/config-test$(EXEEXT) subversion/tests/libsvn_wc/conflict-data-test$(EXEEXT) subversion/tests/libsvn_subr/crypto-test$(EXEEXT) subversion/tests/libsvn_wc/db-test$(EXEEXT) subversion/tests/libsvn_diff/diff-diff3-test$(EXEEXT) subversion/tests/libsvn_subr/dirent_uri-test$(EXEEXT) subversion/tests/libsvn_wc/entries-compat-test$(EXEEXT) subversion/tests/cmdline/entries-dump$(EXEEXT) subversion/tests/libsvn_subr/error-code-test$(EXEEXT) subversion/tests/libsvn_subr/error-test$(EXEEXT) subversion/tests/libsvn_fs_fs/fs-pack-test$(EXEEXT) subversion/tests/libsvn_fs/fs-test$(EXEEXT) subversion/tests/libsvn_subr/hashdump-test$(EXEEXT) subversion/tests/libsvn_subr/io-test$(EXEEXT) subversion/tests/libsvn_fs/locks-test$(EXEEXT) subversion/tests/libsvn_subr/mergeinfo-test$(EXEEXT) subversion/tests/libsvn_subr/named_atomic-test$(EXEEXT) subversion/tests/libsvn_wc/op-depth-test$(EXEEXT) subversion/tests/libsvn_subr/opt-test$(EXEEXT) subversion/tests/libsvn_diff/parse-diff-test$(EXEEXT) subversion/tests/libsvn_subr/path-test$(EXEEXT) subversion/tests/libsvn_wc/pristine-store-test$(EXEEXT) subversion/tests/libsvn_ra_local/ra-local-test$(EXEEXT) subversion/tests/libsvn_ra/ra-test$(EXEEXT) subversion/tests/libsvn_delta/random-test$(EXEEXT) subversion/tests/libsvn_repos/repos-test$(EXEEXT) subversion/tests/libsvn_subr/revision-test$(EXEEXT) subversion/tests/libsvn_subr/skel-test$(EXEEXT) subversion/tests/libsvn_subr/spillbuf-test$(EXEEXT) subversion/tests/libsvn_subr/stream-test$(EXEEXT) subversion/tests/libsvn_subr/string-test$(EXEEXT) subversion/tests/libsvn_subr/subst_translate-test$(EXEEXT) tools/server-side/svnauthz$(EXEEXT) tools/server-side/svnauthz-validate$(EXEEXT) subversion/tests/libsvn_delta/svndiff-test$(EXEEXT) subversion/tests/libsvn_subr/time-test$(EXEEXT) subversion/tests/libsvn_subr/translate-test$(EXEEXT) subversion/tests/libsvn_subr/utf-test$(EXEEXT) subversion/tests/libsvn_delta/vdelta-test$(EXEEXT) subversion/tests/libsvn_wc/wc-incomplete-tester$(EXEEXT) subversion/tests/libsvn_wc/wc-lock-tester$(EXEEXT) subversion/tests/libsvn_wc/wc-queries-test$(EXEEXT) subversion/tests/libsvn_wc/wc-test$(EXEEXT) subversion/tests/libsvn_delta/window-test$(EXEEXT) subversion/tests/cmdline/authz_tests.py subversion/tests/cmdline/autoprop_tests.py subversion/tests/cmdline/basic_tests.py subversion/tests/cmdline/blame_tests.py subversion/tests/cmdline/cat_tests.py subversion/tests/cmdline/changelist_tests.py subversion/tests/cmdline/checkout_tests.py subversion/tests/cmdline/commit_tests.py subversion/tests/cmdline/copy_tests.py subversion/tests/cmdline/depth_tests.py subversion/tests/cmdline/diff_tests.py subversion/tests/cmdline/entries_tests.py subversion/tests/cmdline/export_tests.py subversion/tests/cmdline/externals_tests.py subversion/tests/cmdline/getopt_tests.py subversion/tests/cmdline/history_tests.py subversion/tests/cmdline/import_tests.py subversion/tests/cmdline/info_tests.py subversion/tests/cmdline/input_validation_tests.py subversion/tests/cmdline/iprop_authz_tests.py subversion/tests/cmdline/iprop_tests.py subversion/tests/cmdline/lock_tests.py subversion/tests/cmdline/log_tests.py subversion/tests/cmdline/merge_authz_tests.py subversion/tests/cmdline/merge_automatic_tests.py subversion/tests/cmdline/merge_reintegrate_tests.py subversion/tests/cmdline/merge_tests.py subversion/tests/cmdline/merge_tree_conflict_tests.py subversion/tests/cmdline/mergeinfo_tests.py subversion/tests/cmdline/move_tests.py subversion/tests/cmdline/patch_tests.py subversion/tests/cmdline/prop_tests.py subversion/tests/cmdline/redirect_tests.py subversion/tests/cmdline/relocate_tests.py subversion/tests/cmdline/resolve_tests.py subversion/tests/cmdline/revert_tests.py subversion/tests/cmdline/schedule_tests.py subversion/tests/cmdline/special_tests.py subversion/tests/cmdline/stat_tests.py subversion/tests/cmdline/svnadmin_tests.py subversion/tests/cmdline/svnauthz_tests.py subversion/tests/cmdline/svndumpfilter_tests.py subversion/tests/cmdline/svnlook_tests.py subversion/tests/cmdline/svnmucc_tests.py subversion/tests/cmdline/svnrdump_tests.py subversion/tests/cmdline/svnsync_authz_tests.py subversion/tests/cmdline/svnsync_tests.py subversion/tests/cmdline/svnversion_tests.py subversion/tests/cmdline/switch_tests.py subversion/tests/cmdline/trans_tests.py subversion/tests/cmdline/tree_conflict_tests.py subversion/tests/cmdline/update_tests.py subversion/tests/cmdline/upgrade_tests.py subversion/tests/cmdline/wc_tests.py
+TEST_DEPS = subversion/tests/cmdline/atomic-ra-revprop-change$(EXEEXT) subversion/tests/libsvn_subr/auth-test$(EXEEXT) subversion/tests/libsvn_subr/cache-test$(EXEEXT) subversion/tests/libsvn_subr/checksum-test$(EXEEXT) subversion/tests/libsvn_client/client-test$(EXEEXT) subversion/tests/libsvn_subr/compat-test$(EXEEXT) subversion/tests/libsvn_subr/config-test$(EXEEXT) subversion/tests/libsvn_wc/conflict-data-test$(EXEEXT) subversion/tests/libsvn_subr/crypto-test$(EXEEXT) subversion/tests/libsvn_wc/db-test$(EXEEXT) subversion/tests/libsvn_diff/diff-diff3-test$(EXEEXT) subversion/tests/libsvn_subr/dirent_uri-test$(EXEEXT) subversion/tests/libsvn_wc/entries-compat-test$(EXEEXT) subversion/tests/cmdline/entries-dump$(EXEEXT) subversion/tests/libsvn_subr/error-code-test$(EXEEXT) subversion/tests/libsvn_subr/error-test$(EXEEXT) subversion/tests/libsvn_fs_fs/fs-pack-test$(EXEEXT) subversion/tests/libsvn_fs/fs-test$(EXEEXT) subversion/tests/libsvn_subr/hashdump-test$(EXEEXT) subversion/tests/libsvn_subr/io-test$(EXEEXT) subversion/tests/libsvn_fs/locks-test$(EXEEXT) subversion/tests/libsvn_subr/mergeinfo-test$(EXEEXT) subversion/tests/libsvn_subr/named_atomic-test$(EXEEXT) subversion/tests/libsvn_wc/op-depth-test$(EXEEXT) subversion/tests/libsvn_subr/opt-test$(EXEEXT) subversion/tests/libsvn_diff/parse-diff-test$(EXEEXT) subversion/tests/libsvn_subr/path-test$(EXEEXT) subversion/tests/libsvn_wc/pristine-store-test$(EXEEXT) subversion/tests/libsvn_ra_local/ra-local-test$(EXEEXT) subversion/tests/libsvn_ra/ra-test$(EXEEXT) subversion/tests/libsvn_delta/random-test$(EXEEXT) subversion/tests/libsvn_repos/repos-test$(EXEEXT) subversion/tests/libsvn_subr/revision-test$(EXEEXT) subversion/tests/libsvn_subr/skel-test$(EXEEXT) subversion/tests/libsvn_subr/spillbuf-test$(EXEEXT) subversion/tests/libsvn_subr/stream-test$(EXEEXT) subversion/tests/libsvn_subr/string-test$(EXEEXT) subversion/tests/libsvn_subr/subst_translate-test$(EXEEXT) tools/server-side/svnauthz$(EXEEXT) tools/server-side/svnauthz-validate$(EXEEXT) subversion/tests/libsvn_delta/svndiff-test$(EXEEXT) subversion/tests/libsvn_subr/time-test$(EXEEXT) subversion/tests/libsvn_subr/translate-test$(EXEEXT) subversion/tests/libsvn_subr/utf-test$(EXEEXT) subversion/tests/libsvn_delta/vdelta-test$(EXEEXT) subversion/tests/libsvn_wc/wc-incomplete-tester$(EXEEXT) subversion/tests/libsvn_wc/wc-lock-tester$(EXEEXT) subversion/tests/libsvn_wc/wc-queries-test$(EXEEXT) subversion/tests/libsvn_wc/wc-test$(EXEEXT) subversion/tests/libsvn_delta/window-test$(EXEEXT) subversion/tests/cmdline/authz_tests.py subversion/tests/cmdline/autoprop_tests.py subversion/tests/cmdline/basic_tests.py subversion/tests/cmdline/blame_tests.py subversion/tests/cmdline/cat_tests.py subversion/tests/cmdline/changelist_tests.py subversion/tests/cmdline/checkout_tests.py subversion/tests/cmdline/commit_tests.py subversion/tests/cmdline/copy_tests.py subversion/tests/cmdline/depth_tests.py subversion/tests/cmdline/diff_tests.py subversion/tests/cmdline/entries_tests.py subversion/tests/cmdline/export_tests.py subversion/tests/cmdline/externals_tests.py subversion/tests/cmdline/getopt_tests.py subversion/tests/cmdline/history_tests.py subversion/tests/cmdline/import_tests.py subversion/tests/cmdline/info_tests.py subversion/tests/cmdline/input_validation_tests.py subversion/tests/cmdline/iprop_authz_tests.py subversion/tests/cmdline/iprop_tests.py subversion/tests/cmdline/lock_tests.py subversion/tests/cmdline/log_tests.py subversion/tests/cmdline/merge_authz_tests.py subversion/tests/cmdline/merge_automatic_tests.py subversion/tests/cmdline/merge_reintegrate_tests.py subversion/tests/cmdline/merge_tests.py subversion/tests/cmdline/merge_tree_conflict_tests.py subversion/tests/cmdline/mergeinfo_tests.py subversion/tests/cmdline/mod_authz_svn_tests.py subversion/tests/cmdline/move_tests.py subversion/tests/cmdline/patch_tests.py subversion/tests/cmdline/prop_tests.py subversion/tests/cmdline/redirect_tests.py subversion/tests/cmdline/relocate_tests.py subversion/tests/cmdline/resolve_tests.py subversion/tests/cmdline/revert_tests.py subversion/tests/cmdline/schedule_tests.py subversion/tests/cmdline/special_tests.py subversion/tests/cmdline/stat_tests.py subversion/tests/cmdline/svnadmin_tests.py subversion/tests/cmdline/svnauthz_tests.py subversion/tests/cmdline/svndumpfilter_tests.py subversion/tests/cmdline/svnlook_tests.py subversion/tests/cmdline/svnmucc_tests.py subversion/tests/cmdline/svnrdump_tests.py subversion/tests/cmdline/svnsync_authz_tests.py subversion/tests/cmdline/svnsync_tests.py subversion/tests/cmdline/svnversion_tests.py subversion/tests/cmdline/switch_tests.py subversion/tests/cmdline/trans_tests.py subversion/tests/cmdline/tree_conflict_tests.py subversion/tests/cmdline/update_tests.py subversion/tests/cmdline/upgrade_tests.py subversion/tests/cmdline/wc_tests.py
-TEST_PROGRAMS = subversion/tests/libsvn_subr/auth-test$(EXEEXT) subversion/tests/libsvn_subr/cache-test$(EXEEXT) subversion/tests/libsvn_subr/checksum-test$(EXEEXT) subversion/tests/libsvn_client/client-test$(EXEEXT) subversion/tests/libsvn_subr/compat-test$(EXEEXT) subversion/tests/libsvn_subr/config-test$(EXEEXT) subversion/tests/libsvn_wc/conflict-data-test$(EXEEXT) subversion/tests/libsvn_subr/crypto-test$(EXEEXT) subversion/tests/libsvn_wc/db-test$(EXEEXT) subversion/tests/libsvn_diff/diff-diff3-test$(EXEEXT) subversion/tests/libsvn_subr/dirent_uri-test$(EXEEXT) subversion/tests/libsvn_wc/entries-compat-test$(EXEEXT) subversion/tests/libsvn_subr/error-code-test$(EXEEXT) subversion/tests/libsvn_subr/error-test$(EXEEXT) subversion/tests/libsvn_fs_fs/fs-pack-test$(EXEEXT) subversion/tests/libsvn_fs/fs-test$(EXEEXT) subversion/tests/libsvn_subr/hashdump-test$(EXEEXT) subversion/tests/libsvn_subr/io-test$(EXEEXT) subversion/tests/libsvn_fs/locks-test$(EXEEXT) subversion/tests/libsvn_subr/mergeinfo-test$(EXEEXT) subversion/tests/libsvn_subr/named_atomic-test$(EXEEXT) subversion/tests/libsvn_wc/op-depth-test$(EXEEXT) subversion/tests/libsvn_subr/opt-test$(EXEEXT) subversion/tests/libsvn_diff/parse-diff-test$(EXEEXT) subversion/tests/libsvn_subr/path-test$(EXEEXT) subversion/tests/libsvn_wc/pristine-store-test$(EXEEXT) subversion/tests/libsvn_ra_local/ra-local-test$(EXEEXT) subversion/tests/libsvn_ra/ra-test$(EXEEXT) subversion/tests/libsvn_delta/random-test$(EXEEXT) subversion/tests/libsvn_repos/repos-test$(EXEEXT) subversion/tests/libsvn_subr/revision-test$(EXEEXT) subversion/tests/libsvn_subr/skel-test$(EXEEXT) subversion/tests/libsvn_subr/spillbuf-test$(EXEEXT) subversion/tests/libsvn_subr/stream-test$(EXEEXT) subversion/tests/libsvn_subr/string-test$(EXEEXT) subversion/tests/libsvn_subr/subst_translate-test$(EXEEXT) subversion/tests/libsvn_subr/time-test$(EXEEXT) subversion/tests/libsvn_subr/translate-test$(EXEEXT) subversion/tests/libsvn_subr/utf-test$(EXEEXT) subversion/tests/libsvn_wc/wc-queries-test$(EXEEXT) subversion/tests/libsvn_wc/wc-test$(EXEEXT) subversion/tests/libsvn_delta/window-test$(EXEEXT) subversion/tests/cmdline/authz_tests.py subversion/tests/cmdline/autoprop_tests.py subversion/tests/cmdline/basic_tests.py subversion/tests/cmdline/blame_tests.py subversion/tests/cmdline/cat_tests.py subversion/tests/cmdline/changelist_tests.py subversion/tests/cmdline/checkout_tests.py subversion/tests/cmdline/commit_tests.py subversion/tests/cmdline/copy_tests.py subversion/tests/cmdline/depth_tests.py subversion/tests/cmdline/diff_tests.py subversion/tests/cmdline/entries_tests.py subversion/tests/cmdline/export_tests.py subversion/tests/cmdline/externals_tests.py subversion/tests/cmdline/getopt_tests.py subversion/tests/cmdline/history_tests.py subversion/tests/cmdline/import_tests.py subversion/tests/cmdline/info_tests.py subversion/tests/cmdline/input_validation_tests.py subversion/tests/cmdline/iprop_authz_tests.py subversion/tests/cmdline/iprop_tests.py subversion/tests/cmdline/lock_tests.py subversion/tests/cmdline/log_tests.py subversion/tests/cmdline/merge_authz_tests.py subversion/tests/cmdline/merge_automatic_tests.py subversion/tests/cmdline/merge_reintegrate_tests.py subversion/tests/cmdline/merge_tests.py subversion/tests/cmdline/merge_tree_conflict_tests.py subversion/tests/cmdline/mergeinfo_tests.py subversion/tests/cmdline/move_tests.py subversion/tests/cmdline/patch_tests.py subversion/tests/cmdline/prop_tests.py subversion/tests/cmdline/redirect_tests.py subversion/tests/cmdline/relocate_tests.py subversion/tests/cmdline/resolve_tests.py subversion/tests/cmdline/revert_tests.py subversion/tests/cmdline/schedule_tests.py subversion/tests/cmdline/special_tests.py subversion/tests/cmdline/stat_tests.py subversion/tests/cmdline/svnadmin_tests.py subversion/tests/cmdline/svnauthz_tests.py subversion/tests/cmdline/svndumpfilter_tests.py subversion/tests/cmdline/svnlook_tests.py subversion/tests/cmdline/svnmucc_tests.py subversion/tests/cmdline/svnrdump_tests.py subversion/tests/cmdline/svnsync_authz_tests.py subversion/tests/cmdline/svnsync_tests.py subversion/tests/cmdline/svnversion_tests.py subversion/tests/cmdline/switch_tests.py subversion/tests/cmdline/trans_tests.py subversion/tests/cmdline/tree_conflict_tests.py subversion/tests/cmdline/update_tests.py subversion/tests/cmdline/upgrade_tests.py subversion/tests/cmdline/wc_tests.py
+TEST_PROGRAMS = subversion/tests/libsvn_subr/auth-test$(EXEEXT) subversion/tests/libsvn_subr/cache-test$(EXEEXT) subversion/tests/libsvn_subr/checksum-test$(EXEEXT) subversion/tests/libsvn_client/client-test$(EXEEXT) subversion/tests/libsvn_subr/compat-test$(EXEEXT) subversion/tests/libsvn_subr/config-test$(EXEEXT) subversion/tests/libsvn_wc/conflict-data-test$(EXEEXT) subversion/tests/libsvn_subr/crypto-test$(EXEEXT) subversion/tests/libsvn_wc/db-test$(EXEEXT) subversion/tests/libsvn_diff/diff-diff3-test$(EXEEXT) subversion/tests/libsvn_subr/dirent_uri-test$(EXEEXT) subversion/tests/libsvn_wc/entries-compat-test$(EXEEXT) subversion/tests/libsvn_subr/error-code-test$(EXEEXT) subversion/tests/libsvn_subr/error-test$(EXEEXT) subversion/tests/libsvn_fs_fs/fs-pack-test$(EXEEXT) subversion/tests/libsvn_fs/fs-test$(EXEEXT) subversion/tests/libsvn_subr/hashdump-test$(EXEEXT) subversion/tests/libsvn_subr/io-test$(EXEEXT) subversion/tests/libsvn_fs/locks-test$(EXEEXT) subversion/tests/libsvn_subr/mergeinfo-test$(EXEEXT) subversion/tests/libsvn_subr/named_atomic-test$(EXEEXT) subversion/tests/libsvn_wc/op-depth-test$(EXEEXT) subversion/tests/libsvn_subr/opt-test$(EXEEXT) subversion/tests/libsvn_diff/parse-diff-test$(EXEEXT) subversion/tests/libsvn_subr/path-test$(EXEEXT) subversion/tests/libsvn_wc/pristine-store-test$(EXEEXT) subversion/tests/libsvn_ra_local/ra-local-test$(EXEEXT) subversion/tests/libsvn_ra/ra-test$(EXEEXT) subversion/tests/libsvn_delta/random-test$(EXEEXT) subversion/tests/libsvn_repos/repos-test$(EXEEXT) subversion/tests/libsvn_subr/revision-test$(EXEEXT) subversion/tests/libsvn_subr/skel-test$(EXEEXT) subversion/tests/libsvn_subr/spillbuf-test$(EXEEXT) subversion/tests/libsvn_subr/stream-test$(EXEEXT) subversion/tests/libsvn_subr/string-test$(EXEEXT) subversion/tests/libsvn_subr/subst_translate-test$(EXEEXT) subversion/tests/libsvn_subr/time-test$(EXEEXT) subversion/tests/libsvn_subr/translate-test$(EXEEXT) subversion/tests/libsvn_subr/utf-test$(EXEEXT) subversion/tests/libsvn_wc/wc-queries-test$(EXEEXT) subversion/tests/libsvn_wc/wc-test$(EXEEXT) subversion/tests/libsvn_delta/window-test$(EXEEXT) subversion/tests/cmdline/authz_tests.py subversion/tests/cmdline/autoprop_tests.py subversion/tests/cmdline/basic_tests.py subversion/tests/cmdline/blame_tests.py subversion/tests/cmdline/cat_tests.py subversion/tests/cmdline/changelist_tests.py subversion/tests/cmdline/checkout_tests.py subversion/tests/cmdline/commit_tests.py subversion/tests/cmdline/copy_tests.py subversion/tests/cmdline/depth_tests.py subversion/tests/cmdline/diff_tests.py subversion/tests/cmdline/entries_tests.py subversion/tests/cmdline/export_tests.py subversion/tests/cmdline/externals_tests.py subversion/tests/cmdline/getopt_tests.py subversion/tests/cmdline/history_tests.py subversion/tests/cmdline/import_tests.py subversion/tests/cmdline/info_tests.py subversion/tests/cmdline/input_validation_tests.py subversion/tests/cmdline/iprop_authz_tests.py subversion/tests/cmdline/iprop_tests.py subversion/tests/cmdline/lock_tests.py subversion/tests/cmdline/log_tests.py subversion/tests/cmdline/merge_authz_tests.py subversion/tests/cmdline/merge_automatic_tests.py subversion/tests/cmdline/merge_reintegrate_tests.py subversion/tests/cmdline/merge_tests.py subversion/tests/cmdline/merge_tree_conflict_tests.py subversion/tests/cmdline/mergeinfo_tests.py subversion/tests/cmdline/mod_authz_svn_tests.py subversion/tests/cmdline/move_tests.py subversion/tests/cmdline/patch_tests.py subversion/tests/cmdline/prop_tests.py subversion/tests/cmdline/redirect_tests.py subversion/tests/cmdline/relocate_tests.py subversion/tests/cmdline/resolve_tests.py subversion/tests/cmdline/revert_tests.py subversion/tests/cmdline/schedule_tests.py subversion/tests/cmdline/special_tests.py subversion/tests/cmdline/stat_tests.py subversion/tests/cmdline/svnadmin_tests.py subversion/tests/cmdline/svnauthz_tests.py subversion/tests/cmdline/svndumpfilter_tests.py subversion/tests/cmdline/svnlook_tests.py subversion/tests/cmdline/svnmucc_tests.py subversion/tests/cmdline/svnrdump_tests.py subversion/tests/cmdline/svnsync_authz_tests.py subversion/tests/cmdline/svnsync_tests.py subversion/tests/cmdline/svnversion_tests.py subversion/tests/cmdline/switch_tests.py subversion/tests/cmdline/trans_tests.py subversion/tests/cmdline/tree_conflict_tests.py subversion/tests/cmdline/update_tests.py subversion/tests/cmdline/upgrade_tests.py subversion/tests/cmdline/wc_tests.py
check-deps test-deps: subversion/tests/cmdline/atomic-ra-revprop-change$(EXEEXT) subversion/tests/cmdline/entries-dump$(EXEEXT) tools/server-side/svnauthz$(EXEEXT) tools/server-side/svnauthz-validate$(EXEEXT) subversion/tests/libsvn_delta/svndiff-test$(EXEEXT) subversion/tests/libsvn_delta/vdelta-test$(EXEEXT) subversion/tests/libsvn_wc/wc-incomplete-tester$(EXEEXT) subversion/tests/libsvn_wc/wc-lock-tester$(EXEEXT)
MANPAGES = subversion/svn/svn.1 subversion/svnadmin/svnadmin.1 subversion/svndumpfilter/svndumpfilter.1 subversion/svnlook/svnlook.1 subversion/svnmucc/svnmucc.1 subversion/svnrdump/svnrdump.1 subversion/svnserve/svnserve.8 subversion/svnserve/svnserve.conf.5 subversion/svnsync/svnsync.1 subversion/svnversion/svnversion.1
-CLEAN_FILES = subversion/bindings/cxxhl/cxxhl-tests$(EXEEXT) subversion/svn/svn$(EXEEXT) subversion/svnadmin/svnadmin$(EXEEXT) subversion/svndumpfilter/svndumpfilter$(EXEEXT) subversion/svnlook/svnlook$(EXEEXT) subversion/svnmucc/svnmucc$(EXEEXT) subversion/svnrdump/svnrdump$(EXEEXT) subversion/svnserve/svnserve$(EXEEXT) subversion/svnsync/svnsync$(EXEEXT) subversion/svnversion/svnversion$(EXEEXT) subversion/tests/cmdline/atomic-ra-revprop-change$(EXEEXT) subversion/tests/cmdline/authz_tests.pyc subversion/tests/cmdline/autoprop_tests.pyc subversion/tests/cmdline/basic_tests.pyc subversion/tests/cmdline/blame_tests.pyc subversion/tests/cmdline/cat_tests.pyc subversion/tests/cmdline/changelist_tests.pyc subversion/tests/cmdline/checkout_tests.pyc subversion/tests/cmdline/commit_tests.pyc subversion/tests/cmdline/copy_tests.pyc subversion/tests/cmdline/depth_tests.pyc subversion/tests/cmdline/diff_tests.pyc subversion/tests/cmdline/entries-dump$(EXEEXT) subversion/tests/cmdline/entries_tests.pyc subversion/tests/cmdline/export_tests.pyc subversion/tests/cmdline/externals_tests.pyc subversion/tests/cmdline/getopt_tests.pyc subversion/tests/cmdline/history_tests.pyc subversion/tests/cmdline/import_tests.pyc subversion/tests/cmdline/info_tests.pyc subversion/tests/cmdline/input_validation_tests.pyc subversion/tests/cmdline/iprop_authz_tests.pyc subversion/tests/cmdline/iprop_tests.pyc subversion/tests/cmdline/lock_tests.pyc subversion/tests/cmdline/log_tests.pyc subversion/tests/cmdline/merge_authz_tests.pyc subversion/tests/cmdline/merge_automatic_tests.pyc subversion/tests/cmdline/merge_reintegrate_tests.pyc subversion/tests/cmdline/merge_tests.pyc subversion/tests/cmdline/merge_tree_conflict_tests.pyc subversion/tests/cmdline/mergeinfo_tests.pyc subversion/tests/cmdline/move_tests.pyc subversion/tests/cmdline/patch_tests.pyc subversion/tests/cmdline/prop_tests.pyc subversion/tests/cmdline/redirect_tests.pyc subversion/tests/cmdline/relocate_tests.pyc subversion/tests/cmdline/resolve_tests.pyc subversion/tests/cmdline/revert_tests.pyc subversion/tests/cmdline/schedule_tests.pyc subversion/tests/cmdline/special_tests.pyc subversion/tests/cmdline/stat_tests.pyc subversion/tests/cmdline/svnadmin_tests.pyc subversion/tests/cmdline/svnauthz_tests.pyc subversion/tests/cmdline/svndumpfilter_tests.pyc subversion/tests/cmdline/svnlook_tests.pyc subversion/tests/cmdline/svnmucc_tests.pyc subversion/tests/cmdline/svnrdump_tests.pyc subversion/tests/cmdline/svnsync_authz_tests.pyc subversion/tests/cmdline/svnsync_tests.pyc subversion/tests/cmdline/svnversion_tests.pyc subversion/tests/cmdline/switch_tests.pyc subversion/tests/cmdline/trans_tests.pyc subversion/tests/cmdline/tree_conflict_tests.pyc subversion/tests/cmdline/update_tests.pyc subversion/tests/cmdline/upgrade_tests.pyc subversion/tests/cmdline/wc_tests.pyc subversion/tests/libsvn_client/client-test$(EXEEXT) subversion/tests/libsvn_delta/random-test$(EXEEXT) subversion/tests/libsvn_delta/svndiff-test$(EXEEXT) subversion/tests/libsvn_delta/vdelta-test$(EXEEXT) subversion/tests/libsvn_delta/window-test$(EXEEXT) subversion/tests/libsvn_diff/diff-diff3-test$(EXEEXT) subversion/tests/libsvn_diff/parse-diff-test$(EXEEXT) subversion/tests/libsvn_fs/fs-test$(EXEEXT) subversion/tests/libsvn_fs/locks-test$(EXEEXT) subversion/tests/libsvn_fs_base/changes-test$(EXEEXT) subversion/tests/libsvn_fs_base/fs-base-test$(EXEEXT) subversion/tests/libsvn_fs_base/strings-reps-test$(EXEEXT) subversion/tests/libsvn_fs_fs/fs-pack-test$(EXEEXT) subversion/tests/libsvn_ra/ra-test$(EXEEXT) subversion/tests/libsvn_ra_local/ra-local-test$(EXEEXT) subversion/tests/libsvn_repos/repos-test$(EXEEXT) subversion/tests/libsvn_subr/auth-test$(EXEEXT) subversion/tests/libsvn_subr/cache-test$(EXEEXT) subversion/tests/libsvn_subr/checksum-test$(EXEEXT) subversion/tests/libsvn_subr/compat-test$(EXEEXT) subversion/tests/libsvn_subr/config-test$(EXEEXT) subversion/tests/libsvn_subr/crypto-test$(EXEEXT) subversion/tests/libsvn_subr/dirent_uri-test$(EXEEXT) subversion/tests/libsvn_subr/error-code-test$(EXEEXT) subversion/tests/libsvn_subr/error-test$(EXEEXT) subversion/tests/libsvn_subr/hashdump-test$(EXEEXT) subversion/tests/libsvn_subr/io-test$(EXEEXT) subversion/tests/libsvn_subr/mergeinfo-test$(EXEEXT) subversion/tests/libsvn_subr/named_atomic-proc-test$(EXEEXT) subversion/tests/libsvn_subr/named_atomic-test$(EXEEXT) subversion/tests/libsvn_subr/opt-test$(EXEEXT) subversion/tests/libsvn_subr/path-test$(EXEEXT) subversion/tests/libsvn_subr/revision-test$(EXEEXT) subversion/tests/libsvn_subr/skel-test$(EXEEXT) subversion/tests/libsvn_subr/spillbuf-test$(EXEEXT) subversion/tests/libsvn_subr/stream-test$(EXEEXT) subversion/tests/libsvn_subr/string-test$(EXEEXT) subversion/tests/libsvn_subr/subst_translate-test$(EXEEXT) subversion/tests/libsvn_subr/time-test$(EXEEXT) subversion/tests/libsvn_subr/translate-test$(EXEEXT) subversion/tests/libsvn_subr/utf-test$(EXEEXT) subversion/tests/libsvn_wc/conflict-data-test$(EXEEXT) subversion/tests/libsvn_wc/db-test$(EXEEXT) subversion/tests/libsvn_wc/entries-compat-test$(EXEEXT) subversion/tests/libsvn_wc/op-depth-test$(EXEEXT) subversion/tests/libsvn_wc/pristine-store-test$(EXEEXT) subversion/tests/libsvn_wc/wc-incomplete-tester$(EXEEXT) subversion/tests/libsvn_wc/wc-lock-tester$(EXEEXT) subversion/tests/libsvn_wc/wc-queries-test$(EXEEXT) subversion/tests/libsvn_wc/wc-test$(EXEEXT) tools/client-side/svn-bench/svn-bench$(EXEEXT) tools/dev/fsfs-access-map$(EXEEXT) tools/dev/fsfs-reorg$(EXEEXT) tools/dev/svnraisetreeconflict/svnraisetreeconflict$(EXEEXT) tools/diff/diff$(EXEEXT) tools/diff/diff3$(EXEEXT) tools/diff/diff4$(EXEEXT) tools/server-side/fsfs-stats$(EXEEXT) tools/server-side/svn-populate-node-origins-index$(EXEEXT) tools/server-side/svn-rep-sharing-stats$(EXEEXT) tools/server-side/svnauthz$(EXEEXT) tools/server-side/svnauthz-validate$(EXEEXT)
+CLEAN_FILES = subversion/bindings/cxxhl/cxxhl-tests$(EXEEXT) subversion/svn/svn$(EXEEXT) subversion/svnadmin/svnadmin$(EXEEXT) subversion/svndumpfilter/svndumpfilter$(EXEEXT) subversion/svnlook/svnlook$(EXEEXT) subversion/svnmucc/svnmucc$(EXEEXT) subversion/svnrdump/svnrdump$(EXEEXT) subversion/svnserve/svnserve$(EXEEXT) subversion/svnsync/svnsync$(EXEEXT) subversion/svnversion/svnversion$(EXEEXT) subversion/tests/cmdline/atomic-ra-revprop-change$(EXEEXT) subversion/tests/cmdline/authz_tests.pyc subversion/tests/cmdline/autoprop_tests.pyc subversion/tests/cmdline/basic_tests.pyc subversion/tests/cmdline/blame_tests.pyc subversion/tests/cmdline/cat_tests.pyc subversion/tests/cmdline/changelist_tests.pyc subversion/tests/cmdline/checkout_tests.pyc subversion/tests/cmdline/commit_tests.pyc subversion/tests/cmdline/copy_tests.pyc subversion/tests/cmdline/depth_tests.pyc subversion/tests/cmdline/diff_tests.pyc subversion/tests/cmdline/entries-dump$(EXEEXT) subversion/tests/cmdline/entries_tests.pyc subversion/tests/cmdline/export_tests.pyc subversion/tests/cmdline/externals_tests.pyc subversion/tests/cmdline/getopt_tests.pyc subversion/tests/cmdline/history_tests.pyc subversion/tests/cmdline/import_tests.pyc subversion/tests/cmdline/info_tests.pyc subversion/tests/cmdline/input_validation_tests.pyc subversion/tests/cmdline/iprop_authz_tests.pyc subversion/tests/cmdline/iprop_tests.pyc subversion/tests/cmdline/lock_tests.pyc subversion/tests/cmdline/log_tests.pyc subversion/tests/cmdline/merge_authz_tests.pyc subversion/tests/cmdline/merge_automatic_tests.pyc subversion/tests/cmdline/merge_reintegrate_tests.pyc subversion/tests/cmdline/merge_tests.pyc subversion/tests/cmdline/merge_tree_conflict_tests.pyc subversion/tests/cmdline/mergeinfo_tests.pyc subversion/tests/cmdline/mod_authz_svn_tests.pyc subversion/tests/cmdline/move_tests.pyc subversion/tests/cmdline/patch_tests.pyc subversion/tests/cmdline/prop_tests.pyc subversion/tests/cmdline/redirect_tests.pyc subversion/tests/cmdline/relocate_tests.pyc subversion/tests/cmdline/resolve_tests.pyc subversion/tests/cmdline/revert_tests.pyc subversion/tests/cmdline/schedule_tests.pyc subversion/tests/cmdline/special_tests.pyc subversion/tests/cmdline/stat_tests.pyc subversion/tests/cmdline/svnadmin_tests.pyc subversion/tests/cmdline/svnauthz_tests.pyc subversion/tests/cmdline/svndumpfilter_tests.pyc subversion/tests/cmdline/svnlook_tests.pyc subversion/tests/cmdline/svnmucc_tests.pyc subversion/tests/cmdline/svnrdump_tests.pyc subversion/tests/cmdline/svnsync_authz_tests.pyc subversion/tests/cmdline/svnsync_tests.pyc subversion/tests/cmdline/svnversion_tests.pyc subversion/tests/cmdline/switch_tests.pyc subversion/tests/cmdline/trans_tests.pyc subversion/tests/cmdline/tree_conflict_tests.pyc subversion/tests/cmdline/update_tests.pyc subversion/tests/cmdline/upgrade_tests.pyc subversion/tests/cmdline/wc_tests.pyc subversion/tests/libsvn_client/client-test$(EXEEXT) subversion/tests/libsvn_delta/random-test$(EXEEXT) subversion/tests/libsvn_delta/svndiff-test$(EXEEXT) subversion/tests/libsvn_delta/vdelta-test$(EXEEXT) subversion/tests/libsvn_delta/window-test$(EXEEXT) subversion/tests/libsvn_diff/diff-diff3-test$(EXEEXT) subversion/tests/libsvn_diff/parse-diff-test$(EXEEXT) subversion/tests/libsvn_fs/fs-test$(EXEEXT) subversion/tests/libsvn_fs/locks-test$(EXEEXT) subversion/tests/libsvn_fs_base/changes-test$(EXEEXT) subversion/tests/libsvn_fs_base/fs-base-test$(EXEEXT) subversion/tests/libsvn_fs_base/strings-reps-test$(EXEEXT) subversion/tests/libsvn_fs_fs/fs-pack-test$(EXEEXT) subversion/tests/libsvn_ra/ra-test$(EXEEXT) subversion/tests/libsvn_ra_local/ra-local-test$(EXEEXT) subversion/tests/libsvn_repos/repos-test$(EXEEXT) subversion/tests/libsvn_subr/auth-test$(EXEEXT) subversion/tests/libsvn_subr/cache-test$(EXEEXT) subversion/tests/libsvn_subr/checksum-test$(EXEEXT) subversion/tests/libsvn_subr/compat-test$(EXEEXT) subversion/tests/libsvn_subr/config-test$(EXEEXT) subversion/tests/libsvn_subr/crypto-test$(EXEEXT) subversion/tests/libsvn_subr/dirent_uri-test$(EXEEXT) subversion/tests/libsvn_subr/error-code-test$(EXEEXT) subversion/tests/libsvn_subr/error-test$(EXEEXT) subversion/tests/libsvn_subr/hashdump-test$(EXEEXT) subversion/tests/libsvn_subr/io-test$(EXEEXT) subversion/tests/libsvn_subr/mergeinfo-test$(EXEEXT) subversion/tests/libsvn_subr/named_atomic-proc-test$(EXEEXT) subversion/tests/libsvn_subr/named_atomic-test$(EXEEXT) subversion/tests/libsvn_subr/opt-test$(EXEEXT) subversion/tests/libsvn_subr/path-test$(EXEEXT) subversion/tests/libsvn_subr/revision-test$(EXEEXT) subversion/tests/libsvn_subr/skel-test$(EXEEXT) subversion/tests/libsvn_subr/spillbuf-test$(EXEEXT) subversion/tests/libsvn_subr/stream-test$(EXEEXT) subversion/tests/libsvn_subr/string-test$(EXEEXT) subversion/tests/libsvn_subr/subst_translate-test$(EXEEXT) subversion/tests/libsvn_subr/time-test$(EXEEXT) subversion/tests/libsvn_subr/translate-test$(EXEEXT) subversion/tests/libsvn_subr/utf-test$(EXEEXT) subversion/tests/libsvn_wc/conflict-data-test$(EXEEXT) subversion/tests/libsvn_wc/db-test$(EXEEXT) subversion/tests/libsvn_wc/entries-compat-test$(EXEEXT) subversion/tests/libsvn_wc/op-depth-test$(EXEEXT) subversion/tests/libsvn_wc/pristine-store-test$(EXEEXT) subversion/tests/libsvn_wc/wc-incomplete-tester$(EXEEXT) subversion/tests/libsvn_wc/wc-lock-tester$(EXEEXT) subversion/tests/libsvn_wc/wc-queries-test$(EXEEXT) subversion/tests/libsvn_wc/wc-test$(EXEEXT) tools/client-side/svn-bench/svn-bench$(EXEEXT) tools/dev/fsfs-access-map$(EXEEXT) tools/dev/fsfs-reorg$(EXEEXT) tools/dev/svnraisetreeconflict/svnraisetreeconflict$(EXEEXT) tools/diff/diff$(EXEEXT) tools/diff/diff3$(EXEEXT) tools/diff/diff4$(EXEEXT) tools/server-side/fsfs-stats$(EXEEXT) tools/server-side/svn-populate-node-origins-index$(EXEEXT) tools/server-side/svn-rep-sharing-stats$(EXEEXT) tools/server-side/svnauthz$(EXEEXT) tools/server-side/svnauthz-validate$(EXEEXT)
EXTRACLEAN_FILES = subversion/libsvn_fs_fs/rep-cache-db.h subversion/libsvn_subr/internal_statements.h subversion/libsvn_wc/wc-queries.h
SWIG_INCLUDES = -I$(abs_builddir)/subversion \
-I$(abs_srcdir)/subversion/include \
-I$(abs_srcdir)/subversion/bindings/swig \
-I$(abs_srcdir)/subversion/bindings/swig/include \
-I$(abs_srcdir)/subversion/bindings/swig/proxy \
-I$(abs_builddir)/subversion/bindings/swig/proxy \
$(SVN_APR_INCLUDES) $(SVN_APRUTIL_INCLUDES)
RELEASE_MODE = 1
########################################
# Section 2: SWIG headers (wrappers and external runtimes)
########################################
########################################
# Section 3: SWIG autogen rules
########################################
autogen-swig-py: subversion/bindings/swig/python/core.c subversion/bindings/swig/python/svn_client.c subversion/bindings/swig/python/svn_delta.c subversion/bindings/swig/python/svn_diff.c subversion/bindings/swig/python/svn_fs.c subversion/bindings/swig/python/svn_ra.c subversion/bindings/swig/python/svn_repos.c subversion/bindings/swig/python/svn_wc.c
autogen-swig: autogen-swig-py
autogen-swig-pl: subversion/bindings/swig/perl/native/core.c subversion/bindings/swig/perl/native/svn_client.c subversion/bindings/swig/perl/native/svn_delta.c subversion/bindings/swig/perl/native/svn_diff.c subversion/bindings/swig/perl/native/svn_fs.c subversion/bindings/swig/perl/native/svn_ra.c subversion/bindings/swig/perl/native/svn_repos.c subversion/bindings/swig/perl/native/svn_wc.c
autogen-swig: autogen-swig-pl
autogen-swig-rb: subversion/bindings/swig/ruby/core.c subversion/bindings/swig/ruby/svn_client.c subversion/bindings/swig/ruby/svn_delta.c subversion/bindings/swig/ruby/svn_diff.c subversion/bindings/swig/ruby/svn_fs.c subversion/bindings/swig/ruby/svn_ra.c subversion/bindings/swig/ruby/svn_repos.c subversion/bindings/swig/ruby/svn_wc.c
autogen-swig: autogen-swig-rb
########################################
# Section 4: Rules to build SWIG .c files from .i files
########################################
# This needs to be here, rather than in Makefile.in, else
# './autogen.sh --release' doesn't find it.
.swig_checked:
@if [ "$(SWIG)" = "none" ]; then \
echo "SWIG disabled at configure time" >&2; \
exit 1; \
fi
@touch .swig_checked
########################################
# Section 5: Individual target build rules
########################################
atomic_ra_revprop_change_PATH = subversion/tests/cmdline
atomic_ra_revprop_change_DEPS = subversion/tests/cmdline/atomic-ra-revprop-change.lo subversion/libsvn_ra/libsvn_ra-1.la subversion/libsvn_subr/libsvn_subr-1.la
atomic_ra_revprop_change_OBJECTS = atomic-ra-revprop-change.lo
subversion/tests/cmdline/atomic-ra-revprop-change$(EXEEXT): $(atomic_ra_revprop_change_DEPS)
cd subversion/tests/cmdline && $(LINK) $(atomic_ra_revprop_change_LDFLAGS) -o atomic-ra-revprop-change$(EXEEXT) $(atomic_ra_revprop_change_OBJECTS) ../../../subversion/libsvn_ra/libsvn_ra-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
auth_test_PATH = subversion/tests/libsvn_subr
auth_test_DEPS = subversion/tests/libsvn_subr/auth-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la
auth_test_OBJECTS = auth-test.lo
subversion/tests/libsvn_subr/auth-test$(EXEEXT): $(auth_test_DEPS)
cd subversion/tests/libsvn_subr && $(LINK) $(auth_test_LDFLAGS) -o auth-test$(EXEEXT) $(auth_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
cache_test_PATH = subversion/tests/libsvn_subr
cache_test_DEPS = subversion/tests/libsvn_subr/cache-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la
cache_test_OBJECTS = cache-test.lo
subversion/tests/libsvn_subr/cache-test$(EXEEXT): $(cache_test_DEPS)
cd subversion/tests/libsvn_subr && $(LINK) $(cache_test_LDFLAGS) -o cache-test$(EXEEXT) $(cache_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
changes_test_PATH = subversion/tests/libsvn_fs_base
changes_test_DEPS = subversion/tests/libsvn_fs_base/changes-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_fs_base/libsvn_fs_base-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
changes_test_OBJECTS = changes-test.lo
subversion/tests/libsvn_fs_base/changes-test$(EXEEXT): $(changes_test_DEPS)
cd subversion/tests/libsvn_fs_base && $(LINK) $(changes_test_LDFLAGS) -o changes-test$(EXEEXT) $(changes_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_fs/libsvn_fs-1.la ../../../subversion/libsvn_fs_base/libsvn_fs_base-1.la ../../../subversion/libsvn_delta/libsvn_delta-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
checksum_test_PATH = subversion/tests/libsvn_subr
checksum_test_DEPS = subversion/tests/libsvn_subr/checksum-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la
checksum_test_OBJECTS = checksum-test.lo
subversion/tests/libsvn_subr/checksum-test$(EXEEXT): $(checksum_test_DEPS)
cd subversion/tests/libsvn_subr && $(LINK) $(checksum_test_LDFLAGS) -o checksum-test$(EXEEXT) $(checksum_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(SVN_ZLIB_LIBS) $(LIBS)
client_test_PATH = subversion/tests/libsvn_client
client_test_DEPS = subversion/tests/libsvn_client/client-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_client/libsvn_client-1.la subversion/libsvn_wc/libsvn_wc-1.la subversion/libsvn_repos/libsvn_repos-1.la subversion/libsvn_ra/libsvn_ra-1.la subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
client_test_OBJECTS = client-test.lo
subversion/tests/libsvn_client/client-test$(EXEEXT): $(client_test_DEPS)
cd subversion/tests/libsvn_client && $(LINK) $(client_test_LDFLAGS) -o client-test$(EXEEXT) $(client_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_client/libsvn_client-1.la ../../../subversion/libsvn_wc/libsvn_wc-1.la ../../../subversion/libsvn_repos/libsvn_repos-1.la ../../../subversion/libsvn_ra/libsvn_ra-1.la ../../../subversion/libsvn_fs/libsvn_fs-1.la ../../../subversion/libsvn_delta/libsvn_delta-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
compat_test_PATH = subversion/tests/libsvn_subr
compat_test_DEPS = subversion/tests/libsvn_subr/compat-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la
compat_test_OBJECTS = compat-test.lo
subversion/tests/libsvn_subr/compat-test$(EXEEXT): $(compat_test_DEPS)
cd subversion/tests/libsvn_subr && $(LINK) $(compat_test_LDFLAGS) -o compat-test$(EXEEXT) $(compat_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
config_test_PATH = subversion/tests/libsvn_subr
config_test_DEPS = subversion/tests/libsvn_subr/config-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la
config_test_OBJECTS = config-test.lo
subversion/tests/libsvn_subr/config-test$(EXEEXT): $(config_test_DEPS)
cd subversion/tests/libsvn_subr && $(LINK) $(config_test_LDFLAGS) -o config-test$(EXEEXT) $(config_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
conflict_data_test_PATH = subversion/tests/libsvn_wc
conflict_data_test_DEPS = subversion/tests/libsvn_wc/conflict-data-test.lo subversion/tests/libsvn_wc/utils.lo subversion/libsvn_client/libsvn_client-1.la subversion/tests/libsvn_test-1.la subversion/libsvn_wc/libsvn_wc-1.la subversion/libsvn_subr/libsvn_subr-1.la
conflict_data_test_OBJECTS = conflict-data-test.lo utils.lo
subversion/tests/libsvn_wc/conflict-data-test$(EXEEXT): $(conflict_data_test_DEPS)
cd subversion/tests/libsvn_wc && $(LINK) $(conflict_data_test_LDFLAGS) -o conflict-data-test$(EXEEXT) $(conflict_data_test_OBJECTS) ../../../subversion/libsvn_client/libsvn_client-1.la ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_wc/libsvn_wc-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
crypto_test_PATH = subversion/tests/libsvn_subr
crypto_test_DEPS = subversion/tests/libsvn_subr/crypto-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la
crypto_test_OBJECTS = crypto-test.lo
subversion/tests/libsvn_subr/crypto-test$(EXEEXT): $(crypto_test_DEPS)
cd subversion/tests/libsvn_subr && $(LINK) $(crypto_test_LDFLAGS) -o crypto-test$(EXEEXT) $(crypto_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
cxxhl_tests_PATH = subversion/bindings/cxxhl
cxxhl_tests_DEPS = subversion/bindings/cxxhl/tests/test_exception.lo subversion/bindings/cxxhl/libsvncxxhl-1.la subversion/libsvn_subr/libsvn_subr-1.la
cxxhl_tests_OBJECTS = tests/test_exception.lo
subversion/bindings/cxxhl/cxxhl-tests$(EXEEXT): $(cxxhl_tests_DEPS)
cd subversion/bindings/cxxhl && $(LINK_CXX) $(cxxhl_tests_LDFLAGS) -o cxxhl-tests$(EXEEXT) $(cxxhl_tests_OBJECTS) ../../../subversion/bindings/cxxhl/libsvncxxhl-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(LIBS)
db_test_PATH = subversion/tests/libsvn_wc
db_test_DEPS = subversion/tests/libsvn_wc/db-test.lo subversion/tests/libsvn_wc/utils.lo subversion/libsvn_client/libsvn_client-1.la subversion/tests/libsvn_test-1.la subversion/libsvn_wc/libsvn_wc-1.la subversion/libsvn_subr/libsvn_subr-1.la
db_test_OBJECTS = db-test.lo utils.lo
subversion/tests/libsvn_wc/db-test$(EXEEXT): $(db_test_DEPS)
cd subversion/tests/libsvn_wc && $(LINK) $(db_test_LDFLAGS) -o db-test$(EXEEXT) $(db_test_OBJECTS) ../../../subversion/libsvn_client/libsvn_client-1.la ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_wc/libsvn_wc-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
diff_PATH = tools/diff
diff_DEPS = tools/diff/diff.lo subversion/libsvn_diff/libsvn_diff-1.la subversion/libsvn_subr/libsvn_subr-1.la
diff_OBJECTS = diff.lo
tools/diff/diff$(EXEEXT): $(diff_DEPS)
cd tools/diff && $(LINK) $(diff_LDFLAGS) -o diff$(EXEEXT) $(diff_OBJECTS) ../../subversion/libsvn_diff/libsvn_diff-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
diff_diff3_test_PATH = subversion/tests/libsvn_diff
diff_diff3_test_DEPS = subversion/tests/libsvn_diff/diff-diff3-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_diff/libsvn_diff-1.la subversion/libsvn_subr/libsvn_subr-1.la
diff_diff3_test_OBJECTS = diff-diff3-test.lo
subversion/tests/libsvn_diff/diff-diff3-test$(EXEEXT): $(diff_diff3_test_DEPS)
cd subversion/tests/libsvn_diff && $(LINK) $(diff_diff3_test_LDFLAGS) -o diff-diff3-test$(EXEEXT) $(diff_diff3_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_diff/libsvn_diff-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
diff3_PATH = tools/diff
diff3_DEPS = tools/diff/diff3.lo subversion/libsvn_diff/libsvn_diff-1.la subversion/libsvn_subr/libsvn_subr-1.la
diff3_OBJECTS = diff3.lo
tools/diff/diff3$(EXEEXT): $(diff3_DEPS)
cd tools/diff && $(LINK) $(diff3_LDFLAGS) -o diff3$(EXEEXT) $(diff3_OBJECTS) ../../subversion/libsvn_diff/libsvn_diff-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
diff4_PATH = tools/diff
diff4_DEPS = tools/diff/diff4.lo subversion/libsvn_diff/libsvn_diff-1.la subversion/libsvn_subr/libsvn_subr-1.la
diff4_OBJECTS = diff4.lo
tools/diff/diff4$(EXEEXT): $(diff4_DEPS)
cd tools/diff && $(LINK) $(diff4_LDFLAGS) -o diff4$(EXEEXT) $(diff4_OBJECTS) ../../subversion/libsvn_diff/libsvn_diff-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
dirent_uri_test_PATH = subversion/tests/libsvn_subr
dirent_uri_test_DEPS = subversion/tests/libsvn_subr/dirent_uri-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la
dirent_uri_test_OBJECTS = dirent_uri-test.lo
subversion/tests/libsvn_subr/dirent_uri-test$(EXEEXT): $(dirent_uri_test_DEPS)
cd subversion/tests/libsvn_subr && $(LINK) $(dirent_uri_test_LDFLAGS) -o dirent_uri-test$(EXEEXT) $(dirent_uri_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
entries_compat_test_PATH = subversion/tests/libsvn_wc
entries_compat_test_DEPS = subversion/tests/libsvn_wc/entries-compat.lo subversion/tests/libsvn_wc/utils.lo subversion/libsvn_client/libsvn_client-1.la subversion/tests/libsvn_test-1.la subversion/libsvn_wc/libsvn_wc-1.la subversion/libsvn_subr/libsvn_subr-1.la
entries_compat_test_OBJECTS = entries-compat.lo utils.lo
subversion/tests/libsvn_wc/entries-compat-test$(EXEEXT): $(entries_compat_test_DEPS)
cd subversion/tests/libsvn_wc && $(LINK) $(entries_compat_test_LDFLAGS) -o entries-compat-test$(EXEEXT) $(entries_compat_test_OBJECTS) ../../../subversion/libsvn_client/libsvn_client-1.la ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_wc/libsvn_wc-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
entries_dump_PATH = subversion/tests/cmdline
entries_dump_DEPS = subversion/tests/cmdline/entries-dump.lo subversion/libsvn_wc/libsvn_wc-1.la subversion/libsvn_subr/libsvn_subr-1.la
entries_dump_OBJECTS = entries-dump.lo
subversion/tests/cmdline/entries-dump$(EXEEXT): $(entries_dump_DEPS)
cd subversion/tests/cmdline && $(LINK) $(entries_dump_LDFLAGS) -o entries-dump$(EXEEXT) $(entries_dump_OBJECTS) ../../../subversion/libsvn_wc/libsvn_wc-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
error_code_test_PATH = subversion/tests/libsvn_subr
error_code_test_DEPS = subversion/tests/libsvn_subr/error-code-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la
error_code_test_OBJECTS = error-code-test.lo
subversion/tests/libsvn_subr/error-code-test$(EXEEXT): $(error_code_test_DEPS)
cd subversion/tests/libsvn_subr && $(LINK) $(error_code_test_LDFLAGS) -o error-code-test$(EXEEXT) $(error_code_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
error_test_PATH = subversion/tests/libsvn_subr
error_test_DEPS = subversion/tests/libsvn_subr/error-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la
error_test_OBJECTS = error-test.lo
subversion/tests/libsvn_subr/error-test$(EXEEXT): $(error_test_DEPS)
cd subversion/tests/libsvn_subr && $(LINK) $(error_test_LDFLAGS) -o error-test$(EXEEXT) $(error_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
fs_base_test_PATH = subversion/tests/libsvn_fs_base
fs_base_test_DEPS = subversion/tests/libsvn_fs_base/fs-base-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_fs_base/libsvn_fs_base-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_fs_util/libsvn_fs_util-1.la subversion/libsvn_subr/libsvn_subr-1.la
fs_base_test_OBJECTS = fs-base-test.lo
subversion/tests/libsvn_fs_base/fs-base-test$(EXEEXT): $(fs_base_test_DEPS)
cd subversion/tests/libsvn_fs_base && $(LINK) $(fs_base_test_LDFLAGS) -o fs-base-test$(EXEEXT) $(fs_base_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_fs/libsvn_fs-1.la ../../../subversion/libsvn_fs_base/libsvn_fs_base-1.la ../../../subversion/libsvn_delta/libsvn_delta-1.la ../../../subversion/libsvn_fs_util/libsvn_fs_util-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
fs_pack_test_PATH = subversion/tests/libsvn_fs_fs
fs_pack_test_DEPS = subversion/tests/libsvn_fs_fs/fs-pack-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_fs_fs/libsvn_fs_fs-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
fs_pack_test_OBJECTS = fs-pack-test.lo
subversion/tests/libsvn_fs_fs/fs-pack-test$(EXEEXT): $(fs_pack_test_DEPS)
cd subversion/tests/libsvn_fs_fs && $(LINK) $(fs_pack_test_LDFLAGS) -o fs-pack-test$(EXEEXT) $(fs_pack_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_fs/libsvn_fs-1.la ../../../subversion/libsvn_fs_fs/libsvn_fs_fs-1.la ../../../subversion/libsvn_delta/libsvn_delta-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
fs_test_PATH = subversion/tests/libsvn_fs
fs_test_DEPS = subversion/tests/libsvn_fs/fs-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
fs_test_OBJECTS = fs-test.lo
subversion/tests/libsvn_fs/fs-test$(EXEEXT): $(fs_test_DEPS)
cd subversion/tests/libsvn_fs && $(LINK) $(fs_test_LDFLAGS) -o fs-test$(EXEEXT) $(fs_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_fs/libsvn_fs-1.la ../../../subversion/libsvn_delta/libsvn_delta-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
fsfs_access_map_PATH = tools/dev
fsfs_access_map_DEPS = tools/dev/fsfs-access-map.lo subversion/libsvn_subr/libsvn_subr-1.la
fsfs_access_map_OBJECTS = fsfs-access-map.lo
tools/dev/fsfs-access-map$(EXEEXT): $(fsfs_access_map_DEPS)
cd tools/dev && $(LINK) $(fsfs_access_map_LDFLAGS) -o fsfs-access-map$(EXEEXT) $(fsfs_access_map_OBJECTS) ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
fsfs_reorg_PATH = tools/dev
fsfs_reorg_DEPS = tools/dev/fsfs-reorg.lo subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
fsfs_reorg_OBJECTS = fsfs-reorg.lo
tools/dev/fsfs-reorg$(EXEEXT): $(fsfs_reorg_DEPS)
cd tools/dev && $(LINK) $(fsfs_reorg_LDFLAGS) -o fsfs-reorg$(EXEEXT) $(fsfs_reorg_OBJECTS) ../../subversion/libsvn_delta/libsvn_delta-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
fsfs_stats_PATH = tools/server-side
fsfs_stats_DEPS = tools/server-side/fsfs-stats.lo subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
fsfs_stats_OBJECTS = fsfs-stats.lo
tools/server-side/fsfs-stats$(EXEEXT): $(fsfs_stats_DEPS)
cd tools/server-side && $(LINK) $(fsfs_stats_LDFLAGS) -o fsfs-stats$(EXEEXT) $(fsfs_stats_OBJECTS) ../../subversion/libsvn_delta/libsvn_delta-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
hashdump_test_PATH = subversion/tests/libsvn_subr
hashdump_test_DEPS = subversion/tests/libsvn_subr/hashdump-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la
hashdump_test_OBJECTS = hashdump-test.lo
subversion/tests/libsvn_subr/hashdump-test$(EXEEXT): $(hashdump_test_DEPS)
cd subversion/tests/libsvn_subr && $(LINK) $(hashdump_test_LDFLAGS) -o hashdump-test$(EXEEXT) $(hashdump_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
io_test_PATH = subversion/tests/libsvn_subr
io_test_DEPS = subversion/tests/libsvn_subr/io-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la
io_test_OBJECTS = io-test.lo
subversion/tests/libsvn_subr/io-test$(EXEEXT): $(io_test_DEPS)
cd subversion/tests/libsvn_subr && $(LINK) $(io_test_LDFLAGS) -o io-test$(EXEEXT) $(io_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
javahl_callback_javah_PATH = subversion/bindings/javahl/include
javahl_callback_javah_HEADERS = subversion/bindings/javahl/include/org_apache_subversion_javahl_callback_BlameCallback.h subversion/bindings/javahl/include/org_apache_subversion_javahl_callback_ChangelistCallback.h subversion/bindings/javahl/include/org_apache_subversion_javahl_callback_ClientNotifyCallback.h subversion/bindings/javahl/include/org_apache_subversion_javahl_callback_CommitCallback.h subversion/bindings/javahl/include/org_apache_subversion_javahl_callback_CommitMessageCallback.h subversion/bindings/javahl/include/org_apache_subversion_javahl_callback_ConflictResolverCallback.h subversion/bindings/javahl/include/org_apache_subversion_javahl_callback_DiffSummaryCallback.h subversion/bindings/javahl/include/org_apache_subversion_javahl_callback_ImportFilterCallback.h subversion/bindings/javahl/include/org_apache_subversion_javahl_callback_InfoCallback.h subversion/bindings/javahl/include/org_apache_subversion_javahl_callback_InheritedProplistCallback.h subversion/bindings/javahl/include/org_apache_subversion_javahl_callback_ListCallback.h subversion/bindings/javahl/include/org_apache_subversion_javahl_callback_LogMessageCallback.h subversion/bindings/javahl/include/org_apache_subversion_javahl_callback_PatchCallback.h subversion/bindings/javahl/include/org_apache_subversion_javahl_callback_ProgressCallback.h subversion/bindings/javahl/include/org_apache_subversion_javahl_callback_ProplistCallback.h subversion/bindings/javahl/include/org_apache_subversion_javahl_callback_ReposFreezeAction.h subversion/bindings/javahl/include/org_apache_subversion_javahl_callback_ReposNotifyCallback.h subversion/bindings/javahl/include/org_apache_subversion_javahl_callback_StatusCallback.h subversion/bindings/javahl/include/org_apache_subversion_javahl_callback_UserPasswordCallback.h
javahl_callback_javah_OBJECTS =
javahl_callback_javah_DEPS = $(javahl_callback_javah_HEADERS) $(javahl_callback_javah_OBJECTS) $(javahl_java_DEPS)
javahl-callback-javah: $(javahl_callback_javah_DEPS)
javahl_callback_javah_CLASS_FILENAMES = subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/BlameCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ChangelistCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ClientNotifyCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/CommitCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/CommitMessageCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ConflictResolverCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/DiffSummaryCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ImportFilterCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/InfoCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/InheritedProplistCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ListCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/LogMessageCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/PatchCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ProgressCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ProplistCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ReposFreezeAction.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ReposNotifyCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/StatusCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/UserPasswordCallback.class
javahl_callback_javah_CLASSES = org.apache.subversion.javahl.callback.BlameCallback org.apache.subversion.javahl.callback.ChangelistCallback org.apache.subversion.javahl.callback.ClientNotifyCallback org.apache.subversion.javahl.callback.CommitCallback org.apache.subversion.javahl.callback.CommitMessageCallback org.apache.subversion.javahl.callback.ConflictResolverCallback org.apache.subversion.javahl.callback.DiffSummaryCallback org.apache.subversion.javahl.callback.ImportFilterCallback org.apache.subversion.javahl.callback.InfoCallback org.apache.subversion.javahl.callback.InheritedProplistCallback org.apache.subversion.javahl.callback.ListCallback org.apache.subversion.javahl.callback.LogMessageCallback org.apache.subversion.javahl.callback.PatchCallback org.apache.subversion.javahl.callback.ProgressCallback org.apache.subversion.javahl.callback.ProplistCallback org.apache.subversion.javahl.callback.ReposFreezeAction org.apache.subversion.javahl.callback.ReposNotifyCallback org.apache.subversion.javahl.callback.StatusCallback org.apache.subversion.javahl.callback.UserPasswordCallback
$(javahl_callback_javah_HEADERS): $(javahl_callback_javah_CLASS_FILENAMES)
$(COMPILE_JAVAHL_JAVAH) -force -d subversion/bindings/javahl/include -classpath subversion/bindings/javahl/classes:$(javahl_callback_javah_CLASSPATH) $(javahl_callback_javah_CLASSES)
javahl_compat_java_PATH = subversion/bindings/javahl/classes
javahl_compat_java_HEADERS =
javahl_compat_java_OBJECTS = subversion/bindings/javahl/classes/org/tigris/subversion/javahl/BlameCallback.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/BlameCallback2.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/BlameCallback3.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/BlameCallbackImpl.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/ChangePath.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/ChangelistCallback.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/ClientException.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/CommitItem.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/CommitItemStateFlags.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/CommitMessage.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/ConflictDescriptor.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/ConflictResolverCallback.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/ConflictResult.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/ConflictVersion.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/CopySource.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/Depth.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/DiffSummary.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/DiffSummaryReceiver.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/DirEntry.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/ErrorCodes.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/Info.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/Info2.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/InfoCallback.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/InputInterface.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/ListCallback.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/Lock.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/LockStatus.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/LogDate.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/LogMessage.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/LogMessageCallback.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/Mergeinfo.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/MergeinfoLogKind.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/NativeException.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/NodeKind.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/Notify.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/Notify2.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/NotifyAction.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/NotifyInformation.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/NotifyStatus.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/Operation.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/OutputInterface.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/Path.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/ProgressEvent.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/ProgressListener.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/PromptUserPassword.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/PromptUserPassword2.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/PromptUserPassword3.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/PropertyData.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/ProplistCallback.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/ProplistCallbackImpl.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/Revision.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/RevisionKind.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/RevisionRange.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/SVNAdmin.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/SVNClient.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/SVNClientInterface.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/SVNClientLogLevel.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/SVNClientSynchronized.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/SVNInputStream.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/SVNOutputStream.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/ScheduleKind.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/Status.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/StatusCallback.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/StatusKind.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/SubversionException.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/Version.class
javahl_compat_java_DEPS = $(javahl_compat_java_HEADERS) $(javahl_compat_java_OBJECTS) $(javahl_java_DEPS)
javahl-compat-java: $(javahl_compat_java_DEPS)
javahl_compat_java_SRC = $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/BlameCallback.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/BlameCallback2.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/BlameCallback3.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/BlameCallbackImpl.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/ChangePath.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/ChangelistCallback.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/ClientException.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/CommitItem.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/CommitItemStateFlags.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/CommitMessage.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/ConflictDescriptor.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/ConflictResolverCallback.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/ConflictResult.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/ConflictVersion.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/CopySource.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/Depth.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/DiffSummary.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/DiffSummaryReceiver.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/DirEntry.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/ErrorCodes.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/Info.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/Info2.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/InfoCallback.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/InputInterface.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/ListCallback.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/Lock.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/LockStatus.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/LogDate.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/LogMessage.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/LogMessageCallback.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/Mergeinfo.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/MergeinfoLogKind.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/NativeException.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/NodeKind.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/Notify.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/Notify2.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/NotifyAction.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/NotifyInformation.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/NotifyStatus.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/Operation.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/OutputInterface.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/Path.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/ProgressEvent.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/ProgressListener.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/PromptUserPassword.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/PromptUserPassword2.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/PromptUserPassword3.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/PropertyData.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/ProplistCallback.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/ProplistCallbackImpl.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/Revision.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/RevisionKind.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/RevisionRange.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/SVNAdmin.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/SVNClient.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/SVNClientInterface.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/SVNClientLogLevel.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/SVNClientSynchronized.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/SVNInputStream.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/SVNOutputStream.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/ScheduleKind.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/Status.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/StatusCallback.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/StatusKind.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/SubversionException.java $(abs_srcdir)/subversion/bindings/javahl/src/org/tigris/subversion/javahl/Version.java
$(javahl_compat_java_OBJECTS): $(javahl_compat_java_SRC)
$(COMPILE_JAVAHL_JAVAC) -d subversion/bindings/javahl/classes -classpath subversion/bindings/javahl/classes:$(javahl_compat_java_CLASSPATH) $(javahl_compat_java_SRC)
javahl_compat_tests_PATH = subversion/bindings/javahl/classes
javahl_compat_tests_HEADERS =
javahl_compat_tests_OBJECTS = subversion/bindings/javahl/classes/org/tigris/subversion/javahl/BasicTests.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/RunTests.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/SVNAdminTests.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/SVNTests.class subversion/bindings/javahl/classes/org/tigris/subversion/javahl/WC.class
javahl_compat_tests_DEPS = $(javahl_compat_tests_HEADERS) $(javahl_compat_tests_OBJECTS) $(javahl_compat_java_DEPS)
javahl-compat-tests: $(javahl_compat_tests_DEPS)
javahl_compat_tests_SRC = $(abs_srcdir)/subversion/bindings/javahl/tests/org/tigris/subversion/javahl/BasicTests.java $(abs_srcdir)/subversion/bindings/javahl/tests/org/tigris/subversion/javahl/RunTests.java $(abs_srcdir)/subversion/bindings/javahl/tests/org/tigris/subversion/javahl/SVNAdminTests.java $(abs_srcdir)/subversion/bindings/javahl/tests/org/tigris/subversion/javahl/SVNTests.java $(abs_srcdir)/subversion/bindings/javahl/tests/org/tigris/subversion/javahl/WC.java
$(javahl_compat_tests_OBJECTS): $(javahl_compat_tests_SRC)
$(COMPILE_JAVAHL_JAVAC) -d subversion/bindings/javahl/classes -classpath subversion/bindings/javahl/classes:$(javahl_compat_tests_CLASSPATH) $(javahl_compat_tests_SRC)
javahl_java_PATH = subversion/bindings/javahl/classes
javahl_java_HEADERS =
javahl_java_OBJECTS = subversion/bindings/javahl/classes/org/apache/subversion/javahl/ClientException.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/ClientNotifyInformation.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/CommitInfo.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/CommitItem.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/CommitItemStateFlags.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/ConflictDescriptor.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/ConflictResult.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/DiffSummary.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/ISVNClient.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/ISVNRepos.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/JNIError.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/NativeException.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/NativeResources.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/ProgressEvent.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/ReposNotifyInformation.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/SVNClient.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/SVNRepos.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/SubversionException.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/BlameCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ChangelistCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ClientNotifyCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/CommitCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/CommitMessageCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ConflictResolverCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/DiffSummaryCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ImportFilterCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/InfoCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/InheritedProplistCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ListCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/LogMessageCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/PatchCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ProgressCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ProplistCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ReposFreezeAction.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ReposNotifyCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/StatusCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/UserPasswordCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/ChangePath.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Checksum.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/ConflictVersion.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/CopySource.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Depth.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/DiffOptions.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/DirEntry.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Info.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Lock.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/LogDate.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Mergeinfo.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/NodeKind.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Property.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Revision.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/RevisionRange.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Status.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Tristate.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Version.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/VersionExtended.class
javahl_java_DEPS = $(javahl_java_HEADERS) $(javahl_java_OBJECTS)
javahl-java: $(javahl_java_DEPS)
javahl_java_SRC = $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/ClientException.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/ClientNotifyInformation.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/CommitInfo.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/CommitItem.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/CommitItemStateFlags.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/ConflictDescriptor.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/ConflictResult.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/DiffSummary.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNClient.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNRepos.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/JNIError.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeException.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeResources.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/ProgressEvent.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/ReposNotifyInformation.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/SVNClient.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/SVNRepos.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/SubversionException.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/BlameCallback.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/ChangelistCallback.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/ClientNotifyCallback.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/CommitCallback.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/CommitMessageCallback.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/ConflictResolverCallback.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/DiffSummaryCallback.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/ImportFilterCallback.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/InfoCallback.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/InheritedProplistCallback.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/ListCallback.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/LogMessageCallback.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/PatchCallback.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/ProgressCallback.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/ProplistCallback.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/ReposFreezeAction.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/ReposNotifyCallback.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/StatusCallback.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/UserPasswordCallback.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/ChangePath.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/Checksum.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/ConflictVersion.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/CopySource.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/Depth.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/DiffOptions.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/DirEntry.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/Info.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/Lock.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/LogDate.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/Mergeinfo.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/NodeKind.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/Property.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/Revision.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/RevisionRange.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/Status.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/Tristate.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/Version.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/VersionExtended.java
$(javahl_java_OBJECTS): $(javahl_java_SRC)
$(COMPILE_JAVAHL_JAVAC) -d subversion/bindings/javahl/classes -classpath subversion/bindings/javahl/classes:$(javahl_java_CLASSPATH) $(javahl_java_SRC)
javahl_javah_PATH = subversion/bindings/javahl/include
javahl_javah_HEADERS = subversion/bindings/javahl/include/org_apache_subversion_javahl_ClientException.h subversion/bindings/javahl/include/org_apache_subversion_javahl_ClientNotifyInformation.h subversion/bindings/javahl/include/org_apache_subversion_javahl_CommitInfo.h subversion/bindings/javahl/include/org_apache_subversion_javahl_CommitItem.h subversion/bindings/javahl/include/org_apache_subversion_javahl_CommitItemStateFlags.h subversion/bindings/javahl/include/org_apache_subversion_javahl_ConflictDescriptor.h subversion/bindings/javahl/include/org_apache_subversion_javahl_ConflictResult.h subversion/bindings/javahl/include/org_apache_subversion_javahl_DiffSummary.h subversion/bindings/javahl/include/org_apache_subversion_javahl_ISVNClient.h subversion/bindings/javahl/include/org_apache_subversion_javahl_ISVNRepos.h subversion/bindings/javahl/include/org_apache_subversion_javahl_JNIError.h subversion/bindings/javahl/include/org_apache_subversion_javahl_NativeException.h subversion/bindings/javahl/include/org_apache_subversion_javahl_NativeResources.h subversion/bindings/javahl/include/org_apache_subversion_javahl_ProgressEvent.h subversion/bindings/javahl/include/org_apache_subversion_javahl_ReposNotifyInformation.h subversion/bindings/javahl/include/org_apache_subversion_javahl_SVNClient.h subversion/bindings/javahl/include/org_apache_subversion_javahl_SVNRepos.h subversion/bindings/javahl/include/org_apache_subversion_javahl_SubversionException.h
javahl_javah_OBJECTS =
javahl_javah_DEPS = $(javahl_javah_HEADERS) $(javahl_javah_OBJECTS) $(javahl_java_DEPS)
javahl-javah: $(javahl_javah_DEPS)
javahl_javah_CLASS_FILENAMES = subversion/bindings/javahl/classes/org/apache/subversion/javahl/ClientException.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/ClientNotifyInformation.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/CommitInfo.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/CommitItem.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/CommitItemStateFlags.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/ConflictDescriptor.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/ConflictResult.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/DiffSummary.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/ISVNClient.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/ISVNRepos.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/JNIError.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/NativeException.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/NativeResources.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/ProgressEvent.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/ReposNotifyInformation.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/SVNClient.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/SVNRepos.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/SubversionException.class
javahl_javah_CLASSES = org.apache.subversion.javahl.ClientException org.apache.subversion.javahl.ClientNotifyInformation org.apache.subversion.javahl.CommitInfo org.apache.subversion.javahl.CommitItem org.apache.subversion.javahl.CommitItemStateFlags org.apache.subversion.javahl.ConflictDescriptor org.apache.subversion.javahl.ConflictResult org.apache.subversion.javahl.DiffSummary org.apache.subversion.javahl.ISVNClient org.apache.subversion.javahl.ISVNRepos org.apache.subversion.javahl.JNIError org.apache.subversion.javahl.NativeException org.apache.subversion.javahl.NativeResources org.apache.subversion.javahl.ProgressEvent org.apache.subversion.javahl.ReposNotifyInformation org.apache.subversion.javahl.SVNClient org.apache.subversion.javahl.SVNRepos org.apache.subversion.javahl.SubversionException
$(javahl_javah_HEADERS): $(javahl_javah_CLASS_FILENAMES)
$(COMPILE_JAVAHL_JAVAH) -force -d subversion/bindings/javahl/include -classpath subversion/bindings/javahl/classes:$(javahl_javah_CLASSPATH) $(javahl_javah_CLASSES)
javahl_tests_PATH = subversion/bindings/javahl/classes
javahl_tests_HEADERS =
javahl_tests_OBJECTS = subversion/bindings/javahl/classes/org/apache/subversion/javahl/BasicTests.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/RunTests.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/SVNReposTests.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/SVNTests.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/WC.class
javahl_tests_DEPS = $(javahl_tests_HEADERS) $(javahl_tests_OBJECTS) $(javahl_java_DEPS)
javahl-tests: $(javahl_tests_DEPS)
javahl_tests_SRC = $(abs_srcdir)/subversion/bindings/javahl/tests/org/apache/subversion/javahl/BasicTests.java $(abs_srcdir)/subversion/bindings/javahl/tests/org/apache/subversion/javahl/RunTests.java $(abs_srcdir)/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNReposTests.java $(abs_srcdir)/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNTests.java $(abs_srcdir)/subversion/bindings/javahl/tests/org/apache/subversion/javahl/WC.java
$(javahl_tests_OBJECTS): $(javahl_tests_SRC)
$(COMPILE_JAVAHL_JAVAC) -d subversion/bindings/javahl/classes -classpath subversion/bindings/javahl/classes:$(javahl_tests_CLASSPATH) $(javahl_tests_SRC)
javahl_types_javah_PATH = subversion/bindings/javahl/include
javahl_types_javah_HEADERS = subversion/bindings/javahl/include/org_apache_subversion_javahl_types_ChangePath.h subversion/bindings/javahl/include/org_apache_subversion_javahl_types_Checksum.h subversion/bindings/javahl/include/org_apache_subversion_javahl_types_ConflictVersion.h subversion/bindings/javahl/include/org_apache_subversion_javahl_types_CopySource.h subversion/bindings/javahl/include/org_apache_subversion_javahl_types_Depth.h subversion/bindings/javahl/include/org_apache_subversion_javahl_types_DiffOptions.h subversion/bindings/javahl/include/org_apache_subversion_javahl_types_DirEntry.h subversion/bindings/javahl/include/org_apache_subversion_javahl_types_Info.h subversion/bindings/javahl/include/org_apache_subversion_javahl_types_Lock.h subversion/bindings/javahl/include/org_apache_subversion_javahl_types_LogDate.h subversion/bindings/javahl/include/org_apache_subversion_javahl_types_Mergeinfo.h subversion/bindings/javahl/include/org_apache_subversion_javahl_types_NodeKind.h subversion/bindings/javahl/include/org_apache_subversion_javahl_types_Property.h subversion/bindings/javahl/include/org_apache_subversion_javahl_types_Revision.h subversion/bindings/javahl/include/org_apache_subversion_javahl_types_RevisionRange.h subversion/bindings/javahl/include/org_apache_subversion_javahl_types_Status.h subversion/bindings/javahl/include/org_apache_subversion_javahl_types_Tristate.h subversion/bindings/javahl/include/org_apache_subversion_javahl_types_Version.h subversion/bindings/javahl/include/org_apache_subversion_javahl_types_VersionExtended.h
javahl_types_javah_OBJECTS =
javahl_types_javah_DEPS = $(javahl_types_javah_HEADERS) $(javahl_types_javah_OBJECTS) $(javahl_java_DEPS)
javahl-types-javah: $(javahl_types_javah_DEPS)
javahl_types_javah_CLASS_FILENAMES = subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/ChangePath.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Checksum.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/ConflictVersion.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/CopySource.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Depth.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/DiffOptions.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/DirEntry.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Info.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Lock.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/LogDate.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Mergeinfo.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/NodeKind.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Property.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Revision.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/RevisionRange.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Status.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Tristate.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Version.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/VersionExtended.class
javahl_types_javah_CLASSES = org.apache.subversion.javahl.types.ChangePath org.apache.subversion.javahl.types.Checksum org.apache.subversion.javahl.types.ConflictVersion org.apache.subversion.javahl.types.CopySource org.apache.subversion.javahl.types.Depth org.apache.subversion.javahl.types.DiffOptions org.apache.subversion.javahl.types.DirEntry org.apache.subversion.javahl.types.Info org.apache.subversion.javahl.types.Lock org.apache.subversion.javahl.types.LogDate org.apache.subversion.javahl.types.Mergeinfo org.apache.subversion.javahl.types.NodeKind org.apache.subversion.javahl.types.Property org.apache.subversion.javahl.types.Revision org.apache.subversion.javahl.types.RevisionRange org.apache.subversion.javahl.types.Status org.apache.subversion.javahl.types.Tristate org.apache.subversion.javahl.types.Version org.apache.subversion.javahl.types.VersionExtended
$(javahl_types_javah_HEADERS): $(javahl_types_javah_CLASS_FILENAMES)
$(COMPILE_JAVAHL_JAVAH) -force -d subversion/bindings/javahl/include -classpath subversion/bindings/javahl/classes:$(javahl_types_javah_CLASSPATH) $(javahl_types_javah_CLASSES)
libsvn_auth_gnome_keyring_PATH = subversion/libsvn_auth_gnome_keyring
libsvn_auth_gnome_keyring_DEPS = subversion/libsvn_auth_gnome_keyring/gnome_keyring.lo subversion/libsvn_auth_gnome_keyring/version.lo subversion/libsvn_subr/libsvn_subr-1.la
libsvn_auth_gnome_keyring_OBJECTS = gnome_keyring.lo version.lo
subversion/libsvn_auth_gnome_keyring/libsvn_auth_gnome_keyring-1.la: $(libsvn_auth_gnome_keyring_DEPS)
cd subversion/libsvn_auth_gnome_keyring && $(LINK_LIB) $(libsvn_auth_gnome_keyring_LDFLAGS) -o libsvn_auth_gnome_keyring-1.la $(LT_NO_UNDEFINED) $(libsvn_auth_gnome_keyring_OBJECTS) ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(SVN_GNOME_KEYRING_LIBS) $(LIBS)
libsvn_auth_kwallet_PATH = subversion/libsvn_auth_kwallet
libsvn_auth_kwallet_DEPS = subversion/libsvn_auth_kwallet/kwallet.lo subversion/libsvn_auth_kwallet/version.lo subversion/libsvn_subr/libsvn_subr-1.la
libsvn_auth_kwallet_OBJECTS = kwallet.lo version.lo
subversion/libsvn_auth_kwallet/libsvn_auth_kwallet-1.la: $(libsvn_auth_kwallet_DEPS)
cd subversion/libsvn_auth_kwallet && $(LINK_CXX_LIB) $(libsvn_auth_kwallet_LDFLAGS) -o libsvn_auth_kwallet-1.la $(LT_NO_UNDEFINED) $(libsvn_auth_kwallet_OBJECTS) ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(SVN_KWALLET_LIBS) $(LIBS)
libsvn_client_PATH = subversion/libsvn_client
libsvn_client_DEPS = subversion/libsvn_client/add.lo subversion/libsvn_client/blame.lo subversion/libsvn_client/cat.lo subversion/libsvn_client/changelist.lo subversion/libsvn_client/checkout.lo subversion/libsvn_client/cleanup.lo subversion/libsvn_client/cmdline.lo subversion/libsvn_client/commit.lo subversion/libsvn_client/commit_util.lo subversion/libsvn_client/compat_providers.lo subversion/libsvn_client/copy.lo subversion/libsvn_client/copy_foreign.lo subversion/libsvn_client/ctx.lo subversion/libsvn_client/delete.lo subversion/libsvn_client/deprecated.lo subversion/libsvn_client/diff.lo subversion/libsvn_client/diff_local.lo subversion/libsvn_client/diff_summarize.lo subversion/libsvn_client/export.lo subversion/libsvn_client/externals.lo subversion/libsvn_client/import.lo subversion/libsvn_client/info.lo subversion/libsvn_client/iprops.lo subversion/libsvn_client/list.lo subversion/libsvn_client/locking_commands.lo subversion/libsvn_client/log.lo subversion/libsvn_client/merge.lo subversion/libsvn_client/mergeinfo.lo subversion/libsvn_client/patch.lo subversion/libsvn_client/prop_commands.lo subversion/libsvn_client/ra.lo subversion/libsvn_client/relocate.lo subversion/libsvn_client/repos_diff.lo subversion/libsvn_client/resolved.lo subversion/libsvn_client/revert.lo subversion/libsvn_client/revisions.lo subversion/libsvn_client/status.lo subversion/libsvn_client/switch.lo subversion/libsvn_client/update.lo subversion/libsvn_client/upgrade.lo subversion/libsvn_client/url.lo subversion/libsvn_client/util.lo subversion/libsvn_client/version.lo subversion/libsvn_wc/libsvn_wc-1.la subversion/libsvn_ra/libsvn_ra-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_diff/libsvn_diff-1.la subversion/libsvn_subr/libsvn_subr-1.la
libsvn_client_OBJECTS = add.lo blame.lo cat.lo changelist.lo checkout.lo cleanup.lo cmdline.lo commit.lo commit_util.lo compat_providers.lo copy.lo copy_foreign.lo ctx.lo delete.lo deprecated.lo diff.lo diff_local.lo diff_summarize.lo export.lo externals.lo import.lo info.lo iprops.lo list.lo locking_commands.lo log.lo merge.lo mergeinfo.lo patch.lo prop_commands.lo ra.lo relocate.lo repos_diff.lo resolved.lo revert.lo revisions.lo status.lo switch.lo update.lo upgrade.lo url.lo util.lo version.lo
subversion/libsvn_client/libsvn_client-1.la: $(libsvn_client_DEPS)
cd subversion/libsvn_client && $(LINK_LIB) $(libsvn_client_LDFLAGS) -o libsvn_client-1.la $(LT_NO_UNDEFINED) $(libsvn_client_OBJECTS) ../../subversion/libsvn_wc/libsvn_wc-1.la ../../subversion/libsvn_ra/libsvn_ra-1.la ../../subversion/libsvn_delta/libsvn_delta-1.la ../../subversion/libsvn_diff/libsvn_diff-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
libsvn_delta_PATH = subversion/libsvn_delta
libsvn_delta_DEPS = subversion/libsvn_delta/cancel.lo subversion/libsvn_delta/compat.lo subversion/libsvn_delta/compose_delta.lo subversion/libsvn_delta/debug_editor.lo subversion/libsvn_delta/default_editor.lo subversion/libsvn_delta/deprecated.lo subversion/libsvn_delta/depth_filter_editor.lo subversion/libsvn_delta/editor.lo subversion/libsvn_delta/path_driver.lo subversion/libsvn_delta/svndiff.lo subversion/libsvn_delta/text_delta.lo subversion/libsvn_delta/version.lo subversion/libsvn_delta/xdelta.lo subversion/libsvn_subr/libsvn_subr-1.la
libsvn_delta_OBJECTS = cancel.lo compat.lo compose_delta.lo debug_editor.lo default_editor.lo deprecated.lo depth_filter_editor.lo editor.lo path_driver.lo svndiff.lo text_delta.lo version.lo xdelta.lo
subversion/libsvn_delta/libsvn_delta-1.la: $(libsvn_delta_DEPS)
cd subversion/libsvn_delta && $(LINK_LIB) $(libsvn_delta_LDFLAGS) -o libsvn_delta-1.la $(LT_NO_UNDEFINED) $(libsvn_delta_OBJECTS) ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(SVN_ZLIB_LIBS) $(LIBS)
libsvn_diff_PATH = subversion/libsvn_diff
libsvn_diff_DEPS = subversion/libsvn_diff/deprecated.lo subversion/libsvn_diff/diff.lo subversion/libsvn_diff/diff3.lo subversion/libsvn_diff/diff4.lo subversion/libsvn_diff/diff_file.lo subversion/libsvn_diff/diff_memory.lo subversion/libsvn_diff/diff_tree.lo subversion/libsvn_diff/lcs.lo subversion/libsvn_diff/parse-diff.lo subversion/libsvn_diff/token.lo subversion/libsvn_diff/util.lo subversion/libsvn_subr/libsvn_subr-1.la
libsvn_diff_OBJECTS = deprecated.lo diff.lo diff3.lo diff4.lo diff_file.lo diff_memory.lo diff_tree.lo lcs.lo parse-diff.lo token.lo util.lo
subversion/libsvn_diff/libsvn_diff-1.la: $(libsvn_diff_DEPS)
cd subversion/libsvn_diff && $(LINK_LIB) $(libsvn_diff_LDFLAGS) -o libsvn_diff-1.la $(LT_NO_UNDEFINED) $(libsvn_diff_OBJECTS) ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(SVN_ZLIB_LIBS) $(LIBS)
libsvn_fs_PATH = subversion/libsvn_fs
install-ramod-lib: $(SVN_FS_LIB_INSTALL_DEPS)
libsvn_fs_DEPS = $(SVN_FS_LIB_DEPS) subversion/libsvn_fs/access.lo subversion/libsvn_fs/editor.lo subversion/libsvn_fs/fs-loader.lo subversion/libsvn_fs_util/libsvn_fs_util-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
libsvn_fs_OBJECTS = access.lo editor.lo fs-loader.lo
subversion/libsvn_fs/libsvn_fs-1.la: $(libsvn_fs_DEPS)
cd subversion/libsvn_fs && $(LINK_LIB) $(libsvn_fs_LDFLAGS) -o libsvn_fs-1.la $(LT_NO_UNDEFINED) $(libsvn_fs_OBJECTS) ../../subversion/libsvn_fs_util/libsvn_fs_util-1.la ../../subversion/libsvn_delta/libsvn_delta-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_FS_LIB_LINK) $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
libsvn_fs_base_PATH = subversion/libsvn_fs_base
libsvn_fs_base_DEPS = subversion/libsvn_fs_base/bdb/bdb-err.lo subversion/libsvn_fs_base/bdb/bdb_compat.lo subversion/libsvn_fs_base/bdb/changes-table.lo subversion/libsvn_fs_base/bdb/checksum-reps-table.lo subversion/libsvn_fs_base/bdb/copies-table.lo subversion/libsvn_fs_base/bdb/dbt.lo subversion/libsvn_fs_base/bdb/env.lo subversion/libsvn_fs_base/bdb/lock-tokens-table.lo subversion/libsvn_fs_base/bdb/locks-table.lo subversion/libsvn_fs_base/bdb/miscellaneous-table.lo subversion/libsvn_fs_base/bdb/node-origins-table.lo subversion/libsvn_fs_base/bdb/nodes-table.lo subversion/libsvn_fs_base/bdb/reps-table.lo subversion/libsvn_fs_base/bdb/rev-table.lo subversion/libsvn_fs_base/bdb/strings-table.lo subversion/libsvn_fs_base/bdb/txn-table.lo subversion/libsvn_fs_base/bdb/uuids-table.lo subversion/libsvn_fs_base/dag.lo subversion/libsvn_fs_base/err.lo subversion/libsvn_fs_base/fs.lo subversion/libsvn_fs_base/id.lo subversion/libsvn_fs_base/key-gen.lo subversion/libsvn_fs_base/lock.lo subversion/libsvn_fs_base/node-rev.lo subversion/libsvn_fs_base/reps-strings.lo subversion/libsvn_fs_base/revs-txns.lo subversion/libsvn_fs_base/trail.lo subversion/libsvn_fs_base/tree.lo subversion/libsvn_fs_base/util/fs_skels.lo subversion/libsvn_fs_base/uuid.lo subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/libsvn_fs_util/libsvn_fs_util-1.la
libsvn_fs_base_OBJECTS = bdb/bdb-err.lo bdb/bdb_compat.lo bdb/changes-table.lo bdb/checksum-reps-table.lo bdb/copies-table.lo bdb/dbt.lo bdb/env.lo bdb/lock-tokens-table.lo bdb/locks-table.lo bdb/miscellaneous-table.lo bdb/node-origins-table.lo bdb/nodes-table.lo bdb/reps-table.lo bdb/rev-table.lo bdb/strings-table.lo bdb/txn-table.lo bdb/uuids-table.lo dag.lo err.lo fs.lo id.lo key-gen.lo lock.lo node-rev.lo reps-strings.lo revs-txns.lo trail.lo tree.lo util/fs_skels.lo uuid.lo
subversion/libsvn_fs_base/libsvn_fs_base-1.la: $(libsvn_fs_base_DEPS)
cd subversion/libsvn_fs_base && $(LINK_LIB) $(libsvn_fs_base_LDFLAGS) -o libsvn_fs_base-1.la $(LT_NO_UNDEFINED) $(libsvn_fs_base_OBJECTS) ../../subversion/libsvn_delta/libsvn_delta-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(SVN_DB_LIBS) ../../subversion/libsvn_fs_util/libsvn_fs_util-1.la $(LIBS)
libsvn_fs_fs_PATH = subversion/libsvn_fs_fs
libsvn_fs_fs_DEPS = subversion/libsvn_fs_fs/caching.lo subversion/libsvn_fs_fs/dag.lo subversion/libsvn_fs_fs/fs.lo subversion/libsvn_fs_fs/fs_fs.lo subversion/libsvn_fs_fs/id.lo subversion/libsvn_fs_fs/key-gen.lo subversion/libsvn_fs_fs/lock.lo subversion/libsvn_fs_fs/rep-cache.lo subversion/libsvn_fs_fs/temp_serializer.lo subversion/libsvn_fs_fs/tree.lo subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/libsvn_fs_util/libsvn_fs_util-1.la
libsvn_fs_fs_OBJECTS = caching.lo dag.lo fs.lo fs_fs.lo id.lo key-gen.lo lock.lo rep-cache.lo temp_serializer.lo tree.lo
subversion/libsvn_fs_fs/libsvn_fs_fs-1.la: $(libsvn_fs_fs_DEPS)
cd subversion/libsvn_fs_fs && $(LINK_LIB) $(libsvn_fs_fs_LDFLAGS) -o libsvn_fs_fs-1.la $(LT_NO_UNDEFINED) $(libsvn_fs_fs_OBJECTS) ../../subversion/libsvn_delta/libsvn_delta-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) ../../subversion/libsvn_fs_util/libsvn_fs_util-1.la $(LIBS)
libsvn_fs_util_PATH = subversion/libsvn_fs_util
libsvn_fs_util_DEPS = subversion/libsvn_fs_util/fs-util.lo subversion/libsvn_subr/libsvn_subr-1.la
libsvn_fs_util_OBJECTS = fs-util.lo
subversion/libsvn_fs_util/libsvn_fs_util-1.la: $(libsvn_fs_util_DEPS)
cd subversion/libsvn_fs_util && $(LINK_LIB) $(libsvn_fs_util_LDFLAGS) -o libsvn_fs_util-1.la $(LT_NO_UNDEFINED) $(libsvn_fs_util_OBJECTS) ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
libsvn_ra_PATH = subversion/libsvn_ra
install-lib: $(SVN_RA_LIB_INSTALL_DEPS)
libsvn_ra_DEPS = $(SVN_RA_LIB_DEPS) subversion/libsvn_ra/compat.lo subversion/libsvn_ra/debug_reporter.lo subversion/libsvn_ra/deprecated.lo subversion/libsvn_ra/editor.lo subversion/libsvn_ra/ra_loader.lo subversion/libsvn_ra/util.lo subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
libsvn_ra_OBJECTS = compat.lo debug_reporter.lo deprecated.lo editor.lo ra_loader.lo util.lo
subversion/libsvn_ra/libsvn_ra-1.la: $(libsvn_ra_DEPS)
cd subversion/libsvn_ra && $(LINK_LIB) $(libsvn_ra_LDFLAGS) -o libsvn_ra-1.la $(LT_NO_UNDEFINED) $(libsvn_ra_OBJECTS) ../../subversion/libsvn_delta/libsvn_delta-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_RA_LIB_LINK) $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
libsvn_ra_local_PATH = subversion/libsvn_ra_local
libsvn_ra_local_DEPS = subversion/libsvn_ra_local/ra_plugin.lo subversion/libsvn_ra_local/split_url.lo subversion/libsvn_repos/libsvn_repos-1.la subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
libsvn_ra_local_OBJECTS = ra_plugin.lo split_url.lo
subversion/libsvn_ra_local/libsvn_ra_local-1.la: $(libsvn_ra_local_DEPS)
cd subversion/libsvn_ra_local && $(LINK_LIB) $(libsvn_ra_local_LDFLAGS) -o libsvn_ra_local-1.la $(LT_NO_UNDEFINED) $(libsvn_ra_local_OBJECTS) ../../subversion/libsvn_repos/libsvn_repos-1.la ../../subversion/libsvn_fs/libsvn_fs-1.la ../../subversion/libsvn_delta/libsvn_delta-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
libsvn_ra_serf_PATH = subversion/libsvn_ra_serf
libsvn_ra_serf_DEPS = subversion/libsvn_ra_serf/blame.lo subversion/libsvn_ra_serf/blncache.lo subversion/libsvn_ra_serf/commit.lo subversion/libsvn_ra_serf/get_deleted_rev.lo subversion/libsvn_ra_serf/getdate.lo subversion/libsvn_ra_serf/getlocations.lo subversion/libsvn_ra_serf/getlocationsegments.lo subversion/libsvn_ra_serf/getlocks.lo subversion/libsvn_ra_serf/inherited_props.lo subversion/libsvn_ra_serf/locks.lo subversion/libsvn_ra_serf/log.lo subversion/libsvn_ra_serf/merge.lo subversion/libsvn_ra_serf/mergeinfo.lo subversion/libsvn_ra_serf/options.lo subversion/libsvn_ra_serf/property.lo subversion/libsvn_ra_serf/replay.lo subversion/libsvn_ra_serf/sb_bucket.lo subversion/libsvn_ra_serf/serf.lo subversion/libsvn_ra_serf/update.lo subversion/libsvn_ra_serf/util.lo subversion/libsvn_ra_serf/util_error.lo subversion/libsvn_ra_serf/xml.lo subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
libsvn_ra_serf_OBJECTS = blame.lo blncache.lo commit.lo get_deleted_rev.lo getdate.lo getlocations.lo getlocationsegments.lo getlocks.lo inherited_props.lo locks.lo log.lo merge.lo mergeinfo.lo options.lo property.lo replay.lo sb_bucket.lo serf.lo update.lo util.lo util_error.lo xml.lo
subversion/libsvn_ra_serf/libsvn_ra_serf-1.la: $(libsvn_ra_serf_DEPS)
cd subversion/libsvn_ra_serf && $(LINK_LIB) $(libsvn_ra_serf_LDFLAGS) -o libsvn_ra_serf-1.la $(LT_NO_UNDEFINED) $(libsvn_ra_serf_OBJECTS) ../../subversion/libsvn_delta/libsvn_delta-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(SVN_SERF_LIBS) $(SVN_XML_LIBS) $(LIBS)
libsvn_ra_svn_PATH = subversion/libsvn_ra_svn
libsvn_ra_svn_DEPS = subversion/libsvn_ra_svn/client.lo subversion/libsvn_ra_svn/cram.lo subversion/libsvn_ra_svn/cyrus_auth.lo subversion/libsvn_ra_svn/deprecated.lo subversion/libsvn_ra_svn/editorp.lo subversion/libsvn_ra_svn/internal_auth.lo subversion/libsvn_ra_svn/marshal.lo subversion/libsvn_ra_svn/streams.lo subversion/libsvn_ra_svn/version.lo subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
libsvn_ra_svn_OBJECTS = client.lo cram.lo cyrus_auth.lo deprecated.lo editorp.lo internal_auth.lo marshal.lo streams.lo version.lo
subversion/libsvn_ra_svn/libsvn_ra_svn-1.la: $(libsvn_ra_svn_DEPS)
cd subversion/libsvn_ra_svn && $(LINK_LIB) $(libsvn_ra_svn_LDFLAGS) -o libsvn_ra_svn-1.la $(LT_NO_UNDEFINED) $(libsvn_ra_svn_OBJECTS) ../../subversion/libsvn_delta/libsvn_delta-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(SVN_SASL_LIBS) $(LIBS)
libsvn_repos_PATH = subversion/libsvn_repos
libsvn_repos_DEPS = subversion/libsvn_repos/authz.lo subversion/libsvn_repos/commit.lo subversion/libsvn_repos/delta.lo subversion/libsvn_repos/deprecated.lo subversion/libsvn_repos/dump.lo subversion/libsvn_repos/fs-wrap.lo subversion/libsvn_repos/hooks.lo subversion/libsvn_repos/load-fs-vtable.lo subversion/libsvn_repos/load.lo subversion/libsvn_repos/log.lo subversion/libsvn_repos/node_tree.lo subversion/libsvn_repos/notify.lo subversion/libsvn_repos/replay.lo subversion/libsvn_repos/reporter.lo subversion/libsvn_repos/repos.lo subversion/libsvn_repos/rev_hunt.lo subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
libsvn_repos_OBJECTS = authz.lo commit.lo delta.lo deprecated.lo dump.lo fs-wrap.lo hooks.lo load-fs-vtable.lo load.lo log.lo node_tree.lo notify.lo replay.lo reporter.lo repos.lo rev_hunt.lo
subversion/libsvn_repos/libsvn_repos-1.la: $(libsvn_repos_DEPS)
cd subversion/libsvn_repos && $(LINK_LIB) $(libsvn_repos_LDFLAGS) -o libsvn_repos-1.la $(LT_NO_UNDEFINED) $(libsvn_repos_OBJECTS) ../../subversion/libsvn_fs/libsvn_fs-1.la ../../subversion/libsvn_delta/libsvn_delta-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
libsvn_subr_PATH = subversion/libsvn_subr
libsvn_subr_DEPS = subversion/libsvn_subr/adler32.lo subversion/libsvn_subr/atomic.lo subversion/libsvn_subr/auth.lo subversion/libsvn_subr/base64.lo subversion/libsvn_subr/cache-inprocess.lo subversion/libsvn_subr/cache-membuffer.lo subversion/libsvn_subr/cache-memcache.lo subversion/libsvn_subr/cache.lo subversion/libsvn_subr/cache_config.lo subversion/libsvn_subr/checksum.lo subversion/libsvn_subr/cmdline.lo subversion/libsvn_subr/compat.lo subversion/libsvn_subr/config.lo subversion/libsvn_subr/config_auth.lo subversion/libsvn_subr/config_file.lo subversion/libsvn_subr/config_win.lo subversion/libsvn_subr/crypto.lo subversion/libsvn_subr/ctype.lo subversion/libsvn_subr/date.lo subversion/libsvn_subr/debug.lo subversion/libsvn_subr/deprecated.lo subversion/libsvn_subr/dirent_uri.lo subversion/libsvn_subr/dso.lo subversion/libsvn_subr/eol.lo subversion/libsvn_subr/error.lo subversion/libsvn_subr/gpg_agent.lo subversion/libsvn_subr/hash.lo subversion/libsvn_subr/io.lo subversion/libsvn_subr/iter.lo subversion/libsvn_subr/lock.lo subversion/libsvn_subr/log.lo subversion/libsvn_subr/macos_keychain.lo subversion/libsvn_subr/magic.lo subversion/libsvn_subr/md5.lo subversion/libsvn_subr/mergeinfo.lo subversion/libsvn_subr/mutex.lo subversion/libsvn_subr/named_atomic.lo subversion/libsvn_subr/nls.lo subversion/libsvn_subr/opt.lo subversion/libsvn_subr/path.lo subversion/libsvn_subr/pool.lo subversion/libsvn_subr/prompt.lo subversion/libsvn_subr/properties.lo subversion/libsvn_subr/pseudo_md5.lo subversion/libsvn_subr/quoprint.lo subversion/libsvn_subr/sha1.lo subversion/libsvn_subr/simple_providers.lo subversion/libsvn_subr/skel.lo subversion/libsvn_subr/sorts.lo subversion/libsvn_subr/spillbuf.lo subversion/libsvn_subr/sqlite.lo subversion/libsvn_subr/sqlite3wrapper.lo subversion/libsvn_subr/ssl_client_cert_providers.lo subversion/libsvn_subr/ssl_client_cert_pw_providers.lo subversion/libsvn_subr/ssl_server_trust_providers.lo subversion/libsvn_subr/stream.lo subversion/libsvn_subr/string.lo subversion/libsvn_subr/subst.lo subversion/libsvn_subr/sysinfo.lo subversion/libsvn_subr/target.lo subversion/libsvn_subr/temp_serializer.lo subversion/libsvn_subr/time.lo subversion/libsvn_subr/token.lo subversion/libsvn_subr/types.lo subversion/libsvn_subr/user.lo subversion/libsvn_subr/username_providers.lo subversion/libsvn_subr/utf.lo subversion/libsvn_subr/utf_validate.lo subversion/libsvn_subr/utf_width.lo subversion/libsvn_subr/validate.lo subversion/libsvn_subr/version.lo subversion/libsvn_subr/win32_crashrpt.lo subversion/libsvn_subr/win32_crypto.lo subversion/libsvn_subr/win32_xlate.lo subversion/libsvn_subr/xml.lo
libsvn_subr_OBJECTS = adler32.lo atomic.lo auth.lo base64.lo cache-inprocess.lo cache-membuffer.lo cache-memcache.lo cache.lo cache_config.lo checksum.lo cmdline.lo compat.lo config.lo config_auth.lo config_file.lo config_win.lo crypto.lo ctype.lo date.lo debug.lo deprecated.lo dirent_uri.lo dso.lo eol.lo error.lo gpg_agent.lo hash.lo io.lo iter.lo lock.lo log.lo macos_keychain.lo magic.lo md5.lo mergeinfo.lo mutex.lo named_atomic.lo nls.lo opt.lo path.lo pool.lo prompt.lo properties.lo pseudo_md5.lo quoprint.lo sha1.lo simple_providers.lo skel.lo sorts.lo spillbuf.lo sqlite.lo sqlite3wrapper.lo ssl_client_cert_providers.lo ssl_client_cert_pw_providers.lo ssl_server_trust_providers.lo stream.lo string.lo subst.lo sysinfo.lo target.lo temp_serializer.lo time.lo token.lo types.lo user.lo username_providers.lo utf.lo utf_validate.lo utf_width.lo validate.lo version.lo win32_crashrpt.lo win32_crypto.lo win32_xlate.lo xml.lo
subversion/libsvn_subr/libsvn_subr-1.la: $(libsvn_subr_DEPS)
cd subversion/libsvn_subr && $(LINK_LIB) $(libsvn_subr_LDFLAGS) -o libsvn_subr-1.la $(LT_NO_UNDEFINED) $(libsvn_subr_OBJECTS) $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(SVN_XML_LIBS) $(SVN_ZLIB_LIBS) $(SVN_APR_MEMCACHE_LIBS) $(SVN_SQLITE_LIBS) $(SVN_MAGIC_LIBS) $(LIBS)
libsvn_swig_perl_PATH = subversion/bindings/swig/perl/libsvn_swig_perl
libsvn_swig_perl_DEPS = subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.lo subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
libsvn_swig_perl_OBJECTS = swigutil_pl.lo
subversion/bindings/swig/perl/libsvn_swig_perl/libsvn_swig_perl-1.la: $(libsvn_swig_perl_DEPS)
cd subversion/bindings/swig/perl/libsvn_swig_perl && $(LINK_LIB) $(libsvn_swig_perl_LDFLAGS) -o libsvn_swig_perl-1.la $(LT_NO_UNDEFINED) $(libsvn_swig_perl_OBJECTS) ../../../../../subversion/libsvn_delta/libsvn_delta-1.la ../../../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
libsvn_swig_py_PATH = subversion/bindings/swig/python/libsvn_swig_py
libsvn_swig_py_DEPS = subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.lo subversion/libsvn_client/libsvn_client-1.la subversion/libsvn_wc/libsvn_wc-1.la subversion/libsvn_ra/libsvn_ra-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
libsvn_swig_py_OBJECTS = swigutil_py.lo
subversion/bindings/swig/python/libsvn_swig_py/libsvn_swig_py-1.la: $(libsvn_swig_py_DEPS)
cd subversion/bindings/swig/python/libsvn_swig_py && $(LINK) $(libsvn_swig_py_LDFLAGS) -o libsvn_swig_py-1.la $(LT_NO_UNDEFINED) $(libsvn_swig_py_OBJECTS) ../../../../../subversion/libsvn_client/libsvn_client-1.la ../../../../../subversion/libsvn_wc/libsvn_wc-1.la ../../../../../subversion/libsvn_ra/libsvn_ra-1.la ../../../../../subversion/libsvn_delta/libsvn_delta-1.la ../../../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
libsvn_swig_ruby_PATH = subversion/bindings/swig/ruby/libsvn_swig_ruby
libsvn_swig_ruby_DEPS = subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.lo subversion/libsvn_client/libsvn_client-1.la subversion/libsvn_wc/libsvn_wc-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
libsvn_swig_ruby_OBJECTS = swigutil_rb.lo
subversion/bindings/swig/ruby/libsvn_swig_ruby/libsvn_swig_ruby-1.la: $(libsvn_swig_ruby_DEPS)
cd subversion/bindings/swig/ruby/libsvn_swig_ruby && $(LINK) $(SWIG_RB_LIBS) $(libsvn_swig_ruby_LDFLAGS) -o libsvn_swig_ruby-1.la $(LT_NO_UNDEFINED) $(libsvn_swig_ruby_OBJECTS) ../../../../../subversion/libsvn_client/libsvn_client-1.la ../../../../../subversion/libsvn_wc/libsvn_wc-1.la ../../../../../subversion/libsvn_delta/libsvn_delta-1.la ../../../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
libsvn_test_PATH = subversion/tests
libsvn_test_DEPS = subversion/tests/svn_test_fs.lo subversion/tests/svn_test_main.lo subversion/libsvn_repos/libsvn_repos-1.la subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
libsvn_test_OBJECTS = svn_test_fs.lo svn_test_main.lo
subversion/tests/libsvn_test-1.la: $(libsvn_test_DEPS)
cd subversion/tests && $(LINK_LIB) $(libsvn_test_LDFLAGS) -o libsvn_test-1.la $(libsvn_test_OBJECTS) ../../subversion/libsvn_repos/libsvn_repos-1.la ../../subversion/libsvn_fs/libsvn_fs-1.la ../../subversion/libsvn_delta/libsvn_delta-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
libsvn_wc_PATH = subversion/libsvn_wc
libsvn_wc_DEPS = subversion/libsvn_wc/adm_crawler.lo subversion/libsvn_wc/adm_files.lo subversion/libsvn_wc/adm_ops.lo subversion/libsvn_wc/ambient_depth_filter_editor.lo subversion/libsvn_wc/cleanup.lo subversion/libsvn_wc/conflicts.lo subversion/libsvn_wc/context.lo subversion/libsvn_wc/copy.lo subversion/libsvn_wc/crop.lo subversion/libsvn_wc/delete.lo subversion/libsvn_wc/deprecated.lo subversion/libsvn_wc/diff_editor.lo subversion/libsvn_wc/diff_local.lo subversion/libsvn_wc/entries.lo subversion/libsvn_wc/externals.lo subversion/libsvn_wc/info.lo subversion/libsvn_wc/lock.lo subversion/libsvn_wc/merge.lo subversion/libsvn_wc/node.lo subversion/libsvn_wc/old-and-busted.lo subversion/libsvn_wc/props.lo subversion/libsvn_wc/questions.lo subversion/libsvn_wc/relocate.lo subversion/libsvn_wc/revert.lo subversion/libsvn_wc/revision_status.lo subversion/libsvn_wc/status.lo subversion/libsvn_wc/translate.lo subversion/libsvn_wc/tree_conflicts.lo subversion/libsvn_wc/update_editor.lo subversion/libsvn_wc/upgrade.lo subversion/libsvn_wc/util.lo subversion/libsvn_wc/wc_db.lo subversion/libsvn_wc/wc_db_pristine.lo subversion/libsvn_wc/wc_db_update_move.lo subversion/libsvn_wc/wc_db_util.lo subversion/libsvn_wc/wc_db_wcroot.lo subversion/libsvn_wc/wcroot_anchor.lo subversion/libsvn_wc/workqueue.lo subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_diff/libsvn_diff-1.la subversion/libsvn_subr/libsvn_subr-1.la
libsvn_wc_OBJECTS = adm_crawler.lo adm_files.lo adm_ops.lo ambient_depth_filter_editor.lo cleanup.lo conflicts.lo context.lo copy.lo crop.lo delete.lo deprecated.lo diff_editor.lo diff_local.lo entries.lo externals.lo info.lo lock.lo merge.lo node.lo old-and-busted.lo props.lo questions.lo relocate.lo revert.lo revision_status.lo status.lo translate.lo tree_conflicts.lo update_editor.lo upgrade.lo util.lo wc_db.lo wc_db_pristine.lo wc_db_update_move.lo wc_db_util.lo wc_db_wcroot.lo wcroot_anchor.lo workqueue.lo
subversion/libsvn_wc/libsvn_wc-1.la: $(libsvn_wc_DEPS)
cd subversion/libsvn_wc && $(LINK_LIB) $(libsvn_wc_LDFLAGS) -o libsvn_wc-1.la $(LT_NO_UNDEFINED) $(libsvn_wc_OBJECTS) ../../subversion/libsvn_delta/libsvn_delta-1.la ../../subversion/libsvn_diff/libsvn_diff-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
libsvncxxhl_PATH = subversion/bindings/cxxhl
libsvncxxhl_DEPS = subversion/bindings/cxxhl/src/exception.lo subversion/bindings/cxxhl/src/tristate.lo subversion/libsvn_repos/libsvn_repos-1.la subversion/libsvn_client/libsvn_client-1.la subversion/libsvn_wc/libsvn_wc-1.la subversion/libsvn_ra/libsvn_ra-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_diff/libsvn_diff-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/libsvn_fs/libsvn_fs-1.la
libsvncxxhl_OBJECTS = src/exception.lo src/tristate.lo
subversion/bindings/cxxhl/libsvncxxhl-1.la: $(libsvncxxhl_DEPS)
cd subversion/bindings/cxxhl && $(LINK_CXX_LIB) $(libsvncxxhl_LDFLAGS) -o libsvncxxhl-1.la $(LT_NO_UNDEFINED) $(libsvncxxhl_OBJECTS) ../../../subversion/libsvn_repos/libsvn_repos-1.la ../../../subversion/libsvn_client/libsvn_client-1.la ../../../subversion/libsvn_wc/libsvn_wc-1.la ../../../subversion/libsvn_ra/libsvn_ra-1.la ../../../subversion/libsvn_delta/libsvn_delta-1.la ../../../subversion/libsvn_diff/libsvn_diff-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la ../../../subversion/libsvn_fs/libsvn_fs-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
libsvnjavahl_PATH = subversion/bindings/javahl/native
libsvnjavahl_DEPS = $(javahl_javah_DEPS) $(javahl_java_DEPS) $(javahl_callback_javah_DEPS) $(javahl_types_javah_DEPS) subversion/bindings/javahl/native/Array.lo subversion/bindings/javahl/native/BlameCallback.lo subversion/bindings/javahl/native/ChangelistCallback.lo subversion/bindings/javahl/native/ClientContext.lo subversion/bindings/javahl/native/CommitCallback.lo subversion/bindings/javahl/native/CommitMessage.lo subversion/bindings/javahl/native/CopySources.lo subversion/bindings/javahl/native/CreateJ.lo subversion/bindings/javahl/native/DiffOptions.lo subversion/bindings/javahl/native/DiffSummaryReceiver.lo subversion/bindings/javahl/native/EnumMapper.lo subversion/bindings/javahl/native/File.lo subversion/bindings/javahl/native/ImportFilterCallback.lo subversion/bindings/javahl/native/InfoCallback.lo subversion/bindings/javahl/native/InputStream.lo subversion/bindings/javahl/native/JNIByteArray.lo subversion/bindings/javahl/native/JNICriticalSection.lo subversion/bindings/javahl/native/JNIMutex.lo subversion/bindings/javahl/native/JNIStackElement.lo subversion/bindings/javahl/native/JNIStringHolder.lo subversion/bindings/javahl/native/JNIThreadData.lo subversion/bindings/javahl/native/JNIUtil.lo subversion/bindings/javahl/native/ListCallback.lo subversion/bindings/javahl/native/LogMessageCallback.lo subversion/bindings/javahl/native/MessageReceiver.lo subversion/bindings/javahl/native/OutputStream.lo subversion/bindings/javahl/native/PatchCallback.lo subversion/bindings/javahl/native/Path.lo subversion/bindings/javahl/native/Pool.lo subversion/bindings/javahl/native/Prompter.lo subversion/bindings/javahl/native/ProplistCallback.lo subversion/bindings/javahl/native/ReposFreezeAction.lo subversion/bindings/javahl/native/ReposNotifyCallback.lo subversion/bindings/javahl/native/Revision.lo subversion/bindings/javahl/native/RevisionRange.lo subversion/bindings/javahl/native/RevpropTable.lo subversion/bindings/javahl/native/SVNBase.lo subversion/bindings/javahl/native/SVNClient.lo subversion/bindings/javahl/native/SVNRepos.lo subversion/bindings/javahl/native/StatusCallback.lo subversion/bindings/javahl/native/StringArray.lo subversion/bindings/javahl/native/Targets.lo subversion/bindings/javahl/native/VersionExtended.lo subversion/bindings/javahl/native/libsvnjavahl.la.lo subversion/bindings/javahl/native/org_apache_subversion_javahl_NativeResources.lo subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNClient.lo subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNRepos.lo subversion/bindings/javahl/native/org_apache_subversion_javahl_types_Version.lo subversion/bindings/javahl/native/org_apache_subversion_javahl_types_VersionExtended.lo subversion/libsvn_repos/libsvn_repos-1.la subversion/libsvn_client/libsvn_client-1.la subversion/libsvn_wc/libsvn_wc-1.la subversion/libsvn_ra/libsvn_ra-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_diff/libsvn_diff-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/libsvn_fs/libsvn_fs-1.la
libsvnjavahl_OBJECTS = Array.lo BlameCallback.lo ChangelistCallback.lo ClientContext.lo CommitCallback.lo CommitMessage.lo CopySources.lo CreateJ.lo DiffOptions.lo DiffSummaryReceiver.lo EnumMapper.lo File.lo ImportFilterCallback.lo InfoCallback.lo InputStream.lo JNIByteArray.lo JNICriticalSection.lo JNIMutex.lo JNIStackElement.lo JNIStringHolder.lo JNIThreadData.lo JNIUtil.lo ListCallback.lo LogMessageCallback.lo MessageReceiver.lo OutputStream.lo PatchCallback.lo Path.lo Pool.lo Prompter.lo ProplistCallback.lo ReposFreezeAction.lo ReposNotifyCallback.lo Revision.lo RevisionRange.lo RevpropTable.lo SVNBase.lo SVNClient.lo SVNRepos.lo StatusCallback.lo StringArray.lo Targets.lo VersionExtended.lo libsvnjavahl.la.lo org_apache_subversion_javahl_NativeResources.lo org_apache_subversion_javahl_SVNClient.lo org_apache_subversion_javahl_SVNRepos.lo org_apache_subversion_javahl_types_Version.lo org_apache_subversion_javahl_types_VersionExtended.lo
subversion/bindings/javahl/native/libsvnjavahl-1.la: $(libsvnjavahl_DEPS)
cd subversion/bindings/javahl/native && $(LINK_JAVAHL_CXX) $(libsvnjavahl_LDFLAGS) -o libsvnjavahl-1.la $(LT_NO_UNDEFINED) $(libsvnjavahl_OBJECTS) ../../../../subversion/libsvn_repos/libsvn_repos-1.la ../../../../subversion/libsvn_client/libsvn_client-1.la ../../../../subversion/libsvn_wc/libsvn_wc-1.la ../../../../subversion/libsvn_ra/libsvn_ra-1.la ../../../../subversion/libsvn_delta/libsvn_delta-1.la ../../../../subversion/libsvn_diff/libsvn_diff-1.la ../../../../subversion/libsvn_subr/libsvn_subr-1.la ../../../../subversion/libsvn_fs/libsvn_fs-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
locale_PATH = subversion/po
locale_DEPS = subversion/po/de.mo subversion/po/es.mo subversion/po/fr.mo subversion/po/it.mo subversion/po/ja.mo subversion/po/ko.mo subversion/po/nb.mo subversion/po/pl.mo subversion/po/pt_BR.mo subversion/po/sv.mo subversion/po/zh_CN.mo subversion/po/zh_TW.mo
locale: $(locale_DEPS)
locks_test_PATH = subversion/tests/libsvn_fs
locks_test_DEPS = subversion/tests/libsvn_fs/locks-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
locks_test_OBJECTS = locks-test.lo
subversion/tests/libsvn_fs/locks-test$(EXEEXT): $(locks_test_DEPS)
cd subversion/tests/libsvn_fs && $(LINK) $(locks_test_LDFLAGS) -o locks-test$(EXEEXT) $(locks_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_fs/libsvn_fs-1.la ../../../subversion/libsvn_delta/libsvn_delta-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
mergeinfo_test_PATH = subversion/tests/libsvn_subr
mergeinfo_test_DEPS = subversion/tests/libsvn_subr/mergeinfo-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la
mergeinfo_test_OBJECTS = mergeinfo-test.lo
subversion/tests/libsvn_subr/mergeinfo-test$(EXEEXT): $(mergeinfo_test_DEPS)
cd subversion/tests/libsvn_subr && $(LINK) $(mergeinfo_test_LDFLAGS) -o mergeinfo-test$(EXEEXT) $(mergeinfo_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
mod_authz_svn_PATH = subversion/mod_authz_svn
mod_authz_svn_DEPS = subversion/mod_authz_svn/mod_authz_svn.lo subversion/libsvn_repos/libsvn_repos-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/mod_dav_svn/mod_dav_svn.la
mod_authz_svn_OBJECTS = mod_authz_svn.lo
subversion/mod_authz_svn/mod_authz_svn.la: $(mod_authz_svn_DEPS)
if $(INSTALL_APACHE_MODS) ; then cd subversion/mod_authz_svn && $(LINK_APACHE_MOD) $(mod_authz_svn_LDFLAGS) -o mod_authz_svn.la $(LT_NO_UNDEFINED) $(mod_authz_svn_OBJECTS) ../../subversion/libsvn_repos/libsvn_repos-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la $(LIBS) ; else echo "fake" > subversion/mod_authz_svn/mod_authz_svn.la ; fi
mod_dav_svn_PATH = subversion/mod_dav_svn
mod_dav_svn_DEPS = subversion/mod_dav_svn/activity.lo subversion/mod_dav_svn/authz.lo subversion/mod_dav_svn/deadprops.lo subversion/mod_dav_svn/liveprops.lo subversion/mod_dav_svn/lock.lo subversion/mod_dav_svn/merge.lo subversion/mod_dav_svn/mirror.lo subversion/mod_dav_svn/mod_dav_svn.lo subversion/mod_dav_svn/posts/create_txn.lo subversion/mod_dav_svn/reports/dated-rev.lo subversion/mod_dav_svn/reports/deleted-rev.lo subversion/mod_dav_svn/reports/file-revs.lo subversion/mod_dav_svn/reports/get-location-segments.lo subversion/mod_dav_svn/reports/get-locations.lo subversion/mod_dav_svn/reports/get-locks.lo subversion/mod_dav_svn/reports/inherited-props.lo subversion/mod_dav_svn/reports/log.lo subversion/mod_dav_svn/reports/mergeinfo.lo subversion/mod_dav_svn/reports/replay.lo subversion/mod_dav_svn/reports/update.lo subversion/mod_dav_svn/repos.lo subversion/mod_dav_svn/util.lo subversion/mod_dav_svn/version.lo subversion/libsvn_repos/libsvn_repos-1.la subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
mod_dav_svn_OBJECTS = activity.lo authz.lo deadprops.lo liveprops.lo lock.lo merge.lo mirror.lo mod_dav_svn.lo posts/create_txn.lo reports/dated-rev.lo reports/deleted-rev.lo reports/file-revs.lo reports/get-location-segments.lo reports/get-locations.lo reports/get-locks.lo reports/inherited-props.lo reports/log.lo reports/mergeinfo.lo reports/replay.lo reports/update.lo repos.lo util.lo version.lo
subversion/mod_dav_svn/mod_dav_svn.la: $(mod_dav_svn_DEPS)
if $(INSTALL_APACHE_MODS) ; then cd subversion/mod_dav_svn && $(LINK_APACHE_MOD) $(mod_dav_svn_LDFLAGS) -o mod_dav_svn.la $(LT_NO_UNDEFINED) $(mod_dav_svn_OBJECTS) ../../subversion/libsvn_repos/libsvn_repos-1.la ../../subversion/libsvn_fs/libsvn_fs-1.la ../../subversion/libsvn_delta/libsvn_delta-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la $(LIBS) ; else echo "fake" > subversion/mod_dav_svn/mod_dav_svn.la ; fi
mod_dontdothat_PATH = tools/server-side/mod_dontdothat
mod_dontdothat_DEPS = tools/server-side/mod_dontdothat/mod_dontdothat.lo subversion/libsvn_subr/libsvn_subr-1.la subversion/mod_dav_svn/mod_dav_svn.la
mod_dontdothat_OBJECTS = mod_dontdothat.lo
tools/server-side/mod_dontdothat/mod_dontdothat.la: $(mod_dontdothat_DEPS)
if $(INSTALL_APACHE_MODS) ; then cd tools/server-side/mod_dontdothat && $(LINK_APACHE_MOD) $(mod_dontdothat_LDFLAGS) -o mod_dontdothat.la $(LT_NO_UNDEFINED) $(mod_dontdothat_OBJECTS) ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_XML_LIBS) $(LIBS) ; else echo "fake" > tools/server-side/mod_dontdothat/mod_dontdothat.la ; fi
named_atomic_proc_test_PATH = subversion/tests/libsvn_subr
named_atomic_proc_test_DEPS = subversion/tests/libsvn_subr/named_atomic-test-proc.lo subversion/libsvn_subr/libsvn_subr-1.la
named_atomic_proc_test_OBJECTS = named_atomic-test-proc.lo
subversion/tests/libsvn_subr/named_atomic-proc-test$(EXEEXT): $(named_atomic_proc_test_DEPS)
cd subversion/tests/libsvn_subr && $(LINK) $(named_atomic_proc_test_LDFLAGS) -o named_atomic-proc-test$(EXEEXT) $(named_atomic_proc_test_OBJECTS) ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
named_atomic_test_PATH = subversion/tests/libsvn_subr
named_atomic_test_DEPS = subversion/tests/libsvn_subr/named_atomic-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la
named_atomic_test_OBJECTS = named_atomic-test.lo
subversion/tests/libsvn_subr/named_atomic-test$(EXEEXT): $(named_atomic_test_DEPS)
cd subversion/tests/libsvn_subr && $(LINK) $(named_atomic_test_LDFLAGS) -o named_atomic-test$(EXEEXT) $(named_atomic_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
op_depth_test_PATH = subversion/tests/libsvn_wc
op_depth_test_DEPS = subversion/tests/libsvn_wc/op-depth-test.lo subversion/tests/libsvn_wc/utils.lo subversion/libsvn_client/libsvn_client-1.la subversion/tests/libsvn_test-1.la subversion/libsvn_wc/libsvn_wc-1.la subversion/libsvn_subr/libsvn_subr-1.la
op_depth_test_OBJECTS = op-depth-test.lo utils.lo
subversion/tests/libsvn_wc/op-depth-test$(EXEEXT): $(op_depth_test_DEPS)
cd subversion/tests/libsvn_wc && $(LINK) $(op_depth_test_LDFLAGS) -o op-depth-test$(EXEEXT) $(op_depth_test_OBJECTS) ../../../subversion/libsvn_client/libsvn_client-1.la ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_wc/libsvn_wc-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
opt_test_PATH = subversion/tests/libsvn_subr
opt_test_DEPS = subversion/tests/libsvn_subr/opt-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la
opt_test_OBJECTS = opt-test.lo
subversion/tests/libsvn_subr/opt-test$(EXEEXT): $(opt_test_DEPS)
cd subversion/tests/libsvn_subr && $(LINK) $(opt_test_LDFLAGS) -o opt-test$(EXEEXT) $(opt_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
parse_diff_test_PATH = subversion/tests/libsvn_diff
parse_diff_test_DEPS = subversion/tests/libsvn_diff/parse-diff-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_diff/libsvn_diff-1.la subversion/libsvn_subr/libsvn_subr-1.la
parse_diff_test_OBJECTS = parse-diff-test.lo
subversion/tests/libsvn_diff/parse-diff-test$(EXEEXT): $(parse_diff_test_DEPS)
cd subversion/tests/libsvn_diff && $(LINK) $(parse_diff_test_LDFLAGS) -o parse-diff-test$(EXEEXT) $(parse_diff_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_diff/libsvn_diff-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
path_test_PATH = subversion/tests/libsvn_subr
path_test_DEPS = subversion/tests/libsvn_subr/path-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la
path_test_OBJECTS = path-test.lo
subversion/tests/libsvn_subr/path-test$(EXEEXT): $(path_test_DEPS)
cd subversion/tests/libsvn_subr && $(LINK) $(path_test_LDFLAGS) -o path-test$(EXEEXT) $(path_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
perl_client_PATH = subversion/bindings/swig/perl/native
perl_client_DEPS = subversion/bindings/swig/perl/native/svn_client.lo subversion/bindings/swig/perl/libsvn_swig_perl/libsvn_swig_perl-1.la subversion/libsvn_client/libsvn_client-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/bindings/swig/perl/native/_Core.la
perl_client_OBJECTS = svn_client.lo
subversion/bindings/swig/perl/native/_Client.la: $(perl_client_DEPS)
cd subversion/bindings/swig/perl/native && $(LINK_PL_WRAPPER) $(perl_client_LDFLAGS) -o _Client.la $(LT_NO_UNDEFINED) $(perl_client_OBJECTS) ../../../../../subversion/bindings/swig/perl/libsvn_swig_perl/libsvn_swig_perl-1.la ../../../../../subversion/libsvn_client/libsvn_client-1.la ../../../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
perl_core_PATH = subversion/bindings/swig/perl/native
perl_core_DEPS = subversion/bindings/swig/perl/native/core.lo subversion/bindings/swig/perl/libsvn_swig_perl/libsvn_swig_perl-1.la subversion/libsvn_diff/libsvn_diff-1.la subversion/libsvn_subr/libsvn_subr-1.la
perl_core_OBJECTS = core.lo
subversion/bindings/swig/perl/native/_Core.la: $(perl_core_DEPS)
cd subversion/bindings/swig/perl/native && $(LINK_PL_WRAPPER) $(perl_core_LDFLAGS) -o _Core.la $(LT_NO_UNDEFINED) $(perl_core_OBJECTS) ../../../../../subversion/bindings/swig/perl/libsvn_swig_perl/libsvn_swig_perl-1.la ../../../../../subversion/libsvn_diff/libsvn_diff-1.la ../../../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
perl_delta_PATH = subversion/bindings/swig/perl/native
perl_delta_DEPS = subversion/bindings/swig/perl/native/svn_delta.lo subversion/bindings/swig/perl/libsvn_swig_perl/libsvn_swig_perl-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/bindings/swig/perl/native/_Core.la
perl_delta_OBJECTS = svn_delta.lo
subversion/bindings/swig/perl/native/_Delta.la: $(perl_delta_DEPS)
cd subversion/bindings/swig/perl/native && $(LINK_PL_WRAPPER) $(perl_delta_LDFLAGS) -o _Delta.la $(LT_NO_UNDEFINED) $(perl_delta_OBJECTS) ../../../../../subversion/bindings/swig/perl/libsvn_swig_perl/libsvn_swig_perl-1.la ../../../../../subversion/libsvn_delta/libsvn_delta-1.la ../../../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
perl_diff_PATH = subversion/bindings/swig/perl/native
perl_diff_DEPS = subversion/bindings/swig/perl/native/svn_diff.lo subversion/bindings/swig/perl/libsvn_swig_perl/libsvn_swig_perl-1.la subversion/libsvn_diff/libsvn_diff-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/bindings/swig/perl/native/_Core.la
perl_diff_OBJECTS = svn_diff.lo
subversion/bindings/swig/perl/native/_Diff.la: $(perl_diff_DEPS)
cd subversion/bindings/swig/perl/native && $(LINK_PL_WRAPPER) $(perl_diff_LDFLAGS) -o _Diff.la $(LT_NO_UNDEFINED) $(perl_diff_OBJECTS) ../../../../../subversion/bindings/swig/perl/libsvn_swig_perl/libsvn_swig_perl-1.la ../../../../../subversion/libsvn_diff/libsvn_diff-1.la ../../../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
perl_fs_PATH = subversion/bindings/swig/perl/native
perl_fs_DEPS = subversion/bindings/swig/perl/native/svn_fs.lo subversion/bindings/swig/perl/libsvn_swig_perl/libsvn_swig_perl-1.la subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/bindings/swig/perl/native/_Core.la
perl_fs_OBJECTS = svn_fs.lo
subversion/bindings/swig/perl/native/_Fs.la: $(perl_fs_DEPS)
cd subversion/bindings/swig/perl/native && $(LINK_PL_WRAPPER) $(perl_fs_LDFLAGS) -o _Fs.la $(LT_NO_UNDEFINED) $(perl_fs_OBJECTS) ../../../../../subversion/bindings/swig/perl/libsvn_swig_perl/libsvn_swig_perl-1.la ../../../../../subversion/libsvn_fs/libsvn_fs-1.la ../../../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
perl_ra_PATH = subversion/bindings/swig/perl/native
perl_ra_DEPS = subversion/bindings/swig/perl/native/svn_ra.lo subversion/bindings/swig/perl/libsvn_swig_perl/libsvn_swig_perl-1.la subversion/libsvn_ra/libsvn_ra-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/bindings/swig/perl/native/_Core.la
perl_ra_OBJECTS = svn_ra.lo
subversion/bindings/swig/perl/native/_Ra.la: $(perl_ra_DEPS)
cd subversion/bindings/swig/perl/native && $(LINK_PL_WRAPPER) $(perl_ra_LDFLAGS) -o _Ra.la $(LT_NO_UNDEFINED) $(perl_ra_OBJECTS) ../../../../../subversion/bindings/swig/perl/libsvn_swig_perl/libsvn_swig_perl-1.la ../../../../../subversion/libsvn_ra/libsvn_ra-1.la ../../../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
perl_repos_PATH = subversion/bindings/swig/perl/native
perl_repos_DEPS = subversion/bindings/swig/perl/native/svn_repos.lo subversion/bindings/swig/perl/libsvn_swig_perl/libsvn_swig_perl-1.la subversion/libsvn_repos/libsvn_repos-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/bindings/swig/perl/native/_Core.la
perl_repos_OBJECTS = svn_repos.lo
subversion/bindings/swig/perl/native/_Repos.la: $(perl_repos_DEPS)
cd subversion/bindings/swig/perl/native && $(LINK_PL_WRAPPER) $(perl_repos_LDFLAGS) -o _Repos.la $(LT_NO_UNDEFINED) $(perl_repos_OBJECTS) ../../../../../subversion/bindings/swig/perl/libsvn_swig_perl/libsvn_swig_perl-1.la ../../../../../subversion/libsvn_repos/libsvn_repos-1.la ../../../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
perl_wc_PATH = subversion/bindings/swig/perl/native
perl_wc_DEPS = subversion/bindings/swig/perl/native/svn_wc.lo subversion/bindings/swig/perl/libsvn_swig_perl/libsvn_swig_perl-1.la subversion/libsvn_wc/libsvn_wc-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/bindings/swig/perl/native/_Core.la
perl_wc_OBJECTS = svn_wc.lo
subversion/bindings/swig/perl/native/_Wc.la: $(perl_wc_DEPS)
cd subversion/bindings/swig/perl/native && $(LINK_PL_WRAPPER) $(perl_wc_LDFLAGS) -o _Wc.la $(LT_NO_UNDEFINED) $(perl_wc_OBJECTS) ../../../../../subversion/bindings/swig/perl/libsvn_swig_perl/libsvn_swig_perl-1.la ../../../../../subversion/libsvn_wc/libsvn_wc-1.la ../../../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
pristine_store_test_PATH = subversion/tests/libsvn_wc
pristine_store_test_DEPS = subversion/tests/libsvn_wc/pristine-store-test.lo subversion/tests/libsvn_wc/utils.lo subversion/libsvn_client/libsvn_client-1.la subversion/tests/libsvn_test-1.la subversion/libsvn_wc/libsvn_wc-1.la subversion/libsvn_subr/libsvn_subr-1.la
pristine_store_test_OBJECTS = pristine-store-test.lo utils.lo
subversion/tests/libsvn_wc/pristine-store-test$(EXEEXT): $(pristine_store_test_DEPS)
cd subversion/tests/libsvn_wc && $(LINK) $(pristine_store_test_LDFLAGS) -o pristine-store-test$(EXEEXT) $(pristine_store_test_OBJECTS) ../../../subversion/libsvn_client/libsvn_client-1.la ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_wc/libsvn_wc-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
python_client_PATH = subversion/bindings/swig/python
python_client_DEPS = subversion/bindings/swig/python/svn_client.lo subversion/bindings/swig/python/libsvn_swig_py/libsvn_swig_py-1.la subversion/libsvn_client/libsvn_client-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/bindings/swig/python/_core.la
python_client_OBJECTS = svn_client.lo
subversion/bindings/swig/python/_client.la: $(python_client_DEPS)
cd subversion/bindings/swig/python && $(LINK_PY_WRAPPER) $(python_client_LDFLAGS) -o _client.la $(LT_NO_UNDEFINED) $(python_client_OBJECTS) ../../../../subversion/bindings/swig/python/libsvn_swig_py/libsvn_swig_py-1.la ../../../../subversion/libsvn_client/libsvn_client-1.la ../../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
python_core_PATH = subversion/bindings/swig/python
python_core_DEPS = subversion/bindings/swig/python/core.lo subversion/bindings/swig/python/libsvn_swig_py/libsvn_swig_py-1.la subversion/libsvn_diff/libsvn_diff-1.la subversion/libsvn_subr/libsvn_subr-1.la
python_core_OBJECTS = core.lo
subversion/bindings/swig/python/_core.la: $(python_core_DEPS)
cd subversion/bindings/swig/python && $(LINK_PY_WRAPPER) $(python_core_LDFLAGS) -o _core.la $(LT_NO_UNDEFINED) $(python_core_OBJECTS) ../../../../subversion/bindings/swig/python/libsvn_swig_py/libsvn_swig_py-1.la ../../../../subversion/libsvn_diff/libsvn_diff-1.la ../../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
python_delta_PATH = subversion/bindings/swig/python
python_delta_DEPS = subversion/bindings/swig/python/svn_delta.lo subversion/bindings/swig/python/libsvn_swig_py/libsvn_swig_py-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/bindings/swig/python/_core.la
python_delta_OBJECTS = svn_delta.lo
subversion/bindings/swig/python/_delta.la: $(python_delta_DEPS)
cd subversion/bindings/swig/python && $(LINK_PY_WRAPPER) $(python_delta_LDFLAGS) -o _delta.la $(LT_NO_UNDEFINED) $(python_delta_OBJECTS) ../../../../subversion/bindings/swig/python/libsvn_swig_py/libsvn_swig_py-1.la ../../../../subversion/libsvn_delta/libsvn_delta-1.la ../../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
python_diff_PATH = subversion/bindings/swig/python
python_diff_DEPS = subversion/bindings/swig/python/svn_diff.lo subversion/bindings/swig/python/libsvn_swig_py/libsvn_swig_py-1.la subversion/libsvn_diff/libsvn_diff-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/bindings/swig/python/_core.la
python_diff_OBJECTS = svn_diff.lo
subversion/bindings/swig/python/_diff.la: $(python_diff_DEPS)
cd subversion/bindings/swig/python && $(LINK_PY_WRAPPER) $(python_diff_LDFLAGS) -o _diff.la $(LT_NO_UNDEFINED) $(python_diff_OBJECTS) ../../../../subversion/bindings/swig/python/libsvn_swig_py/libsvn_swig_py-1.la ../../../../subversion/libsvn_diff/libsvn_diff-1.la ../../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
python_fs_PATH = subversion/bindings/swig/python
python_fs_DEPS = subversion/bindings/swig/python/svn_fs.lo subversion/bindings/swig/python/libsvn_swig_py/libsvn_swig_py-1.la subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/bindings/swig/python/_core.la
python_fs_OBJECTS = svn_fs.lo
subversion/bindings/swig/python/_fs.la: $(python_fs_DEPS)
cd subversion/bindings/swig/python && $(LINK_PY_WRAPPER) $(python_fs_LDFLAGS) -o _fs.la $(LT_NO_UNDEFINED) $(python_fs_OBJECTS) ../../../../subversion/bindings/swig/python/libsvn_swig_py/libsvn_swig_py-1.la ../../../../subversion/libsvn_fs/libsvn_fs-1.la ../../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
python_ra_PATH = subversion/bindings/swig/python
python_ra_DEPS = subversion/bindings/swig/python/svn_ra.lo subversion/bindings/swig/python/libsvn_swig_py/libsvn_swig_py-1.la subversion/libsvn_ra/libsvn_ra-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/bindings/swig/python/_core.la
python_ra_OBJECTS = svn_ra.lo
subversion/bindings/swig/python/_ra.la: $(python_ra_DEPS)
cd subversion/bindings/swig/python && $(LINK_PY_WRAPPER) $(python_ra_LDFLAGS) -o _ra.la $(LT_NO_UNDEFINED) $(python_ra_OBJECTS) ../../../../subversion/bindings/swig/python/libsvn_swig_py/libsvn_swig_py-1.la ../../../../subversion/libsvn_ra/libsvn_ra-1.la ../../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
python_repos_PATH = subversion/bindings/swig/python
python_repos_DEPS = subversion/bindings/swig/python/svn_repos.lo subversion/bindings/swig/python/libsvn_swig_py/libsvn_swig_py-1.la subversion/libsvn_repos/libsvn_repos-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/bindings/swig/python/_core.la
python_repos_OBJECTS = svn_repos.lo
subversion/bindings/swig/python/_repos.la: $(python_repos_DEPS)
cd subversion/bindings/swig/python && $(LINK_PY_WRAPPER) $(python_repos_LDFLAGS) -o _repos.la $(LT_NO_UNDEFINED) $(python_repos_OBJECTS) ../../../../subversion/bindings/swig/python/libsvn_swig_py/libsvn_swig_py-1.la ../../../../subversion/libsvn_repos/libsvn_repos-1.la ../../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
python_wc_PATH = subversion/bindings/swig/python
python_wc_DEPS = subversion/bindings/swig/python/svn_wc.lo subversion/bindings/swig/python/libsvn_swig_py/libsvn_swig_py-1.la subversion/libsvn_wc/libsvn_wc-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/bindings/swig/python/_core.la
python_wc_OBJECTS = svn_wc.lo
subversion/bindings/swig/python/_wc.la: $(python_wc_DEPS)
cd subversion/bindings/swig/python && $(LINK_PY_WRAPPER) $(python_wc_LDFLAGS) -o _wc.la $(LT_NO_UNDEFINED) $(python_wc_OBJECTS) ../../../../subversion/bindings/swig/python/libsvn_swig_py/libsvn_swig_py-1.la ../../../../subversion/libsvn_wc/libsvn_wc-1.la ../../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
ra_local_test_PATH = subversion/tests/libsvn_ra_local
ra_local_test_DEPS = subversion/tests/libsvn_ra_local/ra-local-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_ra_local/libsvn_ra_local-1.la subversion/libsvn_ra/libsvn_ra-1.la subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
ra_local_test_OBJECTS = ra-local-test.lo
subversion/tests/libsvn_ra_local/ra-local-test$(EXEEXT): $(ra_local_test_DEPS)
cd subversion/tests/libsvn_ra_local && $(LINK) $(ra_local_test_LDFLAGS) -o ra-local-test$(EXEEXT) $(ra_local_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_ra_local/libsvn_ra_local-1.la ../../../subversion/libsvn_ra/libsvn_ra-1.la ../../../subversion/libsvn_fs/libsvn_fs-1.la ../../../subversion/libsvn_delta/libsvn_delta-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
ra_test_PATH = subversion/tests/libsvn_ra
ra_test_DEPS = subversion/tests/libsvn_ra/ra-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_ra/libsvn_ra-1.la subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
ra_test_OBJECTS = ra-test.lo
subversion/tests/libsvn_ra/ra-test$(EXEEXT): $(ra_test_DEPS)
cd subversion/tests/libsvn_ra && $(LINK) $(ra_test_LDFLAGS) -o ra-test$(EXEEXT) $(ra_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_ra/libsvn_ra-1.la ../../../subversion/libsvn_fs/libsvn_fs-1.la ../../../subversion/libsvn_delta/libsvn_delta-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
random_test_PATH = subversion/tests/libsvn_delta
random_test_DEPS = subversion/tests/libsvn_delta/random-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
random_test_OBJECTS = random-test.lo
subversion/tests/libsvn_delta/random-test$(EXEEXT): $(random_test_DEPS)
cd subversion/tests/libsvn_delta && $(LINK) $(random_test_LDFLAGS) -o random-test$(EXEEXT) $(random_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_delta/libsvn_delta-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
repos_test_PATH = subversion/tests/libsvn_repos
repos_test_DEPS = subversion/tests/libsvn_repos/dir-delta-editor.lo subversion/tests/libsvn_repos/repos-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_repos/libsvn_repos-1.la subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
repos_test_OBJECTS = dir-delta-editor.lo repos-test.lo
subversion/tests/libsvn_repos/repos-test$(EXEEXT): $(repos_test_DEPS)
cd subversion/tests/libsvn_repos && $(LINK) $(repos_test_LDFLAGS) -o repos-test$(EXEEXT) $(repos_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_repos/libsvn_repos-1.la ../../../subversion/libsvn_fs/libsvn_fs-1.la ../../../subversion/libsvn_delta/libsvn_delta-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
revision_test_PATH = subversion/tests/libsvn_subr
revision_test_DEPS = subversion/tests/libsvn_subr/revision-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la
revision_test_OBJECTS = revision-test.lo
subversion/tests/libsvn_subr/revision-test$(EXEEXT): $(revision_test_DEPS)
cd subversion/tests/libsvn_subr && $(LINK) $(revision_test_LDFLAGS) -o revision-test$(EXEEXT) $(revision_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
ruby_client_PATH = subversion/bindings/swig/ruby
ruby_client_DEPS = subversion/bindings/swig/ruby/svn_client.lo subversion/bindings/swig/ruby/libsvn_swig_ruby/libsvn_swig_ruby-1.la subversion/libsvn_client/libsvn_client-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/bindings/swig/ruby/core.la
ruby_client_OBJECTS = svn_client.lo
subversion/bindings/swig/ruby/client.la: $(ruby_client_DEPS)
cd subversion/bindings/swig/ruby && $(LINK_RB_WRAPPER) $(ruby_client_LDFLAGS) -o client.la $(LT_NO_UNDEFINED) $(ruby_client_OBJECTS) ../../../../subversion/bindings/swig/ruby/libsvn_swig_ruby/libsvn_swig_ruby-1.la ../../../../subversion/libsvn_client/libsvn_client-1.la ../../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
ruby_core_PATH = subversion/bindings/swig/ruby
ruby_core_DEPS = subversion/bindings/swig/ruby/core.lo subversion/bindings/swig/ruby/libsvn_swig_ruby/libsvn_swig_ruby-1.la subversion/libsvn_diff/libsvn_diff-1.la subversion/libsvn_subr/libsvn_subr-1.la
ruby_core_OBJECTS = core.lo
subversion/bindings/swig/ruby/core.la: $(ruby_core_DEPS)
cd subversion/bindings/swig/ruby && $(LINK_RB_WRAPPER) $(ruby_core_LDFLAGS) -o core.la $(LT_NO_UNDEFINED) $(ruby_core_OBJECTS) ../../../../subversion/bindings/swig/ruby/libsvn_swig_ruby/libsvn_swig_ruby-1.la ../../../../subversion/libsvn_diff/libsvn_diff-1.la ../../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
ruby_delta_PATH = subversion/bindings/swig/ruby
ruby_delta_DEPS = subversion/bindings/swig/ruby/svn_delta.lo subversion/bindings/swig/ruby/libsvn_swig_ruby/libsvn_swig_ruby-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/bindings/swig/ruby/core.la
ruby_delta_OBJECTS = svn_delta.lo
subversion/bindings/swig/ruby/delta.la: $(ruby_delta_DEPS)
cd subversion/bindings/swig/ruby && $(LINK_RB_WRAPPER) $(ruby_delta_LDFLAGS) -o delta.la $(LT_NO_UNDEFINED) $(ruby_delta_OBJECTS) ../../../../subversion/bindings/swig/ruby/libsvn_swig_ruby/libsvn_swig_ruby-1.la ../../../../subversion/libsvn_delta/libsvn_delta-1.la ../../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
ruby_diff_PATH = subversion/bindings/swig/ruby
ruby_diff_DEPS = subversion/bindings/swig/ruby/svn_diff.lo subversion/bindings/swig/ruby/libsvn_swig_ruby/libsvn_swig_ruby-1.la subversion/libsvn_diff/libsvn_diff-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/bindings/swig/ruby/core.la
ruby_diff_OBJECTS = svn_diff.lo
subversion/bindings/swig/ruby/diff.la: $(ruby_diff_DEPS)
cd subversion/bindings/swig/ruby && $(LINK_RB_WRAPPER) $(ruby_diff_LDFLAGS) -o diff.la $(LT_NO_UNDEFINED) $(ruby_diff_OBJECTS) ../../../../subversion/bindings/swig/ruby/libsvn_swig_ruby/libsvn_swig_ruby-1.la ../../../../subversion/libsvn_diff/libsvn_diff-1.la ../../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
ruby_fs_PATH = subversion/bindings/swig/ruby
ruby_fs_DEPS = subversion/bindings/swig/ruby/svn_fs.lo subversion/bindings/swig/ruby/libsvn_swig_ruby/libsvn_swig_ruby-1.la subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/bindings/swig/ruby/core.la
ruby_fs_OBJECTS = svn_fs.lo
subversion/bindings/swig/ruby/fs.la: $(ruby_fs_DEPS)
cd subversion/bindings/swig/ruby && $(LINK_RB_WRAPPER) $(ruby_fs_LDFLAGS) -o fs.la $(LT_NO_UNDEFINED) $(ruby_fs_OBJECTS) ../../../../subversion/bindings/swig/ruby/libsvn_swig_ruby/libsvn_swig_ruby-1.la ../../../../subversion/libsvn_fs/libsvn_fs-1.la ../../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
ruby_ra_PATH = subversion/bindings/swig/ruby
ruby_ra_DEPS = subversion/bindings/swig/ruby/svn_ra.lo subversion/bindings/swig/ruby/libsvn_swig_ruby/libsvn_swig_ruby-1.la subversion/libsvn_ra/libsvn_ra-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/bindings/swig/ruby/core.la
ruby_ra_OBJECTS = svn_ra.lo
subversion/bindings/swig/ruby/ra.la: $(ruby_ra_DEPS)
cd subversion/bindings/swig/ruby && $(LINK_RB_WRAPPER) $(ruby_ra_LDFLAGS) -o ra.la $(LT_NO_UNDEFINED) $(ruby_ra_OBJECTS) ../../../../subversion/bindings/swig/ruby/libsvn_swig_ruby/libsvn_swig_ruby-1.la ../../../../subversion/libsvn_ra/libsvn_ra-1.la ../../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
ruby_repos_PATH = subversion/bindings/swig/ruby
ruby_repos_DEPS = subversion/bindings/swig/ruby/svn_repos.lo subversion/bindings/swig/ruby/libsvn_swig_ruby/libsvn_swig_ruby-1.la subversion/libsvn_repos/libsvn_repos-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/bindings/swig/ruby/core.la
ruby_repos_OBJECTS = svn_repos.lo
subversion/bindings/swig/ruby/repos.la: $(ruby_repos_DEPS)
cd subversion/bindings/swig/ruby && $(LINK_RB_WRAPPER) $(ruby_repos_LDFLAGS) -o repos.la $(LT_NO_UNDEFINED) $(ruby_repos_OBJECTS) ../../../../subversion/bindings/swig/ruby/libsvn_swig_ruby/libsvn_swig_ruby-1.la ../../../../subversion/libsvn_repos/libsvn_repos-1.la ../../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
ruby_wc_PATH = subversion/bindings/swig/ruby
ruby_wc_DEPS = subversion/bindings/swig/ruby/svn_wc.lo subversion/bindings/swig/ruby/libsvn_swig_ruby/libsvn_swig_ruby-1.la subversion/libsvn_wc/libsvn_wc-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/bindings/swig/ruby/core.la
ruby_wc_OBJECTS = svn_wc.lo
subversion/bindings/swig/ruby/wc.la: $(ruby_wc_DEPS)
cd subversion/bindings/swig/ruby && $(LINK_RB_WRAPPER) $(ruby_wc_LDFLAGS) -o wc.la $(LT_NO_UNDEFINED) $(ruby_wc_OBJECTS) ../../../../subversion/bindings/swig/ruby/libsvn_swig_ruby/libsvn_swig_ruby-1.la ../../../../subversion/libsvn_wc/libsvn_wc-1.la ../../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
skel_test_PATH = subversion/tests/libsvn_subr
skel_test_DEPS = subversion/tests/libsvn_subr/skel-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la
skel_test_OBJECTS = skel-test.lo
subversion/tests/libsvn_subr/skel-test$(EXEEXT): $(skel_test_DEPS)
cd subversion/tests/libsvn_subr && $(LINK) $(skel_test_LDFLAGS) -o skel-test$(EXEEXT) $(skel_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
spillbuf_test_PATH = subversion/tests/libsvn_subr
spillbuf_test_DEPS = subversion/tests/libsvn_subr/spillbuf-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la
spillbuf_test_OBJECTS = spillbuf-test.lo
subversion/tests/libsvn_subr/spillbuf-test$(EXEEXT): $(spillbuf_test_DEPS)
cd subversion/tests/libsvn_subr && $(LINK) $(spillbuf_test_LDFLAGS) -o spillbuf-test$(EXEEXT) $(spillbuf_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
stream_test_PATH = subversion/tests/libsvn_subr
stream_test_DEPS = subversion/tests/libsvn_subr/stream-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la
stream_test_OBJECTS = stream-test.lo
subversion/tests/libsvn_subr/stream-test$(EXEEXT): $(stream_test_DEPS)
cd subversion/tests/libsvn_subr && $(LINK) $(stream_test_LDFLAGS) -o stream-test$(EXEEXT) $(stream_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
string_test_PATH = subversion/tests/libsvn_subr
string_test_DEPS = subversion/tests/libsvn_subr/string-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la
string_test_OBJECTS = string-test.lo
subversion/tests/libsvn_subr/string-test$(EXEEXT): $(string_test_DEPS)
cd subversion/tests/libsvn_subr && $(LINK) $(string_test_LDFLAGS) -o string-test$(EXEEXT) $(string_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
strings_reps_test_PATH = subversion/tests/libsvn_fs_base
strings_reps_test_DEPS = subversion/tests/libsvn_fs_base/strings-reps-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_fs_base/libsvn_fs_base-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
strings_reps_test_OBJECTS = strings-reps-test.lo
subversion/tests/libsvn_fs_base/strings-reps-test$(EXEEXT): $(strings_reps_test_DEPS)
cd subversion/tests/libsvn_fs_base && $(LINK) $(strings_reps_test_LDFLAGS) -o strings-reps-test$(EXEEXT) $(strings_reps_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_fs/libsvn_fs-1.la ../../../subversion/libsvn_fs_base/libsvn_fs_base-1.la ../../../subversion/libsvn_delta/libsvn_delta-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
subst_translate_test_PATH = subversion/tests/libsvn_subr
subst_translate_test_DEPS = subversion/tests/libsvn_subr/subst_translate-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la
subst_translate_test_OBJECTS = subst_translate-test.lo
subversion/tests/libsvn_subr/subst_translate-test$(EXEEXT): $(subst_translate_test_DEPS)
cd subversion/tests/libsvn_subr && $(LINK) $(subst_translate_test_LDFLAGS) -o subst_translate-test$(EXEEXT) $(subst_translate_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
svn_PATH = subversion/svn
svn_DEPS = subversion/svn/add-cmd.lo subversion/svn/blame-cmd.lo subversion/svn/cat-cmd.lo subversion/svn/changelist-cmd.lo subversion/svn/checkout-cmd.lo subversion/svn/cl-conflicts.lo subversion/svn/cleanup-cmd.lo subversion/svn/commit-cmd.lo subversion/svn/conflict-callbacks.lo subversion/svn/copy-cmd.lo subversion/svn/delete-cmd.lo subversion/svn/deprecated.lo subversion/svn/diff-cmd.lo subversion/svn/export-cmd.lo subversion/svn/file-merge.lo subversion/svn/help-cmd.lo subversion/svn/import-cmd.lo subversion/svn/info-cmd.lo subversion/svn/list-cmd.lo subversion/svn/lock-cmd.lo subversion/svn/log-cmd.lo subversion/svn/merge-cmd.lo subversion/svn/mergeinfo-cmd.lo subversion/svn/mkdir-cmd.lo subversion/svn/move-cmd.lo subversion/svn/notify.lo subversion/svn/patch-cmd.lo subversion/svn/propdel-cmd.lo subversion/svn/propedit-cmd.lo subversion/svn/propget-cmd.lo subversion/svn/proplist-cmd.lo subversion/svn/props.lo subversion/svn/propset-cmd.lo subversion/svn/relocate-cmd.lo subversion/svn/resolve-cmd.lo subversion/svn/resolved-cmd.lo subversion/svn/revert-cmd.lo subversion/svn/status-cmd.lo subversion/svn/status.lo subversion/svn/svn.lo subversion/svn/switch-cmd.lo subversion/svn/unlock-cmd.lo subversion/svn/update-cmd.lo subversion/svn/upgrade-cmd.lo subversion/svn/util.lo subversion/libsvn_client/libsvn_client-1.la subversion/libsvn_wc/libsvn_wc-1.la subversion/libsvn_ra/libsvn_ra-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_diff/libsvn_diff-1.la subversion/libsvn_subr/libsvn_subr-1.la
svn_OBJECTS = add-cmd.lo blame-cmd.lo cat-cmd.lo changelist-cmd.lo checkout-cmd.lo cl-conflicts.lo cleanup-cmd.lo commit-cmd.lo conflict-callbacks.lo copy-cmd.lo delete-cmd.lo deprecated.lo diff-cmd.lo export-cmd.lo file-merge.lo help-cmd.lo import-cmd.lo info-cmd.lo list-cmd.lo lock-cmd.lo log-cmd.lo merge-cmd.lo mergeinfo-cmd.lo mkdir-cmd.lo move-cmd.lo notify.lo patch-cmd.lo propdel-cmd.lo propedit-cmd.lo propget-cmd.lo proplist-cmd.lo props.lo propset-cmd.lo relocate-cmd.lo resolve-cmd.lo resolved-cmd.lo revert-cmd.lo status-cmd.lo status.lo svn.lo switch-cmd.lo unlock-cmd.lo update-cmd.lo upgrade-cmd.lo util.lo
subversion/svn/svn$(EXEEXT): $(svn_DEPS)
cd subversion/svn && $(LINK) $(svn_LDFLAGS) -o svn$(EXEEXT) $(svn_OBJECTS) ../../subversion/libsvn_client/libsvn_client-1.la ../../subversion/libsvn_wc/libsvn_wc-1.la ../../subversion/libsvn_ra/libsvn_ra-1.la ../../subversion/libsvn_delta/libsvn_delta-1.la ../../subversion/libsvn_diff/libsvn_diff-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
svn_bench_PATH = tools/client-side/svn-bench
svn_bench_DEPS = tools/client-side/svn-bench/help-cmd.lo tools/client-side/svn-bench/notify.lo tools/client-side/svn-bench/null-export-cmd.lo tools/client-side/svn-bench/null-list-cmd.lo tools/client-side/svn-bench/null-log-cmd.lo tools/client-side/svn-bench/svn-bench.lo tools/client-side/svn-bench/util.lo subversion/libsvn_client/libsvn_client-1.la subversion/libsvn_wc/libsvn_wc-1.la subversion/libsvn_ra/libsvn_ra-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/libsvn_delta/libsvn_delta-1.la
svn_bench_OBJECTS = help-cmd.lo notify.lo null-export-cmd.lo null-list-cmd.lo null-log-cmd.lo svn-bench.lo util.lo
tools/client-side/svn-bench/svn-bench$(EXEEXT): $(svn_bench_DEPS)
cd tools/client-side/svn-bench && $(LINK) $(svn_bench_LDFLAGS) -o svn-bench$(EXEEXT) $(svn_bench_OBJECTS) ../../../subversion/libsvn_client/libsvn_client-1.la ../../../subversion/libsvn_wc/libsvn_wc-1.la ../../../subversion/libsvn_ra/libsvn_ra-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la ../../../subversion/libsvn_delta/libsvn_delta-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
svn_populate_node_origins_index_PATH = tools/server-side
svn_populate_node_origins_index_DEPS = tools/server-side/svn-populate-node-origins-index.lo subversion/libsvn_repos/libsvn_repos-1.la subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_subr/libsvn_subr-1.la
svn_populate_node_origins_index_OBJECTS = svn-populate-node-origins-index.lo
tools/server-side/svn-populate-node-origins-index$(EXEEXT): $(svn_populate_node_origins_index_DEPS)
cd tools/server-side && $(LINK) $(svn_populate_node_origins_index_LDFLAGS) -o svn-populate-node-origins-index$(EXEEXT) $(svn_populate_node_origins_index_OBJECTS) ../../subversion/libsvn_repos/libsvn_repos-1.la ../../subversion/libsvn_fs/libsvn_fs-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
svn_rep_sharing_stats_PATH = tools/server-side
svn_rep_sharing_stats_DEPS = tools/server-side/svn-rep-sharing-stats.lo subversion/libsvn_repos/libsvn_repos-1.la subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_fs_fs/libsvn_fs_fs-1.la subversion/libsvn_subr/libsvn_subr-1.la
svn_rep_sharing_stats_OBJECTS = svn-rep-sharing-stats.lo
tools/server-side/svn-rep-sharing-stats$(EXEEXT): $(svn_rep_sharing_stats_DEPS)
cd tools/server-side && $(LINK) $(svn_rep_sharing_stats_LDFLAGS) -o svn-rep-sharing-stats$(EXEEXT) $(svn_rep_sharing_stats_OBJECTS) ../../subversion/libsvn_repos/libsvn_repos-1.la ../../subversion/libsvn_fs/libsvn_fs-1.la ../../subversion/libsvn_fs_fs/libsvn_fs_fs-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
svnadmin_PATH = subversion/svnadmin
svnadmin_DEPS = subversion/svnadmin/svnadmin.lo subversion/libsvn_repos/libsvn_repos-1.la subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
svnadmin_OBJECTS = svnadmin.lo
subversion/svnadmin/svnadmin$(EXEEXT): $(svnadmin_DEPS)
cd subversion/svnadmin && $(LINK) $(svnadmin_LDFLAGS) -o svnadmin$(EXEEXT) $(svnadmin_OBJECTS) ../../subversion/libsvn_repos/libsvn_repos-1.la ../../subversion/libsvn_fs/libsvn_fs-1.la ../../subversion/libsvn_delta/libsvn_delta-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
svnauthz_PATH = tools/server-side
svnauthz_DEPS = tools/server-side/svnauthz.lo subversion/libsvn_repos/libsvn_repos-1.la subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_subr/libsvn_subr-1.la
svnauthz_OBJECTS = svnauthz.lo
tools/server-side/svnauthz$(EXEEXT): $(svnauthz_DEPS)
cd tools/server-side && $(LINK) $(svnauthz_LDFLAGS) -o svnauthz$(EXEEXT) $(svnauthz_OBJECTS) ../../subversion/libsvn_repos/libsvn_repos-1.la ../../subversion/libsvn_fs/libsvn_fs-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
svnauthz_validate_PATH = tools/server-side
svnauthz_validate_DEPS = tools/server-side/svnauthz.lo subversion/libsvn_repos/libsvn_repos-1.la subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_subr/libsvn_subr-1.la
svnauthz_validate_OBJECTS = svnauthz.lo
tools/server-side/svnauthz-validate$(EXEEXT): $(svnauthz_validate_DEPS)
cd tools/server-side && $(LINK) $(svnauthz_validate_LDFLAGS) -o svnauthz-validate$(EXEEXT) $(svnauthz_validate_OBJECTS) ../../subversion/libsvn_repos/libsvn_repos-1.la ../../subversion/libsvn_fs/libsvn_fs-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
svndiff_test_PATH = subversion/tests/libsvn_delta
svndiff_test_DEPS = subversion/tests/libsvn_delta/svndiff-test.lo subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
svndiff_test_OBJECTS = svndiff-test.lo
subversion/tests/libsvn_delta/svndiff-test$(EXEEXT): $(svndiff_test_DEPS)
cd subversion/tests/libsvn_delta && $(LINK) $(svndiff_test_LDFLAGS) -o svndiff-test$(EXEEXT) $(svndiff_test_OBJECTS) ../../../subversion/libsvn_delta/libsvn_delta-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
svndumpfilter_PATH = subversion/svndumpfilter
svndumpfilter_DEPS = subversion/svndumpfilter/svndumpfilter.lo subversion/libsvn_repos/libsvn_repos-1.la subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
svndumpfilter_OBJECTS = svndumpfilter.lo
subversion/svndumpfilter/svndumpfilter$(EXEEXT): $(svndumpfilter_DEPS)
cd subversion/svndumpfilter && $(LINK) $(svndumpfilter_LDFLAGS) -o svndumpfilter$(EXEEXT) $(svndumpfilter_OBJECTS) ../../subversion/libsvn_repos/libsvn_repos-1.la ../../subversion/libsvn_fs/libsvn_fs-1.la ../../subversion/libsvn_delta/libsvn_delta-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
svnlook_PATH = subversion/svnlook
svnlook_DEPS = subversion/svnlook/svnlook.lo subversion/libsvn_repos/libsvn_repos-1.la subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_diff/libsvn_diff-1.la subversion/libsvn_subr/libsvn_subr-1.la
svnlook_OBJECTS = svnlook.lo
subversion/svnlook/svnlook$(EXEEXT): $(svnlook_DEPS)
cd subversion/svnlook && $(LINK) $(svnlook_LDFLAGS) -o svnlook$(EXEEXT) $(svnlook_OBJECTS) ../../subversion/libsvn_repos/libsvn_repos-1.la ../../subversion/libsvn_fs/libsvn_fs-1.la ../../subversion/libsvn_delta/libsvn_delta-1.la ../../subversion/libsvn_diff/libsvn_diff-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
svnmucc_PATH = subversion/svnmucc
svnmucc_DEPS = subversion/svnmucc/svnmucc.lo subversion/libsvn_client/libsvn_client-1.la subversion/libsvn_ra/libsvn_ra-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/libsvn_delta/libsvn_delta-1.la
svnmucc_OBJECTS = svnmucc.lo
subversion/svnmucc/svnmucc$(EXEEXT): $(svnmucc_DEPS)
cd subversion/svnmucc && $(LINK) $(svnmucc_LDFLAGS) -o svnmucc$(EXEEXT) $(svnmucc_OBJECTS) ../../subversion/libsvn_client/libsvn_client-1.la ../../subversion/libsvn_ra/libsvn_ra-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la ../../subversion/libsvn_delta/libsvn_delta-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
svnraisetreeconflict_PATH = tools/dev/svnraisetreeconflict
svnraisetreeconflict_DEPS = tools/dev/svnraisetreeconflict/svnraisetreeconflict.lo subversion/libsvn_wc/libsvn_wc-1.la subversion/libsvn_subr/libsvn_subr-1.la
svnraisetreeconflict_OBJECTS = svnraisetreeconflict.lo
tools/dev/svnraisetreeconflict/svnraisetreeconflict$(EXEEXT): $(svnraisetreeconflict_DEPS)
cd tools/dev/svnraisetreeconflict && $(LINK) $(svnraisetreeconflict_LDFLAGS) -o svnraisetreeconflict$(EXEEXT) $(svnraisetreeconflict_OBJECTS) ../../../subversion/libsvn_wc/libsvn_wc-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
svnrdump_PATH = subversion/svnrdump
svnrdump_DEPS = subversion/svnrdump/dump_editor.lo subversion/svnrdump/load_editor.lo subversion/svnrdump/svnrdump.lo subversion/svnrdump/util.lo subversion/libsvn_client/libsvn_client-1.la subversion/libsvn_ra/libsvn_ra-1.la subversion/libsvn_repos/libsvn_repos-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
svnrdump_OBJECTS = dump_editor.lo load_editor.lo svnrdump.lo util.lo
subversion/svnrdump/svnrdump$(EXEEXT): $(svnrdump_DEPS)
cd subversion/svnrdump && $(LINK) $(svnrdump_LDFLAGS) -o svnrdump$(EXEEXT) $(svnrdump_OBJECTS) ../../subversion/libsvn_client/libsvn_client-1.la ../../subversion/libsvn_ra/libsvn_ra-1.la ../../subversion/libsvn_repos/libsvn_repos-1.la ../../subversion/libsvn_delta/libsvn_delta-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
svnserve_PATH = subversion/svnserve
svnserve_DEPS = subversion/svnserve/cyrus_auth.lo subversion/svnserve/log-escape.lo subversion/svnserve/serve.lo subversion/svnserve/svnserve.lo subversion/svnserve/winservice.lo subversion/libsvn_repos/libsvn_repos-1.la subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la subversion/libsvn_ra_svn/libsvn_ra_svn-1.la
svnserve_OBJECTS = cyrus_auth.lo log-escape.lo serve.lo svnserve.lo winservice.lo
subversion/svnserve/svnserve$(EXEEXT): $(svnserve_DEPS)
cd subversion/svnserve && $(LINK) $(svnserve_LDFLAGS) -o svnserve$(EXEEXT) $(svnserve_OBJECTS) ../../subversion/libsvn_repos/libsvn_repos-1.la ../../subversion/libsvn_fs/libsvn_fs-1.la ../../subversion/libsvn_delta/libsvn_delta-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la ../../subversion/libsvn_ra_svn/libsvn_ra_svn-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(SVN_SASL_LIBS) $(LIBS)
svnsync_PATH = subversion/svnsync
svnsync_DEPS = subversion/svnsync/svnsync.lo subversion/svnsync/sync.lo subversion/libsvn_ra/libsvn_ra-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
svnsync_OBJECTS = svnsync.lo sync.lo
subversion/svnsync/svnsync$(EXEEXT): $(svnsync_DEPS)
cd subversion/svnsync && $(LINK) $(svnsync_LDFLAGS) -o svnsync$(EXEEXT) $(svnsync_OBJECTS) ../../subversion/libsvn_ra/libsvn_ra-1.la ../../subversion/libsvn_delta/libsvn_delta-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS)
svnversion_PATH = subversion/svnversion
svnversion_DEPS = subversion/svnversion/svnversion.lo subversion/libsvn_wc/libsvn_wc-1.la subversion/libsvn_subr/libsvn_subr-1.la
svnversion_OBJECTS = svnversion.lo
subversion/svnversion/svnversion$(EXEEXT): $(svnversion_DEPS)
cd subversion/svnversion && $(LINK) $(svnversion_LDFLAGS) -o svnversion$(EXEEXT) $(svnversion_OBJECTS) ../../subversion/libsvn_wc/libsvn_wc-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
time_test_PATH = subversion/tests/libsvn_subr
time_test_DEPS = subversion/tests/libsvn_subr/time-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la
time_test_OBJECTS = time-test.lo
subversion/tests/libsvn_subr/time-test$(EXEEXT): $(time_test_DEPS)
cd subversion/tests/libsvn_subr && $(LINK) $(time_test_LDFLAGS) -o time-test$(EXEEXT) $(time_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
translate_test_PATH = subversion/tests/libsvn_subr
translate_test_DEPS = subversion/tests/libsvn_subr/translate-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la
translate_test_OBJECTS = translate-test.lo
subversion/tests/libsvn_subr/translate-test$(EXEEXT): $(translate_test_DEPS)
cd subversion/tests/libsvn_subr && $(LINK) $(translate_test_LDFLAGS) -o translate-test$(EXEEXT) $(translate_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
utf_test_PATH = subversion/tests/libsvn_subr
utf_test_DEPS = subversion/tests/libsvn_subr/utf-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la
utf_test_OBJECTS = utf-test.lo
subversion/tests/libsvn_subr/utf-test$(EXEEXT): $(utf_test_DEPS)
cd subversion/tests/libsvn_subr && $(LINK) $(utf_test_LDFLAGS) -o utf-test$(EXEEXT) $(utf_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
vdelta_test_PATH = subversion/tests/libsvn_delta
vdelta_test_DEPS = subversion/tests/libsvn_delta/vdelta-test.lo subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
vdelta_test_OBJECTS = vdelta-test.lo
subversion/tests/libsvn_delta/vdelta-test$(EXEEXT): $(vdelta_test_DEPS)
cd subversion/tests/libsvn_delta && $(LINK) $(vdelta_test_LDFLAGS) -o vdelta-test$(EXEEXT) $(vdelta_test_OBJECTS) ../../../subversion/libsvn_delta/libsvn_delta-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
wc_incomplete_tester_PATH = subversion/tests/libsvn_wc
wc_incomplete_tester_DEPS = subversion/tests/libsvn_wc/wc-incomplete-tester.lo subversion/libsvn_wc/libsvn_wc-1.la subversion/libsvn_subr/libsvn_subr-1.la
wc_incomplete_tester_OBJECTS = wc-incomplete-tester.lo
subversion/tests/libsvn_wc/wc-incomplete-tester$(EXEEXT): $(wc_incomplete_tester_DEPS)
cd subversion/tests/libsvn_wc && $(LINK) $(wc_incomplete_tester_LDFLAGS) -o wc-incomplete-tester$(EXEEXT) $(wc_incomplete_tester_OBJECTS) ../../../subversion/libsvn_wc/libsvn_wc-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
wc_lock_tester_PATH = subversion/tests/libsvn_wc
wc_lock_tester_DEPS = subversion/tests/libsvn_wc/wc-lock-tester.lo subversion/libsvn_wc/libsvn_wc-1.la subversion/libsvn_subr/libsvn_subr-1.la
wc_lock_tester_OBJECTS = wc-lock-tester.lo
subversion/tests/libsvn_wc/wc-lock-tester$(EXEEXT): $(wc_lock_tester_DEPS)
cd subversion/tests/libsvn_wc && $(LINK) $(wc_lock_tester_LDFLAGS) -o wc-lock-tester$(EXEEXT) $(wc_lock_tester_OBJECTS) ../../../subversion/libsvn_wc/libsvn_wc-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
wc_queries_test_PATH = subversion/tests/libsvn_wc
wc_queries_test_DEPS = subversion/tests/libsvn_wc/wc-queries-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la
wc_queries_test_OBJECTS = wc-queries-test.lo
subversion/tests/libsvn_wc/wc-queries-test$(EXEEXT): $(wc_queries_test_DEPS)
cd subversion/tests/libsvn_wc && $(LINK) $(wc_queries_test_LDFLAGS) -o wc-queries-test$(EXEEXT) $(wc_queries_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(SVN_SQLITE_LIBS) $(LIBS)
wc_test_PATH = subversion/tests/libsvn_wc
wc_test_DEPS = subversion/tests/libsvn_wc/utils.lo subversion/tests/libsvn_wc/wc-test.lo subversion/libsvn_client/libsvn_client-1.la subversion/tests/libsvn_test-1.la subversion/libsvn_wc/libsvn_wc-1.la subversion/libsvn_subr/libsvn_subr-1.la
wc_test_OBJECTS = utils.lo wc-test.lo
subversion/tests/libsvn_wc/wc-test$(EXEEXT): $(wc_test_DEPS)
cd subversion/tests/libsvn_wc && $(LINK) $(wc_test_LDFLAGS) -o wc-test$(EXEEXT) $(wc_test_OBJECTS) ../../../subversion/libsvn_client/libsvn_client-1.la ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_wc/libsvn_wc-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
window_test_PATH = subversion/tests/libsvn_delta
window_test_DEPS = subversion/tests/libsvn_delta/window-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la
window_test_OBJECTS = window-test.lo
subversion/tests/libsvn_delta/window-test$(EXEEXT): $(window_test_DEPS)
cd subversion/tests/libsvn_delta && $(LINK) $(window_test_LDFLAGS) -o window-test$(EXEEXT) $(window_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_delta/libsvn_delta-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS)
########################################
# Section 6: Install-Group build targets
########################################
apache-mod: subversion/mod_authz_svn/mod_authz_svn.la subversion/mod_dav_svn/mod_dav_svn.la
bdb-lib: subversion/libsvn_fs_base/libsvn_fs_base-1.la
bdb-test: subversion/tests/libsvn_fs_base/changes-test$(EXEEXT) subversion/tests/libsvn_fs_base/fs-base-test$(EXEEXT) subversion/tests/libsvn_fs_base/strings-reps-test$(EXEEXT)
bin: subversion/svn/svn$(EXEEXT) subversion/svnadmin/svnadmin$(EXEEXT) subversion/svndumpfilter/svndumpfilter$(EXEEXT) subversion/svnlook/svnlook$(EXEEXT) subversion/svnmucc/svnmucc$(EXEEXT) subversion/svnrdump/svnrdump$(EXEEXT) subversion/svnserve/svnserve$(EXEEXT) subversion/svnsync/svnsync$(EXEEXT) subversion/svnversion/svnversion$(EXEEXT)
cxxhl-lib: subversion/bindings/cxxhl/libsvncxxhl-1.la
fsmod-lib: subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_fs_fs/libsvn_fs_fs-1.la subversion/libsvn_fs_util/libsvn_fs_util-1.la subversion/libsvn_subr/libsvn_subr-1.la
gnome-keyring-lib: subversion/libsvn_auth_gnome_keyring/libsvn_auth_gnome_keyring-1.la
javahl-callback-javah:
javahl-compat-java:
javahl-compat-tests:
javahl-java:
javahl-javah:
javahl-lib: subversion/bindings/javahl/native/libsvnjavahl-1.la
javahl-tests:
javahl-types-javah:
kwallet-lib: subversion/libsvn_auth_kwallet/libsvn_auth_kwallet-1.la
lib: subversion/libsvn_client/libsvn_client-1.la subversion/libsvn_diff/libsvn_diff-1.la subversion/libsvn_ra/libsvn_ra-1.la subversion/libsvn_wc/libsvn_wc-1.la
locale:
ramod-lib: subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_ra_local/libsvn_ra_local-1.la subversion/libsvn_ra_svn/libsvn_ra_svn-1.la subversion/libsvn_repos/libsvn_repos-1.la
serf-lib: subversion/libsvn_ra_serf/libsvn_ra_serf-1.la
sub-test: subversion/tests/libsvn_subr/named_atomic-proc-test$(EXEEXT)
swig-pl-lib: subversion/bindings/swig/perl/libsvn_swig_perl/libsvn_swig_perl-1.la
swig-py: subversion/bindings/swig/python/_client.la subversion/bindings/swig/python/_core.la subversion/bindings/swig/python/_delta.la subversion/bindings/swig/python/_diff.la subversion/bindings/swig/python/_fs.la subversion/bindings/swig/python/_ra.la subversion/bindings/swig/python/_repos.la subversion/bindings/swig/python/_wc.la
swig-py-lib: subversion/bindings/swig/python/libsvn_swig_py/libsvn_swig_py-1.la
swig-rb: subversion/bindings/swig/ruby/client.la subversion/bindings/swig/ruby/core.la subversion/bindings/swig/ruby/delta.la subversion/bindings/swig/ruby/diff.la subversion/bindings/swig/ruby/fs.la subversion/bindings/swig/ruby/ra.la subversion/bindings/swig/ruby/repos.la subversion/bindings/swig/ruby/wc.la
swig-rb-lib: subversion/bindings/swig/ruby/libsvn_swig_ruby/libsvn_swig_ruby-1.la
test: subversion/tests/cmdline/atomic-ra-revprop-change$(EXEEXT) subversion/tests/libsvn_subr/auth-test$(EXEEXT) subversion/tests/libsvn_subr/cache-test$(EXEEXT) subversion/tests/libsvn_subr/checksum-test$(EXEEXT) subversion/tests/libsvn_client/client-test$(EXEEXT) subversion/tests/libsvn_subr/compat-test$(EXEEXT) subversion/tests/libsvn_subr/config-test$(EXEEXT) subversion/tests/libsvn_wc/conflict-data-test$(EXEEXT) subversion/tests/libsvn_subr/crypto-test$(EXEEXT) subversion/tests/libsvn_wc/db-test$(EXEEXT) subversion/tests/libsvn_diff/diff-diff3-test$(EXEEXT) subversion/tests/libsvn_subr/dirent_uri-test$(EXEEXT) subversion/tests/libsvn_wc/entries-compat-test$(EXEEXT) subversion/tests/cmdline/entries-dump$(EXEEXT) subversion/tests/libsvn_subr/error-code-test$(EXEEXT) subversion/tests/libsvn_subr/error-test$(EXEEXT) subversion/tests/libsvn_fs_fs/fs-pack-test$(EXEEXT) subversion/tests/libsvn_fs/fs-test$(EXEEXT) subversion/tests/libsvn_subr/hashdump-test$(EXEEXT) subversion/tests/libsvn_subr/io-test$(EXEEXT) subversion/tests/libsvn_test-1.la subversion/tests/libsvn_fs/locks-test$(EXEEXT) subversion/tests/libsvn_subr/mergeinfo-test$(EXEEXT) subversion/tests/libsvn_subr/named_atomic-test$(EXEEXT) subversion/tests/libsvn_wc/op-depth-test$(EXEEXT) subversion/tests/libsvn_subr/opt-test$(EXEEXT) subversion/tests/libsvn_diff/parse-diff-test$(EXEEXT) subversion/tests/libsvn_subr/path-test$(EXEEXT) subversion/tests/libsvn_wc/pristine-store-test$(EXEEXT) subversion/tests/libsvn_ra_local/ra-local-test$(EXEEXT) subversion/tests/libsvn_ra/ra-test$(EXEEXT) subversion/tests/libsvn_delta/random-test$(EXEEXT) subversion/tests/libsvn_repos/repos-test$(EXEEXT) subversion/tests/libsvn_subr/revision-test$(EXEEXT) subversion/tests/libsvn_subr/skel-test$(EXEEXT) subversion/tests/libsvn_subr/spillbuf-test$(EXEEXT) subversion/tests/libsvn_subr/stream-test$(EXEEXT) subversion/tests/libsvn_subr/string-test$(EXEEXT) subversion/tests/libsvn_subr/subst_translate-test$(EXEEXT) subversion/tests/libsvn_delta/svndiff-test$(EXEEXT) subversion/tests/libsvn_subr/time-test$(EXEEXT) subversion/tests/libsvn_subr/translate-test$(EXEEXT) subversion/tests/libsvn_subr/utf-test$(EXEEXT) subversion/tests/libsvn_delta/vdelta-test$(EXEEXT) subversion/tests/libsvn_wc/wc-incomplete-tester$(EXEEXT) subversion/tests/libsvn_wc/wc-lock-tester$(EXEEXT) subversion/tests/libsvn_wc/wc-queries-test$(EXEEXT) subversion/tests/libsvn_wc/wc-test$(EXEEXT) subversion/tests/libsvn_delta/window-test$(EXEEXT)
tests: subversion/bindings/cxxhl/cxxhl-tests$(EXEEXT)
tools: tools/diff/diff$(EXEEXT) tools/diff/diff3$(EXEEXT) tools/diff/diff4$(EXEEXT) tools/dev/fsfs-access-map$(EXEEXT) tools/dev/fsfs-reorg$(EXEEXT) tools/server-side/fsfs-stats$(EXEEXT) tools/server-side/mod_dontdothat/mod_dontdothat.la tools/client-side/svn-bench/svn-bench$(EXEEXT) tools/server-side/svn-populate-node-origins-index$(EXEEXT) tools/server-side/svn-rep-sharing-stats$(EXEEXT) tools/server-side/svnauthz$(EXEEXT) tools/server-side/svnauthz-validate$(EXEEXT) tools/dev/svnraisetreeconflict/svnraisetreeconflict$(EXEEXT)
########################################
# Section 7: Install-Group install targets
########################################
install-mods-shared: subversion/mod_dav_svn/mod_dav_svn.la subversion/mod_authz_svn/mod_authz_svn.la
if $(INSTALL_APACHE_MODS) ; then cd subversion/mod_dav_svn ; $(MKDIR) "$(APACHE_LIBEXECDIR)" ; $(INSTALL_MOD_SHARED) -n dav_svn mod_dav_svn.la ; fi
if $(INSTALL_APACHE_MODS) ; then cd subversion/mod_authz_svn ; $(MKDIR) "$(APACHE_LIBEXECDIR)" ; $(INSTALL_MOD_SHARED) -n authz_svn mod_authz_svn.la ; fi
install-bdb-lib: subversion/libsvn_fs_base/libsvn_fs_base-1.la
$(MKDIR) $(DESTDIR)$(bdb_libdir)
cd subversion/libsvn_fs_base ; $(INSTALL_BDB_LIB) libsvn_fs_base-1.la $(DESTDIR)$(bdb_libdir)/libsvn_fs_base-1.la
install-bin: subversion/svn/svn$(EXEEXT) subversion/svnadmin/svnadmin$(EXEEXT) subversion/svndumpfilter/svndumpfilter$(EXEEXT) subversion/svnlook/svnlook$(EXEEXT) subversion/svnmucc/svnmucc$(EXEEXT) subversion/svnrdump/svnrdump$(EXEEXT) subversion/svnserve/svnserve$(EXEEXT) subversion/svnsync/svnsync$(EXEEXT) subversion/svnversion/svnversion$(EXEEXT)
$(MKDIR) $(DESTDIR)$(bindir)
cd subversion/svn ; $(INSTALL_BIN) svn$(EXEEXT) $(DESTDIR)$(bindir)/svn$(EXEEXT)
cd subversion/svnadmin ; $(INSTALL_BIN) svnadmin$(EXEEXT) $(DESTDIR)$(bindir)/svnadmin$(EXEEXT)
cd subversion/svndumpfilter ; $(INSTALL_BIN) svndumpfilter$(EXEEXT) $(DESTDIR)$(bindir)/svndumpfilter$(EXEEXT)
cd subversion/svnlook ; $(INSTALL_BIN) svnlook$(EXEEXT) $(DESTDIR)$(bindir)/svnlook$(EXEEXT)
cd subversion/svnmucc ; $(INSTALL_BIN) svnmucc$(EXEEXT) $(DESTDIR)$(bindir)/svnmucc$(EXEEXT)
cd subversion/svnrdump ; $(INSTALL_BIN) svnrdump$(EXEEXT) $(DESTDIR)$(bindir)/svnrdump$(EXEEXT)
cd subversion/svnserve ; $(INSTALL_BIN) svnserve$(EXEEXT) $(DESTDIR)$(bindir)/svnserve$(EXEEXT)
cd subversion/svnsync ; $(INSTALL_BIN) svnsync$(EXEEXT) $(DESTDIR)$(bindir)/svnsync$(EXEEXT)
cd subversion/svnversion ; $(INSTALL_BIN) svnversion$(EXEEXT) $(DESTDIR)$(bindir)/svnversion$(EXEEXT)
install-cxxhl-lib: subversion/bindings/cxxhl/libsvncxxhl-1.la
$(MKDIR) $(DESTDIR)$(cxxhl_libdir)
cd subversion/bindings/cxxhl ; $(INSTALL_CXXHL_LIB) libsvncxxhl-1.la $(DESTDIR)$(cxxhl_libdir)/libsvncxxhl-1.la
$(INSTALL_EXTRA_CXXHL_LIB)
install-fsmod-lib: subversion/libsvn_subr/libsvn_subr-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_fs_util/libsvn_fs_util-1.la subversion/libsvn_fs_fs/libsvn_fs_fs-1.la
$(MKDIR) $(DESTDIR)$(fsmod_libdir)
cd subversion/libsvn_subr ; $(INSTALL_FSMOD_LIB) libsvn_subr-1.la $(DESTDIR)$(fsmod_libdir)/libsvn_subr-1.la
cd subversion/libsvn_delta ; $(INSTALL_FSMOD_LIB) libsvn_delta-1.la $(DESTDIR)$(fsmod_libdir)/libsvn_delta-1.la
cd subversion/libsvn_fs_util ; $(INSTALL_FSMOD_LIB) libsvn_fs_util-1.la $(DESTDIR)$(fsmod_libdir)/libsvn_fs_util-1.la
cd subversion/libsvn_fs_fs ; $(INSTALL_FSMOD_LIB) libsvn_fs_fs-1.la $(DESTDIR)$(fsmod_libdir)/libsvn_fs_fs-1.la
install-gnome-keyring-lib: subversion/libsvn_auth_gnome_keyring/libsvn_auth_gnome_keyring-1.la
$(MKDIR) $(DESTDIR)$(gnome_keyring_libdir)
cd subversion/libsvn_auth_gnome_keyring ; $(INSTALL_GNOME_KEYRING_LIB) libsvn_auth_gnome_keyring-1.la $(DESTDIR)$(gnome_keyring_libdir)/libsvn_auth_gnome_keyring-1.la
install-javahl-callback-javah:
$(MKDIR) $(DESTDIR)$(javahl_callback_javahdir)
$(INSTALL_EXTRA_JAVAHL_CALLBACK_JAVAH)
install-javahl-compat-java:
$(MKDIR) $(DESTDIR)$(javahl_compat_javadir)
$(INSTALL_EXTRA_JAVAHL_COMPAT_JAVA)
install-javahl-compat-tests:
$(MKDIR) $(DESTDIR)$(javahl_compat_testsdir)
$(INSTALL_EXTRA_JAVAHL_COMPAT_TESTS)
install-javahl-java:
$(MKDIR) $(DESTDIR)$(javahl_javadir)
$(INSTALL_EXTRA_JAVAHL_JAVA)
install-javahl-javah:
$(MKDIR) $(DESTDIR)$(javahl_javahdir)
$(INSTALL_EXTRA_JAVAHL_JAVAH)
install-javahl-lib: subversion/bindings/javahl/native/libsvnjavahl-1.la
$(MKDIR) $(DESTDIR)$(javahl_libdir)
cd subversion/bindings/javahl/native ; $(INSTALL_JAVAHL_LIB) libsvnjavahl-1.la $(DESTDIR)$(javahl_libdir)/libsvnjavahl-1.la
$(INSTALL_EXTRA_JAVAHL_LIB)
install-javahl-tests:
$(MKDIR) $(DESTDIR)$(javahl_testsdir)
$(INSTALL_EXTRA_JAVAHL_TESTS)
install-javahl-types-javah:
$(MKDIR) $(DESTDIR)$(javahl_types_javahdir)
$(INSTALL_EXTRA_JAVAHL_TYPES_JAVAH)
install-kwallet-lib: subversion/libsvn_auth_kwallet/libsvn_auth_kwallet-1.la
$(MKDIR) $(DESTDIR)$(kwallet_libdir)
cd subversion/libsvn_auth_kwallet ; $(INSTALL_KWALLET_LIB) libsvn_auth_kwallet-1.la $(DESTDIR)$(kwallet_libdir)/libsvn_auth_kwallet-1.la
install-lib: subversion/libsvn_diff/libsvn_diff-1.la subversion/libsvn_ra/libsvn_ra-1.la subversion/libsvn_wc/libsvn_wc-1.la subversion/libsvn_client/libsvn_client-1.la
$(MKDIR) $(DESTDIR)$(libdir)
cd subversion/libsvn_diff ; $(INSTALL_LIB) libsvn_diff-1.la $(DESTDIR)$(libdir)/libsvn_diff-1.la
cd subversion/libsvn_ra ; $(INSTALL_LIB) libsvn_ra-1.la $(DESTDIR)$(libdir)/libsvn_ra-1.la
cd subversion/libsvn_wc ; $(INSTALL_LIB) libsvn_wc-1.la $(DESTDIR)$(libdir)/libsvn_wc-1.la
cd subversion/libsvn_client ; $(INSTALL_LIB) libsvn_client-1.la $(DESTDIR)$(libdir)/libsvn_client-1.la
install-locale: subversion/po/de.mo subversion/po/es.mo subversion/po/fr.mo subversion/po/it.mo subversion/po/ja.mo subversion/po/ko.mo subversion/po/nb.mo subversion/po/pl.mo subversion/po/pt_BR.mo subversion/po/sv.mo subversion/po/zh_CN.mo subversion/po/zh_TW.mo
$(MKDIR) $(DESTDIR)$(localedir)
$(MKDIR) $(DESTDIR)$(localedir)/de/LC_MESSAGES
cd subversion/po ; $(INSTALL_LOCALE) de.mo $(DESTDIR)$(localedir)/de/LC_MESSAGES/$(PACKAGE_NAME).mo
$(MKDIR) $(DESTDIR)$(localedir)/es/LC_MESSAGES
cd subversion/po ; $(INSTALL_LOCALE) es.mo $(DESTDIR)$(localedir)/es/LC_MESSAGES/$(PACKAGE_NAME).mo
$(MKDIR) $(DESTDIR)$(localedir)/fr/LC_MESSAGES
cd subversion/po ; $(INSTALL_LOCALE) fr.mo $(DESTDIR)$(localedir)/fr/LC_MESSAGES/$(PACKAGE_NAME).mo
$(MKDIR) $(DESTDIR)$(localedir)/it/LC_MESSAGES
cd subversion/po ; $(INSTALL_LOCALE) it.mo $(DESTDIR)$(localedir)/it/LC_MESSAGES/$(PACKAGE_NAME).mo
$(MKDIR) $(DESTDIR)$(localedir)/ja/LC_MESSAGES
cd subversion/po ; $(INSTALL_LOCALE) ja.mo $(DESTDIR)$(localedir)/ja/LC_MESSAGES/$(PACKAGE_NAME).mo
$(MKDIR) $(DESTDIR)$(localedir)/ko/LC_MESSAGES
cd subversion/po ; $(INSTALL_LOCALE) ko.mo $(DESTDIR)$(localedir)/ko/LC_MESSAGES/$(PACKAGE_NAME).mo
$(MKDIR) $(DESTDIR)$(localedir)/nb/LC_MESSAGES
cd subversion/po ; $(INSTALL_LOCALE) nb.mo $(DESTDIR)$(localedir)/nb/LC_MESSAGES/$(PACKAGE_NAME).mo
$(MKDIR) $(DESTDIR)$(localedir)/pl/LC_MESSAGES
cd subversion/po ; $(INSTALL_LOCALE) pl.mo $(DESTDIR)$(localedir)/pl/LC_MESSAGES/$(PACKAGE_NAME).mo
$(MKDIR) $(DESTDIR)$(localedir)/pt_BR/LC_MESSAGES
cd subversion/po ; $(INSTALL_LOCALE) pt_BR.mo $(DESTDIR)$(localedir)/pt_BR/LC_MESSAGES/$(PACKAGE_NAME).mo
$(MKDIR) $(DESTDIR)$(localedir)/sv/LC_MESSAGES
cd subversion/po ; $(INSTALL_LOCALE) sv.mo $(DESTDIR)$(localedir)/sv/LC_MESSAGES/$(PACKAGE_NAME).mo
$(MKDIR) $(DESTDIR)$(localedir)/zh_CN/LC_MESSAGES
cd subversion/po ; $(INSTALL_LOCALE) zh_CN.mo $(DESTDIR)$(localedir)/zh_CN/LC_MESSAGES/$(PACKAGE_NAME).mo
$(MKDIR) $(DESTDIR)$(localedir)/zh_TW/LC_MESSAGES
cd subversion/po ; $(INSTALL_LOCALE) zh_TW.mo $(DESTDIR)$(localedir)/zh_TW/LC_MESSAGES/$(PACKAGE_NAME).mo
install-ramod-lib: subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_ra_svn/libsvn_ra_svn-1.la subversion/libsvn_repos/libsvn_repos-1.la subversion/libsvn_ra_local/libsvn_ra_local-1.la
$(MKDIR) $(DESTDIR)$(ramod_libdir)
cd subversion/libsvn_fs ; $(INSTALL_RAMOD_LIB) libsvn_fs-1.la $(DESTDIR)$(ramod_libdir)/libsvn_fs-1.la
cd subversion/libsvn_ra_svn ; $(INSTALL_RAMOD_LIB) libsvn_ra_svn-1.la $(DESTDIR)$(ramod_libdir)/libsvn_ra_svn-1.la
cd subversion/libsvn_repos ; $(INSTALL_RAMOD_LIB) libsvn_repos-1.la $(DESTDIR)$(ramod_libdir)/libsvn_repos-1.la
cd subversion/libsvn_ra_local ; $(INSTALL_RAMOD_LIB) libsvn_ra_local-1.la $(DESTDIR)$(ramod_libdir)/libsvn_ra_local-1.la
install-serf-lib: subversion/libsvn_ra_serf/libsvn_ra_serf-1.la
$(MKDIR) $(DESTDIR)$(serf_libdir)
cd subversion/libsvn_ra_serf ; $(INSTALL_SERF_LIB) libsvn_ra_serf-1.la $(DESTDIR)$(serf_libdir)/libsvn_ra_serf-1.la
install-sub-test: subversion/tests/libsvn_subr/named_atomic-proc-test$(EXEEXT)
$(MKDIR) $(DESTDIR)$(sub_testdir)
cd subversion/tests/libsvn_subr ; $(INSTALL_SUB_TEST) named_atomic-proc-test$(EXEEXT) $(DESTDIR)$(sub_testdir)/named_atomic-proc-test$(EXEEXT)
install-swig-pl-lib: subversion/bindings/swig/perl/libsvn_swig_perl/libsvn_swig_perl-1.la
$(MKDIR) $(DESTDIR)$(swig_pl_libdir)
cd subversion/bindings/swig/perl/libsvn_swig_perl ; $(INSTALL_SWIG_PL_LIB) libsvn_swig_perl-1.la $(DESTDIR)$(swig_pl_libdir)/libsvn_swig_perl-1.la
install-swig-py: subversion/bindings/swig/python/_core.la subversion/bindings/swig/python/_client.la subversion/bindings/swig/python/_delta.la subversion/bindings/swig/python/_diff.la subversion/bindings/swig/python/_fs.la subversion/bindings/swig/python/_ra.la subversion/bindings/swig/python/_repos.la subversion/bindings/swig/python/_wc.la
$(MKDIR) $(DESTDIR)$(swig_pydir)
cd subversion/bindings/swig/python ; $(INSTALL_SWIG_PY) _core.la $(DESTDIR)$(swig_pydir)/_core.la
cd subversion/bindings/swig/python ; $(INSTALL_SWIG_PY) _client.la $(DESTDIR)$(swig_pydir)/_client.la
cd subversion/bindings/swig/python ; $(INSTALL_SWIG_PY) _delta.la $(DESTDIR)$(swig_pydir)/_delta.la
cd subversion/bindings/swig/python ; $(INSTALL_SWIG_PY) _diff.la $(DESTDIR)$(swig_pydir)/_diff.la
cd subversion/bindings/swig/python ; $(INSTALL_SWIG_PY) _fs.la $(DESTDIR)$(swig_pydir)/_fs.la
cd subversion/bindings/swig/python ; $(INSTALL_SWIG_PY) _ra.la $(DESTDIR)$(swig_pydir)/_ra.la
cd subversion/bindings/swig/python ; $(INSTALL_SWIG_PY) _repos.la $(DESTDIR)$(swig_pydir)/_repos.la
cd subversion/bindings/swig/python ; $(INSTALL_SWIG_PY) _wc.la $(DESTDIR)$(swig_pydir)/_wc.la
$(INSTALL_EXTRA_SWIG_PY)
install-swig-py-lib: subversion/bindings/swig/python/libsvn_swig_py/libsvn_swig_py-1.la
$(MKDIR) $(DESTDIR)$(swig_py_libdir)
cd subversion/bindings/swig/python/libsvn_swig_py ; $(INSTALL_SWIG_PY_LIB) libsvn_swig_py-1.la $(DESTDIR)$(swig_py_libdir)/libsvn_swig_py-1.la
install-swig-rb: subversion/bindings/swig/ruby/core.la subversion/bindings/swig/ruby/client.la subversion/bindings/swig/ruby/delta.la subversion/bindings/swig/ruby/diff.la subversion/bindings/swig/ruby/fs.la subversion/bindings/swig/ruby/ra.la subversion/bindings/swig/ruby/repos.la subversion/bindings/swig/ruby/wc.la
$(MKDIR) $(DESTDIR)$(swig_rbdir)
cd subversion/bindings/swig/ruby ; $(INSTALL_SWIG_RB) core.la $(DESTDIR)$(swig_rbdir)/core.la
cd subversion/bindings/swig/ruby ; $(INSTALL_SWIG_RB) client.la $(DESTDIR)$(swig_rbdir)/client.la
cd subversion/bindings/swig/ruby ; $(INSTALL_SWIG_RB) delta.la $(DESTDIR)$(swig_rbdir)/delta.la
cd subversion/bindings/swig/ruby ; $(INSTALL_SWIG_RB) diff.la $(DESTDIR)$(swig_rbdir)/diff.la
cd subversion/bindings/swig/ruby ; $(INSTALL_SWIG_RB) fs.la $(DESTDIR)$(swig_rbdir)/fs.la
cd subversion/bindings/swig/ruby ; $(INSTALL_SWIG_RB) ra.la $(DESTDIR)$(swig_rbdir)/ra.la
cd subversion/bindings/swig/ruby ; $(INSTALL_SWIG_RB) repos.la $(DESTDIR)$(swig_rbdir)/repos.la
cd subversion/bindings/swig/ruby ; $(INSTALL_SWIG_RB) wc.la $(DESTDIR)$(swig_rbdir)/wc.la
$(INSTALL_EXTRA_SWIG_RB)
install-swig-rb-lib: subversion/bindings/swig/ruby/libsvn_swig_ruby/libsvn_swig_ruby-1.la
$(MKDIR) $(DESTDIR)$(swig_rb_libdir)
cd subversion/bindings/swig/ruby/libsvn_swig_ruby ; $(INSTALL_SWIG_RB_LIB) libsvn_swig_ruby-1.la $(DESTDIR)$(swig_rb_libdir)/libsvn_swig_ruby-1.la
install-tests: subversion/bindings/cxxhl/cxxhl-tests$(EXEEXT)
$(MKDIR) $(DESTDIR)$(testsdir)
cd subversion/bindings/cxxhl ; $(INSTALL_TESTS) cxxhl-tests$(EXEEXT) $(DESTDIR)$(testsdir)/cxxhl-tests$(EXEEXT)
install-tools: tools/diff/diff$(EXEEXT) tools/diff/diff3$(EXEEXT) tools/diff/diff4$(EXEEXT) tools/dev/fsfs-access-map$(EXEEXT) tools/dev/fsfs-reorg$(EXEEXT) tools/server-side/fsfs-stats$(EXEEXT) tools/server-side/mod_dontdothat/mod_dontdothat.la tools/client-side/svn-bench/svn-bench$(EXEEXT) tools/server-side/svn-populate-node-origins-index$(EXEEXT) tools/server-side/svn-rep-sharing-stats$(EXEEXT) tools/server-side/svnauthz$(EXEEXT) tools/server-side/svnauthz-validate$(EXEEXT) tools/dev/svnraisetreeconflict/svnraisetreeconflict$(EXEEXT)
$(MKDIR) $(DESTDIR)$(toolsdir)
cd tools/diff ; $(INSTALL_TOOLS) diff$(EXEEXT) $(DESTDIR)$(toolsdir)/diff$(EXEEXT)
cd tools/diff ; $(INSTALL_TOOLS) diff3$(EXEEXT) $(DESTDIR)$(toolsdir)/diff3$(EXEEXT)
cd tools/diff ; $(INSTALL_TOOLS) diff4$(EXEEXT) $(DESTDIR)$(toolsdir)/diff4$(EXEEXT)
cd tools/dev ; $(INSTALL_TOOLS) fsfs-access-map$(EXEEXT) $(DESTDIR)$(toolsdir)/fsfs-access-map$(EXEEXT)
cd tools/dev ; $(INSTALL_TOOLS) fsfs-reorg$(EXEEXT) $(DESTDIR)$(toolsdir)/fsfs-reorg$(EXEEXT)
cd tools/server-side ; $(INSTALL_TOOLS) fsfs-stats$(EXEEXT) $(DESTDIR)$(toolsdir)/fsfs-stats$(EXEEXT)
if $(INSTALL_APACHE_MODS) ; then cd tools/server-side/mod_dontdothat ; $(MKDIR) "$(APACHE_LIBEXECDIR)" ; $(INSTALL_MOD_SHARED) -n dontdothat mod_dontdothat.la ; fi
cd tools/client-side/svn-bench ; $(INSTALL_TOOLS) svn-bench$(EXEEXT) $(DESTDIR)$(toolsdir)/svn-bench$(EXEEXT)
cd tools/server-side ; $(INSTALL_TOOLS) svn-populate-node-origins-index$(EXEEXT) $(DESTDIR)$(toolsdir)/svn-populate-node-origins-index$(EXEEXT)
cd tools/server-side ; $(INSTALL_TOOLS) svn-rep-sharing-stats$(EXEEXT) $(DESTDIR)$(toolsdir)/svn-rep-sharing-stats$(EXEEXT)
cd tools/server-side ; $(INSTALL_TOOLS) svnauthz$(EXEEXT) $(DESTDIR)$(toolsdir)/svnauthz$(EXEEXT)
cd tools/server-side ; $(INSTALL_TOOLS) svnauthz-validate$(EXEEXT) $(DESTDIR)$(toolsdir)/svnauthz-validate$(EXEEXT)
cd tools/dev/svnraisetreeconflict ; $(INSTALL_TOOLS) svnraisetreeconflict$(EXEEXT) $(DESTDIR)$(toolsdir)/svnraisetreeconflict$(EXEEXT)
$(INSTALL_EXTRA_TOOLS)
########################################
# Section 8: The install-include rule
########################################
install-include: subversion/include/mod_authz_svn.h subversion/include/mod_dav_svn.h subversion/include/svn_auth.h subversion/include/svn_base64.h subversion/include/svn_cache_config.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_compat.h subversion/include/svn_config.h subversion/include/svn_ctype.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_dso.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_iter.h subversion/include/svn_md5.h subversion/include/svn_mergeinfo.h subversion/include/svn_nls.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_quoprint.h subversion/include/svn_ra.h subversion/include/svn_ra_svn.h subversion/include/svn_repos.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_user.h subversion/include/svn_utf.h subversion/include/svn_version.h subversion/include/svn_wc.h subversion/include/svn_xml.h
$(MKDIR) $(DESTDIR)$(includedir)/subversion-1
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/mod_authz_svn.h $(DESTDIR)$(includedir)/subversion-1/mod_authz_svn.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/mod_dav_svn.h $(DESTDIR)$(includedir)/subversion-1/mod_dav_svn.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_auth.h $(DESTDIR)$(includedir)/subversion-1/svn_auth.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_base64.h $(DESTDIR)$(includedir)/subversion-1/svn_base64.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_cache_config.h $(DESTDIR)$(includedir)/subversion-1/svn_cache_config.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_checksum.h $(DESTDIR)$(includedir)/subversion-1/svn_checksum.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_client.h $(DESTDIR)$(includedir)/subversion-1/svn_client.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_cmdline.h $(DESTDIR)$(includedir)/subversion-1/svn_cmdline.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_compat.h $(DESTDIR)$(includedir)/subversion-1/svn_compat.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_config.h $(DESTDIR)$(includedir)/subversion-1/svn_config.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_ctype.h $(DESTDIR)$(includedir)/subversion-1/svn_ctype.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_dav.h $(DESTDIR)$(includedir)/subversion-1/svn_dav.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_delta.h $(DESTDIR)$(includedir)/subversion-1/svn_delta.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_diff.h $(DESTDIR)$(includedir)/subversion-1/svn_diff.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_dirent_uri.h $(DESTDIR)$(includedir)/subversion-1/svn_dirent_uri.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_dso.h $(DESTDIR)$(includedir)/subversion-1/svn_dso.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_error.h $(DESTDIR)$(includedir)/subversion-1/svn_error.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_error_codes.h $(DESTDIR)$(includedir)/subversion-1/svn_error_codes.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_fs.h $(DESTDIR)$(includedir)/subversion-1/svn_fs.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_hash.h $(DESTDIR)$(includedir)/subversion-1/svn_hash.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_io.h $(DESTDIR)$(includedir)/subversion-1/svn_io.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_iter.h $(DESTDIR)$(includedir)/subversion-1/svn_iter.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_md5.h $(DESTDIR)$(includedir)/subversion-1/svn_md5.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_mergeinfo.h $(DESTDIR)$(includedir)/subversion-1/svn_mergeinfo.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_nls.h $(DESTDIR)$(includedir)/subversion-1/svn_nls.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_opt.h $(DESTDIR)$(includedir)/subversion-1/svn_opt.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_path.h $(DESTDIR)$(includedir)/subversion-1/svn_path.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_pools.h $(DESTDIR)$(includedir)/subversion-1/svn_pools.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_props.h $(DESTDIR)$(includedir)/subversion-1/svn_props.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_quoprint.h $(DESTDIR)$(includedir)/subversion-1/svn_quoprint.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_ra.h $(DESTDIR)$(includedir)/subversion-1/svn_ra.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_ra_svn.h $(DESTDIR)$(includedir)/subversion-1/svn_ra_svn.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_repos.h $(DESTDIR)$(includedir)/subversion-1/svn_repos.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_sorts.h $(DESTDIR)$(includedir)/subversion-1/svn_sorts.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_string.h $(DESTDIR)$(includedir)/subversion-1/svn_string.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_subst.h $(DESTDIR)$(includedir)/subversion-1/svn_subst.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_time.h $(DESTDIR)$(includedir)/subversion-1/svn_time.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_types.h $(DESTDIR)$(includedir)/subversion-1/svn_types.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_user.h $(DESTDIR)$(includedir)/subversion-1/svn_user.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_utf.h $(DESTDIR)$(includedir)/subversion-1/svn_utf.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_version.h $(DESTDIR)$(includedir)/subversion-1/svn_version.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_wc.h $(DESTDIR)$(includedir)/subversion-1/svn_wc.h
$(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_xml.h $(DESTDIR)$(includedir)/subversion-1/svn_xml.h
########################################
# Section 9: Shortcut targets for manual builds of specific items
########################################
atomic-ra-revprop-change: subversion/tests/cmdline/atomic-ra-revprop-change$(EXEEXT)
auth-test: subversion/tests/libsvn_subr/auth-test$(EXEEXT)
cache-test: subversion/tests/libsvn_subr/cache-test$(EXEEXT)
changes-test: subversion/tests/libsvn_fs_base/changes-test$(EXEEXT)
checksum-test: subversion/tests/libsvn_subr/checksum-test$(EXEEXT)
client-test: subversion/tests/libsvn_client/client-test$(EXEEXT)
compat-test: subversion/tests/libsvn_subr/compat-test$(EXEEXT)
config-test: subversion/tests/libsvn_subr/config-test$(EXEEXT)
conflict-data-test: subversion/tests/libsvn_wc/conflict-data-test$(EXEEXT)
crypto-test: subversion/tests/libsvn_subr/crypto-test$(EXEEXT)
cxxhl-tests: subversion/bindings/cxxhl/cxxhl-tests$(EXEEXT)
db-test: subversion/tests/libsvn_wc/db-test$(EXEEXT)
diff: tools/diff/diff$(EXEEXT)
diff-diff3-test: subversion/tests/libsvn_diff/diff-diff3-test$(EXEEXT)
diff3: tools/diff/diff3$(EXEEXT)
diff4: tools/diff/diff4$(EXEEXT)
dirent_uri-test: subversion/tests/libsvn_subr/dirent_uri-test$(EXEEXT)
entries-compat-test: subversion/tests/libsvn_wc/entries-compat-test$(EXEEXT)
entries-dump: subversion/tests/cmdline/entries-dump$(EXEEXT)
error-code-test: subversion/tests/libsvn_subr/error-code-test$(EXEEXT)
error-test: subversion/tests/libsvn_subr/error-test$(EXEEXT)
fs-base-test: subversion/tests/libsvn_fs_base/fs-base-test$(EXEEXT)
fs-pack-test: subversion/tests/libsvn_fs_fs/fs-pack-test$(EXEEXT)
fs-test: subversion/tests/libsvn_fs/fs-test$(EXEEXT)
fsfs-access-map: tools/dev/fsfs-access-map$(EXEEXT)
fsfs-reorg: tools/dev/fsfs-reorg$(EXEEXT)
fsfs-stats: tools/server-side/fsfs-stats$(EXEEXT)
hashdump-test: subversion/tests/libsvn_subr/hashdump-test$(EXEEXT)
io-test: subversion/tests/libsvn_subr/io-test$(EXEEXT)
libsvn_auth_gnome_keyring: subversion/libsvn_auth_gnome_keyring/libsvn_auth_gnome_keyring-1.la
libsvn_auth_kwallet: subversion/libsvn_auth_kwallet/libsvn_auth_kwallet-1.la
libsvn_client: subversion/libsvn_client/libsvn_client-1.la
libsvn_delta: subversion/libsvn_delta/libsvn_delta-1.la
libsvn_diff: subversion/libsvn_diff/libsvn_diff-1.la
libsvn_fs: subversion/libsvn_fs/libsvn_fs-1.la
libsvn_fs_base: subversion/libsvn_fs_base/libsvn_fs_base-1.la
libsvn_fs_fs: subversion/libsvn_fs_fs/libsvn_fs_fs-1.la
libsvn_fs_util: subversion/libsvn_fs_util/libsvn_fs_util-1.la
libsvn_ra: subversion/libsvn_ra/libsvn_ra-1.la
libsvn_ra_local: subversion/libsvn_ra_local/libsvn_ra_local-1.la
libsvn_ra_serf: subversion/libsvn_ra_serf/libsvn_ra_serf-1.la
libsvn_ra_svn: subversion/libsvn_ra_svn/libsvn_ra_svn-1.la
libsvn_repos: subversion/libsvn_repos/libsvn_repos-1.la
libsvn_subr: subversion/libsvn_subr/libsvn_subr-1.la
libsvn_swig_perl: subversion/bindings/swig/perl/libsvn_swig_perl/libsvn_swig_perl-1.la
libsvn_swig_py: subversion/bindings/swig/python/libsvn_swig_py/libsvn_swig_py-1.la
libsvn_swig_ruby: subversion/bindings/swig/ruby/libsvn_swig_ruby/libsvn_swig_ruby-1.la
libsvn_test: subversion/tests/libsvn_test-1.la
libsvn_wc: subversion/libsvn_wc/libsvn_wc-1.la
libsvncxxhl: subversion/bindings/cxxhl/libsvncxxhl-1.la
libsvnjavahl: subversion/bindings/javahl/native/libsvnjavahl-1.la
locks-test: subversion/tests/libsvn_fs/locks-test$(EXEEXT)
mergeinfo-test: subversion/tests/libsvn_subr/mergeinfo-test$(EXEEXT)
mod_authz_svn: subversion/mod_authz_svn/mod_authz_svn.la
mod_dav_svn: subversion/mod_dav_svn/mod_dav_svn.la
mod_dontdothat: tools/server-side/mod_dontdothat/mod_dontdothat.la
named_atomic-proc-test: subversion/tests/libsvn_subr/named_atomic-proc-test$(EXEEXT)
named_atomic-test: subversion/tests/libsvn_subr/named_atomic-test$(EXEEXT)
op-depth-test: subversion/tests/libsvn_wc/op-depth-test$(EXEEXT)
opt-test: subversion/tests/libsvn_subr/opt-test$(EXEEXT)
parse-diff-test: subversion/tests/libsvn_diff/parse-diff-test$(EXEEXT)
path-test: subversion/tests/libsvn_subr/path-test$(EXEEXT)
perl_client: subversion/bindings/swig/perl/native/_Client.la
perl_core: subversion/bindings/swig/perl/native/_Core.la
perl_delta: subversion/bindings/swig/perl/native/_Delta.la
perl_diff: subversion/bindings/swig/perl/native/_Diff.la
perl_fs: subversion/bindings/swig/perl/native/_Fs.la
perl_ra: subversion/bindings/swig/perl/native/_Ra.la
perl_repos: subversion/bindings/swig/perl/native/_Repos.la
perl_wc: subversion/bindings/swig/perl/native/_Wc.la
pristine-store-test: subversion/tests/libsvn_wc/pristine-store-test$(EXEEXT)
python_client: subversion/bindings/swig/python/_client.la
python_core: subversion/bindings/swig/python/_core.la
python_delta: subversion/bindings/swig/python/_delta.la
python_diff: subversion/bindings/swig/python/_diff.la
python_fs: subversion/bindings/swig/python/_fs.la
python_ra: subversion/bindings/swig/python/_ra.la
python_repos: subversion/bindings/swig/python/_repos.la
python_wc: subversion/bindings/swig/python/_wc.la
ra-local-test: subversion/tests/libsvn_ra_local/ra-local-test$(EXEEXT)
ra-test: subversion/tests/libsvn_ra/ra-test$(EXEEXT)
random-test: subversion/tests/libsvn_delta/random-test$(EXEEXT)
repos-test: subversion/tests/libsvn_repos/repos-test$(EXEEXT)
revision-test: subversion/tests/libsvn_subr/revision-test$(EXEEXT)
ruby_client: subversion/bindings/swig/ruby/client.la
ruby_core: subversion/bindings/swig/ruby/core.la
ruby_delta: subversion/bindings/swig/ruby/delta.la
ruby_diff: subversion/bindings/swig/ruby/diff.la
ruby_fs: subversion/bindings/swig/ruby/fs.la
ruby_ra: subversion/bindings/swig/ruby/ra.la
ruby_repos: subversion/bindings/swig/ruby/repos.la
ruby_wc: subversion/bindings/swig/ruby/wc.la
skel-test: subversion/tests/libsvn_subr/skel-test$(EXEEXT)
spillbuf-test: subversion/tests/libsvn_subr/spillbuf-test$(EXEEXT)
stream-test: subversion/tests/libsvn_subr/stream-test$(EXEEXT)
string-test: subversion/tests/libsvn_subr/string-test$(EXEEXT)
strings-reps-test: subversion/tests/libsvn_fs_base/strings-reps-test$(EXEEXT)
subst_translate-test: subversion/tests/libsvn_subr/subst_translate-test$(EXEEXT)
svn: subversion/svn/svn$(EXEEXT)
svn-bench: tools/client-side/svn-bench/svn-bench$(EXEEXT)
svn-populate-node-origins-index: tools/server-side/svn-populate-node-origins-index$(EXEEXT)
svn-rep-sharing-stats: tools/server-side/svn-rep-sharing-stats$(EXEEXT)
svnadmin: subversion/svnadmin/svnadmin$(EXEEXT)
svnauthz: tools/server-side/svnauthz$(EXEEXT)
svnauthz-validate: tools/server-side/svnauthz-validate$(EXEEXT)
svndiff-test: subversion/tests/libsvn_delta/svndiff-test$(EXEEXT)
svndumpfilter: subversion/svndumpfilter/svndumpfilter$(EXEEXT)
svnlook: subversion/svnlook/svnlook$(EXEEXT)
svnmucc: subversion/svnmucc/svnmucc$(EXEEXT)
svnraisetreeconflict: tools/dev/svnraisetreeconflict/svnraisetreeconflict$(EXEEXT)
svnrdump: subversion/svnrdump/svnrdump$(EXEEXT)
svnserve: subversion/svnserve/svnserve$(EXEEXT)
svnsync: subversion/svnsync/svnsync$(EXEEXT)
svnversion: subversion/svnversion/svnversion$(EXEEXT)
time-test: subversion/tests/libsvn_subr/time-test$(EXEEXT)
translate-test: subversion/tests/libsvn_subr/translate-test$(EXEEXT)
utf-test: subversion/tests/libsvn_subr/utf-test$(EXEEXT)
vdelta-test: subversion/tests/libsvn_delta/vdelta-test$(EXEEXT)
wc-incomplete-tester: subversion/tests/libsvn_wc/wc-incomplete-tester$(EXEEXT)
wc-lock-tester: subversion/tests/libsvn_wc/wc-lock-tester$(EXEEXT)
wc-queries-test: subversion/tests/libsvn_wc/wc-queries-test$(EXEEXT)
wc-test: subversion/tests/libsvn_wc/wc-test$(EXEEXT)
window-test: subversion/tests/libsvn_delta/window-test$(EXEEXT)
########################################
# Section 10: Rules to build all other kinds of object-like files
########################################
subversion/bindings/cxxhl/src/exception.lo: subversion/bindings/cxxhl/src/exception.cpp subversion/bindings/cxxhl/include/svncxxhl/_compat.hpp subversion/bindings/cxxhl/include/svncxxhl/exception.hpp subversion/include/private/svn_atomic.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_error_private.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/svn_private_config.h
$(COMPILE_CXXHL_CXX) $(canonicalized_srcdir)subversion/bindings/cxxhl/src/exception.cpp
subversion/bindings/cxxhl/src/tristate.lo: subversion/bindings/cxxhl/src/tristate.cpp subversion/bindings/cxxhl/include/svncxxhl/tristate.hpp subversion/include/private/svn_debug.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_types.h
$(COMPILE_CXXHL_CXX) $(canonicalized_srcdir)subversion/bindings/cxxhl/src/tristate.cpp
subversion/bindings/cxxhl/tests/test_exception.lo: subversion/bindings/cxxhl/tests/test_exception.cpp subversion/bindings/cxxhl/include/svncxxhl.hpp subversion/bindings/cxxhl/include/svncxxhl/_compat.hpp subversion/bindings/cxxhl/include/svncxxhl/exception.hpp subversion/bindings/cxxhl/include/svncxxhl/tristate.hpp subversion/include/private/svn_debug.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_types.h
$(COMPILE_CXXHL_CXX) $(canonicalized_srcdir)subversion/bindings/cxxhl/tests/test_exception.cpp
subversion/bindings/javahl/classes/org/apache/subversion/javahl/BasicTests.class: subversion/bindings/javahl/tests/org/apache/subversion/javahl/BasicTests.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/ClientException.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/ClientException.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/ClientNotifyInformation.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/ClientNotifyInformation.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/CommitInfo.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/CommitInfo.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/CommitItem.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/CommitItem.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/CommitItemStateFlags.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/CommitItemStateFlags.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/ConflictDescriptor.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/ConflictDescriptor.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/ConflictResult.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/ConflictResult.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/DiffSummary.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/DiffSummary.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/ISVNClient.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNClient.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/ISVNRepos.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNRepos.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/JNIError.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/JNIError.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/NativeException.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeException.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/NativeResources.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeResources.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/ProgressEvent.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/ProgressEvent.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/ReposNotifyInformation.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/ReposNotifyInformation.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/RunTests.class: subversion/bindings/javahl/tests/org/apache/subversion/javahl/RunTests.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/SVNClient.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/SVNClient.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/SVNRepos.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/SVNRepos.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/SVNReposTests.class: subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNReposTests.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/SVNTests.class: subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNTests.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/SubversionException.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/SubversionException.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/WC.class: subversion/bindings/javahl/tests/org/apache/subversion/javahl/WC.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/BlameCallback.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/BlameCallback.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ChangelistCallback.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/ChangelistCallback.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ClientNotifyCallback.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/ClientNotifyCallback.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/CommitCallback.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/CommitCallback.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/CommitMessageCallback.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/CommitMessageCallback.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ConflictResolverCallback.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/ConflictResolverCallback.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/DiffSummaryCallback.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/DiffSummaryCallback.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ImportFilterCallback.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/ImportFilterCallback.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/InfoCallback.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/InfoCallback.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/InheritedProplistCallback.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/InheritedProplistCallback.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ListCallback.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/ListCallback.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/LogMessageCallback.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/LogMessageCallback.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/PatchCallback.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/PatchCallback.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ProgressCallback.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/ProgressCallback.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ProplistCallback.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/ProplistCallback.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ReposFreezeAction.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/ReposFreezeAction.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ReposNotifyCallback.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/ReposNotifyCallback.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/StatusCallback.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/StatusCallback.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/UserPasswordCallback.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/UserPasswordCallback.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/ChangePath.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/types/ChangePath.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Checksum.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/types/Checksum.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/ConflictVersion.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/types/ConflictVersion.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/CopySource.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/types/CopySource.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Depth.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/types/Depth.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/DiffOptions.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/types/DiffOptions.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/DirEntry.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/types/DirEntry.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Info.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/types/Info.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Lock.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/types/Lock.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/LogDate.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/types/LogDate.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Mergeinfo.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/types/Mergeinfo.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/NodeKind.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/types/NodeKind.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Property.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/types/Property.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Revision.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/types/Revision.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/RevisionRange.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/types/RevisionRange.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Status.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/types/Status.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Tristate.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/types/Tristate.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Version.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/types/Version.java
subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/VersionExtended.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/types/VersionExtended.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/BasicTests.class: subversion/bindings/javahl/tests/org/tigris/subversion/javahl/BasicTests.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/BlameCallback.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/BlameCallback.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/BlameCallback2.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/BlameCallback2.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/BlameCallback3.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/BlameCallback3.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/BlameCallbackImpl.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/BlameCallbackImpl.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/ChangePath.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/ChangePath.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/ChangelistCallback.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/ChangelistCallback.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/ClientException.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/ClientException.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/CommitItem.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/CommitItem.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/CommitItemStateFlags.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/CommitItemStateFlags.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/CommitMessage.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/CommitMessage.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/ConflictDescriptor.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/ConflictDescriptor.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/ConflictResolverCallback.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/ConflictResolverCallback.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/ConflictResult.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/ConflictResult.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/ConflictVersion.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/ConflictVersion.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/CopySource.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/CopySource.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/Depth.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/Depth.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/DiffSummary.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/DiffSummary.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/DiffSummaryReceiver.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/DiffSummaryReceiver.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/DirEntry.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/DirEntry.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/ErrorCodes.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/ErrorCodes.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/Info.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/Info.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/Info2.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/Info2.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/InfoCallback.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/InfoCallback.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/InputInterface.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/InputInterface.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/ListCallback.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/ListCallback.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/Lock.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/Lock.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/LockStatus.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/LockStatus.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/LogDate.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/LogDate.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/LogMessage.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/LogMessage.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/LogMessageCallback.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/LogMessageCallback.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/Mergeinfo.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/Mergeinfo.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/MergeinfoLogKind.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/MergeinfoLogKind.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/NativeException.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/NativeException.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/NodeKind.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/NodeKind.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/Notify.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/Notify.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/Notify2.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/Notify2.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/NotifyAction.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/NotifyAction.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/NotifyInformation.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/NotifyInformation.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/NotifyStatus.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/NotifyStatus.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/Operation.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/Operation.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/OutputInterface.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/OutputInterface.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/Path.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/Path.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/ProgressEvent.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/ProgressEvent.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/ProgressListener.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/ProgressListener.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/PromptUserPassword.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/PromptUserPassword.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/PromptUserPassword2.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/PromptUserPassword2.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/PromptUserPassword3.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/PromptUserPassword3.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/PropertyData.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/PropertyData.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/ProplistCallback.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/ProplistCallback.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/ProplistCallbackImpl.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/ProplistCallbackImpl.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/Revision.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/Revision.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/RevisionKind.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/RevisionKind.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/RevisionRange.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/RevisionRange.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/RunTests.class: subversion/bindings/javahl/tests/org/tigris/subversion/javahl/RunTests.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/SVNAdmin.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/SVNAdmin.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/SVNAdminTests.class: subversion/bindings/javahl/tests/org/tigris/subversion/javahl/SVNAdminTests.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/SVNClient.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/SVNClient.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/SVNClientInterface.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/SVNClientInterface.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/SVNClientLogLevel.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/SVNClientLogLevel.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/SVNClientSynchronized.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/SVNClientSynchronized.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/SVNInputStream.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/SVNInputStream.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/SVNOutputStream.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/SVNOutputStream.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/SVNTests.class: subversion/bindings/javahl/tests/org/tigris/subversion/javahl/SVNTests.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/ScheduleKind.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/ScheduleKind.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/Status.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/Status.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/StatusCallback.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/StatusCallback.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/StatusKind.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/StatusKind.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/SubversionException.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/SubversionException.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/Version.class: subversion/bindings/javahl/src/org/tigris/subversion/javahl/Version.java
subversion/bindings/javahl/classes/org/tigris/subversion/javahl/WC.class: subversion/bindings/javahl/tests/org/tigris/subversion/javahl/WC.java
subversion/bindings/javahl/include/BlameCallback.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/BlameCallback.class
subversion/bindings/javahl/include/ChangePath.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/ChangePath.class
subversion/bindings/javahl/include/ChangelistCallback.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ChangelistCallback.class
subversion/bindings/javahl/include/Checksum.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Checksum.class
subversion/bindings/javahl/include/ClientException.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/ClientException.class
subversion/bindings/javahl/include/ClientNotifyCallback.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ClientNotifyCallback.class
subversion/bindings/javahl/include/ClientNotifyInformation.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/ClientNotifyInformation.class
subversion/bindings/javahl/include/CommitCallback.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/CommitCallback.class
subversion/bindings/javahl/include/CommitInfo.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/CommitInfo.class
subversion/bindings/javahl/include/CommitItem.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/CommitItem.class
subversion/bindings/javahl/include/CommitItemStateFlags.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/CommitItemStateFlags.class
subversion/bindings/javahl/include/CommitMessageCallback.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/CommitMessageCallback.class
subversion/bindings/javahl/include/ConflictDescriptor.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/ConflictDescriptor.class
subversion/bindings/javahl/include/ConflictResolverCallback.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ConflictResolverCallback.class
subversion/bindings/javahl/include/ConflictResult.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/ConflictResult.class
subversion/bindings/javahl/include/ConflictVersion.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/ConflictVersion.class
subversion/bindings/javahl/include/CopySource.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/CopySource.class
subversion/bindings/javahl/include/Depth.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Depth.class
subversion/bindings/javahl/include/DiffOptions.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/DiffOptions.class
subversion/bindings/javahl/include/DiffSummary.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/DiffSummary.class
subversion/bindings/javahl/include/DiffSummaryCallback.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/DiffSummaryCallback.class
subversion/bindings/javahl/include/DirEntry.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/DirEntry.class
subversion/bindings/javahl/include/ISVNClient.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/ISVNClient.class
subversion/bindings/javahl/include/ISVNRepos.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/ISVNRepos.class
subversion/bindings/javahl/include/ImportFilterCallback.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ImportFilterCallback.class
subversion/bindings/javahl/include/Info.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Info.class
subversion/bindings/javahl/include/InfoCallback.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/InfoCallback.class
subversion/bindings/javahl/include/InheritedProplistCallback.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/InheritedProplistCallback.class
subversion/bindings/javahl/include/JNIError.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/JNIError.class
subversion/bindings/javahl/include/ListCallback.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ListCallback.class
subversion/bindings/javahl/include/Lock.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Lock.class
subversion/bindings/javahl/include/LogDate.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/LogDate.class
subversion/bindings/javahl/include/LogMessageCallback.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/LogMessageCallback.class
subversion/bindings/javahl/include/Mergeinfo.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Mergeinfo.class
subversion/bindings/javahl/include/NativeException.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/NativeException.class
subversion/bindings/javahl/include/NativeResources.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/NativeResources.class
subversion/bindings/javahl/include/NodeKind.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/NodeKind.class
subversion/bindings/javahl/include/PatchCallback.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/PatchCallback.class
subversion/bindings/javahl/include/ProgressCallback.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ProgressCallback.class
subversion/bindings/javahl/include/ProgressEvent.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/ProgressEvent.class
subversion/bindings/javahl/include/Property.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Property.class
subversion/bindings/javahl/include/ProplistCallback.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ProplistCallback.class
subversion/bindings/javahl/include/ReposFreezeAction.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ReposFreezeAction.class
subversion/bindings/javahl/include/ReposNotifyCallback.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ReposNotifyCallback.class
subversion/bindings/javahl/include/ReposNotifyInformation.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/ReposNotifyInformation.class
subversion/bindings/javahl/include/Revision.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Revision.class
subversion/bindings/javahl/include/RevisionRange.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/RevisionRange.class
subversion/bindings/javahl/include/SVNClient.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/SVNClient.class
subversion/bindings/javahl/include/SVNRepos.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/SVNRepos.class
subversion/bindings/javahl/include/Status.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Status.class
subversion/bindings/javahl/include/StatusCallback.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/StatusCallback.class
subversion/bindings/javahl/include/SubversionException.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/SubversionException.class
subversion/bindings/javahl/include/Tristate.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Tristate.class
subversion/bindings/javahl/include/UserPasswordCallback.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/UserPasswordCallback.class
subversion/bindings/javahl/include/Version.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Version.class
subversion/bindings/javahl/include/VersionExtended.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/VersionExtended.class
subversion/bindings/javahl/native/Array.lo: subversion/bindings/javahl/native/Array.cpp subversion/bindings/javahl/native/Array.h subversion/bindings/javahl/native/JNIStringHolder.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/include/private/svn_debug.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_pools.h subversion/include/svn_types.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/Array.cpp
subversion/bindings/javahl/native/BlameCallback.lo: subversion/bindings/javahl/native/BlameCallback.cpp subversion/bindings/javahl/native/BlameCallback.h subversion/bindings/javahl/native/CreateJ.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_wc.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/BlameCallback.cpp
subversion/bindings/javahl/native/ChangelistCallback.lo: subversion/bindings/javahl/native/ChangelistCallback.cpp subversion/bindings/javahl/native/ChangelistCallback.h subversion/bindings/javahl/native/ClientContext.h subversion/bindings/javahl/native/JNIStringHolder.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Path.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/SVNBase.h subversion/bindings/javahl/native/SVNClient.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/ChangelistCallback.cpp
subversion/bindings/javahl/native/ClientContext.lo: subversion/bindings/javahl/native/ClientContext.cpp subversion/bindings/javahl/native/ClientContext.h subversion/bindings/javahl/native/CommitMessage.h subversion/bindings/javahl/native/CreateJ.h subversion/bindings/javahl/native/EnumMapper.h subversion/bindings/javahl/native/JNICriticalSection.h subversion/bindings/javahl/native/JNIStringHolder.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/Prompter.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn_private_config.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/ClientContext.cpp
subversion/bindings/javahl/native/CommitCallback.lo: subversion/bindings/javahl/native/CommitCallback.cpp subversion/bindings/javahl/native/CommitCallback.h subversion/bindings/javahl/native/CreateJ.h subversion/bindings/javahl/native/EnumMapper.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_compat.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_wc.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/CommitCallback.cpp
subversion/bindings/javahl/native/CommitMessage.lo: subversion/bindings/javahl/native/CommitMessage.cpp subversion/bindings/javahl/native/CommitMessage.h subversion/bindings/javahl/native/CreateJ.h subversion/bindings/javahl/native/EnumMapper.h subversion/bindings/javahl/native/JNIStringHolder.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/CommitMessage.cpp
subversion/bindings/javahl/native/CopySources.lo: subversion/bindings/javahl/native/CopySources.cpp subversion/bindings/javahl/native/Array.h subversion/bindings/javahl/native/CopySources.h subversion/bindings/javahl/native/JNIStringHolder.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/Revision.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/CopySources.cpp
subversion/bindings/javahl/native/CreateJ.lo: subversion/bindings/javahl/native/CreateJ.cpp subversion/bindings/javahl/include/org_apache_subversion_javahl_CommitItemStateFlags.h subversion/bindings/javahl/include/org_apache_subversion_javahl_types_Revision.h subversion/bindings/javahl/native/CreateJ.h subversion/bindings/javahl/native/EnumMapper.h subversion/bindings/javahl/native/JNIStringHolder.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/RevisionRange.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/CreateJ.cpp
subversion/bindings/javahl/native/DiffOptions.lo: subversion/bindings/javahl/native/DiffOptions.cpp subversion/bindings/javahl/native/DiffOptions.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/include/private/svn_debug.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_pools.h subversion/include/svn_types.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/DiffOptions.cpp
subversion/bindings/javahl/native/DiffSummaryReceiver.lo: subversion/bindings/javahl/native/DiffSummaryReceiver.cpp subversion/bindings/javahl/native/DiffSummaryReceiver.h subversion/bindings/javahl/native/EnumMapper.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/DiffSummaryReceiver.cpp
subversion/bindings/javahl/native/EnumMapper.lo: subversion/bindings/javahl/native/EnumMapper.cpp subversion/bindings/javahl/include/org_apache_subversion_javahl_CommitItemStateFlags.h subversion/bindings/javahl/native/EnumMapper.h subversion/bindings/javahl/native/JNIStringHolder.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/EnumMapper.cpp
subversion/bindings/javahl/native/File.lo: subversion/bindings/javahl/native/File.cpp subversion/bindings/javahl/native/File.h subversion/bindings/javahl/native/JNIByteArray.h subversion/bindings/javahl/native/JNIStringHolder.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/File.cpp
subversion/bindings/javahl/native/ImportFilterCallback.lo: subversion/bindings/javahl/native/ImportFilterCallback.cpp subversion/bindings/javahl/native/CreateJ.h subversion/bindings/javahl/native/EnumMapper.h subversion/bindings/javahl/native/ImportFilterCallback.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_wc.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/ImportFilterCallback.cpp
subversion/bindings/javahl/native/InfoCallback.lo: subversion/bindings/javahl/native/InfoCallback.cpp subversion/bindings/javahl/native/CreateJ.h subversion/bindings/javahl/native/EnumMapper.h subversion/bindings/javahl/native/InfoCallback.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_wc.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/InfoCallback.cpp
subversion/bindings/javahl/native/InputStream.lo: subversion/bindings/javahl/native/InputStream.cpp subversion/bindings/javahl/native/InputStream.h subversion/bindings/javahl/native/JNIByteArray.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/InputStream.cpp
subversion/bindings/javahl/native/JNIByteArray.lo: subversion/bindings/javahl/native/JNIByteArray.cpp subversion/bindings/javahl/native/JNIByteArray.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/include/private/svn_debug.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_pools.h subversion/include/svn_types.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/JNIByteArray.cpp
subversion/bindings/javahl/native/JNICriticalSection.lo: subversion/bindings/javahl/native/JNICriticalSection.cpp subversion/bindings/javahl/native/JNICriticalSection.h subversion/bindings/javahl/native/JNIMutex.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/include/private/svn_debug.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_pools.h subversion/include/svn_types.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/JNICriticalSection.cpp
subversion/bindings/javahl/native/JNIMutex.lo: subversion/bindings/javahl/native/JNIMutex.cpp subversion/bindings/javahl/native/JNIMutex.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/include/private/svn_debug.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_pools.h subversion/include/svn_types.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/JNIMutex.cpp
subversion/bindings/javahl/native/JNIStackElement.lo: subversion/bindings/javahl/native/JNIStackElement.cpp subversion/bindings/javahl/native/JNIStackElement.h subversion/bindings/javahl/native/JNIStringHolder.h subversion/bindings/javahl/native/JNIThreadData.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/include/private/svn_debug.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_pools.h subversion/include/svn_types.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/JNIStackElement.cpp
subversion/bindings/javahl/native/JNIStringHolder.lo: subversion/bindings/javahl/native/JNIStringHolder.cpp subversion/bindings/javahl/native/JNIStringHolder.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/include/private/svn_debug.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_pools.h subversion/include/svn_types.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/JNIStringHolder.cpp
subversion/bindings/javahl/native/JNIThreadData.lo: subversion/bindings/javahl/native/JNIThreadData.cpp subversion/bindings/javahl/native/JNIThreadData.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/include/private/svn_debug.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_pools.h subversion/include/svn_types.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/JNIThreadData.cpp
subversion/bindings/javahl/native/JNIUtil.lo: subversion/bindings/javahl/native/JNIUtil.cpp subversion/bindings/javahl/native/Array.h subversion/bindings/javahl/native/JNICriticalSection.h subversion/bindings/javahl/native/JNIMutex.h subversion/bindings/javahl/native/JNIStringHolder.h subversion/bindings/javahl/native/JNIThreadData.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/SVNBase.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_cache_config.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_dso.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_wc.h subversion/svn_private_config.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/JNIUtil.cpp
subversion/bindings/javahl/native/ListCallback.lo: subversion/bindings/javahl/native/ListCallback.cpp subversion/bindings/javahl/native/CreateJ.h subversion/bindings/javahl/native/EnumMapper.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/ListCallback.h subversion/bindings/javahl/native/Pool.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_wc.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/ListCallback.cpp
subversion/bindings/javahl/native/LogMessageCallback.lo: subversion/bindings/javahl/native/LogMessageCallback.cpp subversion/bindings/javahl/native/CreateJ.h subversion/bindings/javahl/native/EnumMapper.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/LogMessageCallback.h subversion/bindings/javahl/native/Pool.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_compat.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_wc.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/LogMessageCallback.cpp
subversion/bindings/javahl/native/MessageReceiver.lo: subversion/bindings/javahl/native/MessageReceiver.cpp subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/MessageReceiver.h subversion/bindings/javahl/native/Pool.h subversion/include/private/svn_debug.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_pools.h subversion/include/svn_types.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/MessageReceiver.cpp
subversion/bindings/javahl/native/OutputStream.lo: subversion/bindings/javahl/native/OutputStream.cpp subversion/bindings/javahl/native/JNIByteArray.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/OutputStream.h subversion/bindings/javahl/native/Pool.h subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/OutputStream.cpp
subversion/bindings/javahl/native/PatchCallback.lo: subversion/bindings/javahl/native/PatchCallback.cpp subversion/bindings/javahl/native/CreateJ.h subversion/bindings/javahl/native/EnumMapper.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/PatchCallback.h subversion/bindings/javahl/native/Pool.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_wc.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/PatchCallback.cpp
subversion/bindings/javahl/native/Path.lo: subversion/bindings/javahl/native/Path.cpp subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Path.h subversion/bindings/javahl/native/Pool.h subversion/include/private/svn_debug.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/Path.cpp
subversion/bindings/javahl/native/Pool.lo: subversion/bindings/javahl/native/Pool.cpp subversion/bindings/javahl/native/JNICriticalSection.h subversion/bindings/javahl/native/JNIMutex.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/include/private/svn_debug.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_pools.h subversion/include/svn_types.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/Pool.cpp
subversion/bindings/javahl/native/Prompter.lo: subversion/bindings/javahl/native/Prompter.cpp subversion/bindings/javahl/include/org_apache_subversion_javahl_callback_UserPasswordCallback.h subversion/bindings/javahl/native/JNIStringHolder.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/Prompter.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/svn_private_config.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/Prompter.cpp
subversion/bindings/javahl/native/ProplistCallback.lo: subversion/bindings/javahl/native/ProplistCallback.cpp subversion/bindings/javahl/native/CreateJ.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/ProplistCallback.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_wc.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/ProplistCallback.cpp
subversion/bindings/javahl/native/ReposFreezeAction.lo: subversion/bindings/javahl/native/ReposFreezeAction.cpp subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/ReposFreezeAction.h subversion/include/private/svn_debug.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_pools.h subversion/include/svn_types.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/ReposFreezeAction.cpp
subversion/bindings/javahl/native/ReposNotifyCallback.lo: subversion/bindings/javahl/native/ReposNotifyCallback.cpp subversion/bindings/javahl/native/CreateJ.h subversion/bindings/javahl/native/EnumMapper.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/ReposNotifyCallback.h subversion/bindings/javahl/native/RevisionRange.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/ReposNotifyCallback.cpp
subversion/bindings/javahl/native/Revision.lo: subversion/bindings/javahl/native/Revision.cpp subversion/bindings/javahl/native/EnumMapper.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/Revision.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/Revision.cpp
subversion/bindings/javahl/native/RevisionRange.lo: subversion/bindings/javahl/native/RevisionRange.cpp subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/Revision.h subversion/bindings/javahl/native/RevisionRange.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/RevisionRange.cpp
subversion/bindings/javahl/native/RevpropTable.lo: subversion/bindings/javahl/native/RevpropTable.cpp subversion/bindings/javahl/native/Array.h subversion/bindings/javahl/native/JNIStringHolder.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Path.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/RevpropTable.h subversion/include/private/svn_debug.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_string.h subversion/include/svn_types.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/RevpropTable.cpp
subversion/bindings/javahl/native/SVNBase.lo: subversion/bindings/javahl/native/SVNBase.cpp subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/SVNBase.h subversion/include/private/svn_debug.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_pools.h subversion/include/svn_types.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/SVNBase.cpp
subversion/bindings/javahl/native/SVNClient.lo: subversion/bindings/javahl/native/SVNClient.cpp subversion/bindings/javahl/native/Array.h subversion/bindings/javahl/native/BlameCallback.h subversion/bindings/javahl/native/ChangelistCallback.h subversion/bindings/javahl/native/ClientContext.h subversion/bindings/javahl/native/CommitCallback.h subversion/bindings/javahl/native/CommitMessage.h subversion/bindings/javahl/native/CopySources.h subversion/bindings/javahl/native/CreateJ.h subversion/bindings/javahl/native/DiffOptions.h subversion/bindings/javahl/native/DiffSummaryReceiver.h subversion/bindings/javahl/native/EnumMapper.h subversion/bindings/javahl/native/ImportFilterCallback.h subversion/bindings/javahl/native/InfoCallback.h subversion/bindings/javahl/native/JNIByteArray.h subversion/bindings/javahl/native/JNIStringHolder.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/ListCallback.h subversion/bindings/javahl/native/LogMessageCallback.h subversion/bindings/javahl/native/OutputStream.h subversion/bindings/javahl/native/PatchCallback.h subversion/bindings/javahl/native/Path.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/Prompter.h subversion/bindings/javahl/native/ProplistCallback.h subversion/bindings/javahl/native/Revision.h subversion/bindings/javahl/native/RevisionRange.h subversion/bindings/javahl/native/RevpropTable.h subversion/bindings/javahl/native/SVNBase.h subversion/bindings/javahl/native/SVNClient.h subversion/bindings/javahl/native/StatusCallback.h subversion/bindings/javahl/native/StringArray.h subversion/bindings/javahl/native/Targets.h subversion/bindings/javahl/native/VersionExtended.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_dso.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_version.h subversion/include/svn_wc.h subversion/svn_private_config.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/SVNClient.cpp
subversion/bindings/javahl/native/SVNRepos.lo: subversion/bindings/javahl/native/SVNRepos.cpp subversion/bindings/javahl/native/Array.h subversion/bindings/javahl/native/CreateJ.h subversion/bindings/javahl/native/File.h subversion/bindings/javahl/native/InputStream.h subversion/bindings/javahl/native/JNIStringHolder.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/MessageReceiver.h subversion/bindings/javahl/native/OutputStream.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/ReposFreezeAction.h subversion/bindings/javahl/native/ReposNotifyCallback.h subversion/bindings/javahl/native/Revision.h subversion/bindings/javahl/native/SVNBase.h subversion/bindings/javahl/native/SVNRepos.h subversion/bindings/javahl/native/StringArray.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_wc.h subversion/svn_private_config.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/SVNRepos.cpp
subversion/bindings/javahl/native/StatusCallback.lo: subversion/bindings/javahl/native/StatusCallback.cpp subversion/bindings/javahl/native/CreateJ.h subversion/bindings/javahl/native/EnumMapper.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/StatusCallback.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_wc.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/StatusCallback.cpp
subversion/bindings/javahl/native/StringArray.lo: subversion/bindings/javahl/native/StringArray.cpp subversion/bindings/javahl/native/Array.h subversion/bindings/javahl/native/JNIStringHolder.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/StringArray.h subversion/include/private/svn_debug.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/StringArray.cpp
subversion/bindings/javahl/native/Targets.lo: subversion/bindings/javahl/native/Targets.cpp subversion/bindings/javahl/native/Array.h subversion/bindings/javahl/native/JNIStringHolder.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Path.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/StringArray.h subversion/bindings/javahl/native/Targets.h subversion/include/private/svn_debug.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/Targets.cpp
subversion/bindings/javahl/native/VersionExtended.lo: subversion/bindings/javahl/native/VersionExtended.cpp subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/SVNBase.h subversion/bindings/javahl/native/VersionExtended.h subversion/include/private/svn_debug.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_pools.h subversion/include/svn_types.h subversion/include/svn_version.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/VersionExtended.cpp
subversion/bindings/javahl/native/libsvnjavahl.la.lo: subversion/bindings/javahl/native/libsvnjavahl.la.c
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/libsvnjavahl.la.c
subversion/bindings/javahl/native/org_apache_subversion_javahl_NativeResources.lo: subversion/bindings/javahl/native/org_apache_subversion_javahl_NativeResources.cpp subversion/bindings/javahl/include/org_apache_subversion_javahl_NativeResources.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/include/private/svn_debug.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_pools.h subversion/include/svn_types.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/org_apache_subversion_javahl_NativeResources.cpp
subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNClient.lo: subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNClient.cpp subversion/bindings/javahl/include/org_apache_subversion_javahl_SVNClient.h subversion/bindings/javahl/native/Array.h subversion/bindings/javahl/native/BlameCallback.h subversion/bindings/javahl/native/ChangelistCallback.h subversion/bindings/javahl/native/ClientContext.h subversion/bindings/javahl/native/CommitCallback.h subversion/bindings/javahl/native/CommitMessage.h subversion/bindings/javahl/native/CopySources.h subversion/bindings/javahl/native/DiffOptions.h subversion/bindings/javahl/native/DiffSummaryReceiver.h subversion/bindings/javahl/native/EnumMapper.h subversion/bindings/javahl/native/ImportFilterCallback.h subversion/bindings/javahl/native/InfoCallback.h subversion/bindings/javahl/native/JNIByteArray.h subversion/bindings/javahl/native/JNIStackElement.h subversion/bindings/javahl/native/JNIStringHolder.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/ListCallback.h subversion/bindings/javahl/native/LogMessageCallback.h subversion/bindings/javahl/native/OutputStream.h subversion/bindings/javahl/native/PatchCallback.h subversion/bindings/javahl/native/Path.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/Prompter.h subversion/bindings/javahl/native/ProplistCallback.h subversion/bindings/javahl/native/Revision.h subversion/bindings/javahl/native/RevisionRange.h subversion/bindings/javahl/native/RevpropTable.h subversion/bindings/javahl/native/SVNBase.h subversion/bindings/javahl/native/SVNClient.h subversion/bindings/javahl/native/StatusCallback.h subversion/bindings/javahl/native/StringArray.h subversion/bindings/javahl/native/Targets.h subversion/bindings/javahl/native/VersionExtended.h subversion/bindings/javahl/native/version.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_wc.h subversion/svn_private_config.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNClient.cpp
subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNRepos.lo: subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNRepos.cpp subversion/bindings/javahl/include/org_apache_subversion_javahl_SVNRepos.h subversion/bindings/javahl/native/Array.h subversion/bindings/javahl/native/EnumMapper.h subversion/bindings/javahl/native/File.h subversion/bindings/javahl/native/InputStream.h subversion/bindings/javahl/native/JNIByteArray.h subversion/bindings/javahl/native/JNIStackElement.h subversion/bindings/javahl/native/JNIStringHolder.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/MessageReceiver.h subversion/bindings/javahl/native/OutputStream.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/ReposFreezeAction.h subversion/bindings/javahl/native/ReposNotifyCallback.h subversion/bindings/javahl/native/Revision.h subversion/bindings/javahl/native/SVNBase.h subversion/bindings/javahl/native/SVNRepos.h subversion/bindings/javahl/native/StringArray.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn_private_config.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNRepos.cpp
subversion/bindings/javahl/native/org_apache_subversion_javahl_types_Version.lo: subversion/bindings/javahl/native/org_apache_subversion_javahl_types_Version.cpp subversion/bindings/javahl/include/org_apache_subversion_javahl_types_Version.h subversion/bindings/javahl/native/JNIStackElement.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/include/private/svn_debug.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_pools.h subversion/include/svn_types.h subversion/include/svn_version.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/org_apache_subversion_javahl_types_Version.cpp
subversion/bindings/javahl/native/org_apache_subversion_javahl_types_VersionExtended.lo: subversion/bindings/javahl/native/org_apache_subversion_javahl_types_VersionExtended.cpp subversion/bindings/javahl/include/org_apache_subversion_javahl_types_VersionExtended.h subversion/bindings/javahl/include/org_apache_subversion_javahl_types_VersionExtended_LinkedLib.h subversion/bindings/javahl/include/org_apache_subversion_javahl_types_VersionExtended_LinkedLibIterator.h subversion/bindings/javahl/include/org_apache_subversion_javahl_types_VersionExtended_LoadedLib.h subversion/bindings/javahl/include/org_apache_subversion_javahl_types_VersionExtended_LoadedLibIterator.h subversion/bindings/javahl/native/JNIStackElement.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/SVNBase.h subversion/bindings/javahl/native/VersionExtended.h subversion/include/private/svn_debug.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_pools.h subversion/include/svn_types.h subversion/include/svn_version.h
$(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/org_apache_subversion_javahl_types_VersionExtended.cpp
subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.lo: subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.c subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h subversion/bindings/swig/proxy/swig_perl_external_runtime.swg subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn_private_config.h
$(COMPILE_SWIG_PL) $(canonicalized_srcdir)subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.c
subversion/bindings/swig/perl/native/core.lo: subversion/bindings/swig/perl/native/core.c subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h subversion/include/svn_client.h subversion/include/svn_delta.h subversion/include/svn_fs.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_wc.h subversion/svn_private_config.h
$(COMPILE_PL_WRAPPER) $(canonicalized_srcdir)subversion/bindings/swig/perl/native/core.c
subversion/bindings/swig/perl/native/svn_client.lo: subversion/bindings/swig/perl/native/svn_client.c subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h subversion/include/svn_fs.h subversion/include/svn_repos.h subversion/svn_private_config.h
$(COMPILE_PL_WRAPPER) $(canonicalized_srcdir)subversion/bindings/swig/perl/native/svn_client.c
subversion/bindings/swig/perl/native/svn_delta.lo: subversion/bindings/swig/perl/native/svn_delta.c subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h subversion/include/svn_client.h subversion/include/svn_fs.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_wc.h subversion/svn_private_config.h
$(COMPILE_PL_WRAPPER) $(canonicalized_srcdir)subversion/bindings/swig/perl/native/svn_delta.c
subversion/bindings/swig/perl/native/svn_diff.lo: subversion/bindings/swig/perl/native/svn_diff.c subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h subversion/include/svn_client.h subversion/include/svn_delta.h subversion/include/svn_fs.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_wc.h subversion/svn_private_config.h
$(COMPILE_PL_WRAPPER) $(canonicalized_srcdir)subversion/bindings/swig/perl/native/svn_diff.c
subversion/bindings/swig/perl/native/svn_fs.lo: subversion/bindings/swig/perl/native/svn_fs.c subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h subversion/include/svn_client.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_wc.h subversion/svn_private_config.h
$(COMPILE_PL_WRAPPER) $(canonicalized_srcdir)subversion/bindings/swig/perl/native/svn_fs.c
subversion/bindings/swig/perl/native/svn_ra.lo: subversion/bindings/swig/perl/native/svn_ra.c subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h subversion/include/svn_client.h subversion/include/svn_fs.h subversion/include/svn_repos.h subversion/include/svn_wc.h subversion/svn_private_config.h
$(COMPILE_PL_WRAPPER) $(canonicalized_srcdir)subversion/bindings/swig/perl/native/svn_ra.c
subversion/bindings/swig/perl/native/svn_repos.lo: subversion/bindings/swig/perl/native/svn_repos.c subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h subversion/include/svn_client.h subversion/include/svn_ra.h subversion/include/svn_wc.h subversion/svn_private_config.h
$(COMPILE_PL_WRAPPER) $(canonicalized_srcdir)subversion/bindings/swig/perl/native/svn_repos.c
subversion/bindings/swig/perl/native/svn_wc.lo: subversion/bindings/swig/perl/native/svn_wc.c subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h subversion/include/svn_client.h subversion/include/svn_fs.h subversion/include/svn_repos.h subversion/svn_private_config.h
$(COMPILE_PL_WRAPPER) $(canonicalized_srcdir)subversion/bindings/swig/perl/native/svn_wc.c
subversion/bindings/swig/python/core.lo: subversion/bindings/swig/python/core.c subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h subversion/include/svn_client.h subversion/include/svn_delta.h subversion/include/svn_fs.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_wc.h subversion/svn_private_config.h
$(COMPILE_PY_WRAPPER) $(canonicalized_srcdir)subversion/bindings/swig/python/core.c
subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.lo: subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c subversion/bindings/swig/proxy/swig_python_external_runtime.swg subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn_private_config.h
$(COMPILE_SWIG_PY) $(canonicalized_srcdir)subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c
subversion/bindings/swig/python/svn_client.lo: subversion/bindings/swig/python/svn_client.c subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h subversion/include/svn_fs.h subversion/include/svn_repos.h subversion/svn_private_config.h
$(COMPILE_PY_WRAPPER) $(canonicalized_srcdir)subversion/bindings/swig/python/svn_client.c
subversion/bindings/swig/python/svn_delta.lo: subversion/bindings/swig/python/svn_delta.c subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h subversion/include/svn_client.h subversion/include/svn_fs.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_wc.h subversion/svn_private_config.h
$(COMPILE_PY_WRAPPER) $(canonicalized_srcdir)subversion/bindings/swig/python/svn_delta.c
subversion/bindings/swig/python/svn_diff.lo: subversion/bindings/swig/python/svn_diff.c subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h subversion/include/svn_client.h subversion/include/svn_delta.h subversion/include/svn_fs.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_wc.h subversion/svn_private_config.h
$(COMPILE_PY_WRAPPER) $(canonicalized_srcdir)subversion/bindings/swig/python/svn_diff.c
subversion/bindings/swig/python/svn_fs.lo: subversion/bindings/swig/python/svn_fs.c subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h subversion/include/svn_client.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_wc.h subversion/svn_private_config.h
$(COMPILE_PY_WRAPPER) $(canonicalized_srcdir)subversion/bindings/swig/python/svn_fs.c
subversion/bindings/swig/python/svn_ra.lo: subversion/bindings/swig/python/svn_ra.c subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h subversion/include/svn_client.h subversion/include/svn_fs.h subversion/include/svn_repos.h subversion/include/svn_wc.h subversion/svn_private_config.h
$(COMPILE_PY_WRAPPER) $(canonicalized_srcdir)subversion/bindings/swig/python/svn_ra.c
subversion/bindings/swig/python/svn_repos.lo: subversion/bindings/swig/python/svn_repos.c subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h subversion/include/svn_client.h subversion/include/svn_ra.h subversion/include/svn_wc.h subversion/svn_private_config.h
$(COMPILE_PY_WRAPPER) $(canonicalized_srcdir)subversion/bindings/swig/python/svn_repos.c
subversion/bindings/swig/python/svn_wc.lo: subversion/bindings/swig/python/svn_wc.c subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h subversion/include/svn_client.h subversion/include/svn_fs.h subversion/include/svn_repos.h subversion/svn_private_config.h
$(COMPILE_PY_WRAPPER) $(canonicalized_srcdir)subversion/bindings/swig/python/svn_wc.c
subversion/bindings/swig/ruby/core.lo: subversion/bindings/swig/ruby/core.c subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h subversion/include/svn_client.h subversion/include/svn_delta.h subversion/include/svn_fs.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_wc.h subversion/svn_private_config.h
$(COMPILE_RB_WRAPPER) $(canonicalized_srcdir)subversion/bindings/swig/ruby/core.c
subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.lo: subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.c subversion/bindings/swig/proxy/swig_ruby_external_runtime.swg subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_nls.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_wc.h subversion/svn_private_config.h
$(COMPILE_SWIG_RB) $(canonicalized_srcdir)subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.c
subversion/bindings/swig/ruby/svn_client.lo: subversion/bindings/swig/ruby/svn_client.c subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h subversion/include/svn_fs.h subversion/include/svn_repos.h subversion/svn_private_config.h
$(COMPILE_RB_WRAPPER) $(canonicalized_srcdir)subversion/bindings/swig/ruby/svn_client.c
subversion/bindings/swig/ruby/svn_delta.lo: subversion/bindings/swig/ruby/svn_delta.c subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h subversion/include/svn_client.h subversion/include/svn_fs.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_wc.h subversion/svn_private_config.h
$(COMPILE_RB_WRAPPER) $(canonicalized_srcdir)subversion/bindings/swig/ruby/svn_delta.c
subversion/bindings/swig/ruby/svn_diff.lo: subversion/bindings/swig/ruby/svn_diff.c subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h subversion/include/svn_client.h subversion/include/svn_delta.h subversion/include/svn_fs.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_wc.h subversion/svn_private_config.h
$(COMPILE_RB_WRAPPER) $(canonicalized_srcdir)subversion/bindings/swig/ruby/svn_diff.c
subversion/bindings/swig/ruby/svn_fs.lo: subversion/bindings/swig/ruby/svn_fs.c subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h subversion/include/svn_client.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_wc.h subversion/svn_private_config.h
$(COMPILE_RB_WRAPPER) $(canonicalized_srcdir)subversion/bindings/swig/ruby/svn_fs.c
subversion/bindings/swig/ruby/svn_ra.lo: subversion/bindings/swig/ruby/svn_ra.c subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h subversion/include/svn_client.h subversion/include/svn_fs.h subversion/include/svn_repos.h subversion/include/svn_wc.h subversion/svn_private_config.h
$(COMPILE_RB_WRAPPER) $(canonicalized_srcdir)subversion/bindings/swig/ruby/svn_ra.c
subversion/bindings/swig/ruby/svn_repos.lo: subversion/bindings/swig/ruby/svn_repos.c subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h subversion/include/svn_client.h subversion/include/svn_ra.h subversion/include/svn_wc.h subversion/svn_private_config.h
$(COMPILE_RB_WRAPPER) $(canonicalized_srcdir)subversion/bindings/swig/ruby/svn_repos.c
subversion/bindings/swig/ruby/svn_wc.lo: subversion/bindings/swig/ruby/svn_wc.c subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h subversion/include/svn_client.h subversion/include/svn_fs.h subversion/include/svn_repos.h subversion/svn_private_config.h
$(COMPILE_RB_WRAPPER) $(canonicalized_srcdir)subversion/bindings/swig/ruby/svn_wc.c
subversion/libsvn_auth_gnome_keyring/gnome_keyring.lo: subversion/libsvn_auth_gnome_keyring/gnome_keyring.c subversion/include/private/svn_auth_private.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/svn_private_config.h
subversion/libsvn_auth_gnome_keyring/version.lo: subversion/libsvn_auth_gnome_keyring/version.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h
subversion/libsvn_auth_kwallet/kwallet.lo: subversion/libsvn_auth_kwallet/kwallet.cpp subversion/include/private/svn_auth_private.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/svn_private_config.h
subversion/libsvn_auth_kwallet/version.lo: subversion/libsvn_auth_kwallet/version.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h
subversion/libsvn_client/add.lo: subversion/libsvn_client/add.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_ctype.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/blame.lo: subversion/libsvn_client/blame.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/cat.lo: subversion/libsvn_client/cat.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/changelist.lo: subversion/libsvn_client/changelist.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/checkout.lo: subversion/libsvn_client/checkout.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/cleanup.lo: subversion/libsvn_client/cleanup.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/cmdline.lo: subversion/libsvn_client/cmdline.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_opt_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/commit.lo: subversion/libsvn_client/commit.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/commit_util.lo: subversion/libsvn_client/commit_util.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_iter.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/compat_providers.lo: subversion/libsvn_client/compat_providers.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h
subversion/libsvn_client/copy.lo: subversion/libsvn_client/copy.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_mergeinfo_private.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/libsvn_client/mergeinfo.h subversion/svn_private_config.h
subversion/libsvn_client/copy_foreign.lo: subversion/libsvn_client/copy_foreign.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_subr_private.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/ctx.lo: subversion/libsvn_client/ctx.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h
subversion/libsvn_client/delete.lo: subversion/libsvn_client/delete.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/deprecated.lo: subversion/libsvn_client/deprecated.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_opt_private.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_compat.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/libsvn_client/mergeinfo.h subversion/svn_private_config.h
subversion/libsvn_client/diff.lo: subversion/libsvn_client/diff.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_private.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_io_private.h subversion/include/private/svn_magic.h subversion/include/private/svn_subr_private.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_version.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/diff_local.lo: subversion/libsvn_client/diff_local.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/diff_summarize.lo: subversion/libsvn_client/diff_summarize.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h
subversion/libsvn_client/export.lo: subversion/libsvn_client/export.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_delta_private.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_subr_private.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/externals.lo: subversion/libsvn_client/externals.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/import.lo: subversion/libsvn_client/import.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/info.lo: subversion/libsvn_client/info.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_fspath.h subversion/include/private/svn_magic.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/iprops.lo: subversion/libsvn_client/iprops.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/list.lo: subversion/libsvn_client/list.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_fspath.h subversion/include/private/svn_magic.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/locking_commands.lo: subversion/libsvn_client/locking_commands.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/include/svn_xml.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/log.lo: subversion/libsvn_client/log.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_compat.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/merge.lo: subversion/libsvn_client/merge.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_fspath.h subversion/include/private/svn_magic.h subversion/include/private/svn_mergeinfo_private.h subversion/include/private/svn_opt_private.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_subr_private.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_version.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/libsvn_client/mergeinfo.h subversion/svn_private_config.h
subversion/libsvn_client/mergeinfo.lo: subversion/libsvn_client/mergeinfo.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_fspath.h subversion/include/private/svn_magic.h subversion/include/private/svn_mergeinfo_private.h subversion/include/private/svn_opt_private.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/libsvn_client/mergeinfo.h subversion/svn_private_config.h
subversion/libsvn_client/patch.lo: subversion/libsvn_client/patch.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_eol_private.h subversion/include/private/svn_magic.h subversion/include/private/svn_string_private.h subversion/include/private/svn_subr_private.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/prop_commands.lo: subversion/libsvn_client/prop_commands.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/ra.lo: subversion/libsvn_client/ra.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/libsvn_client/mergeinfo.h subversion/svn_private_config.h
subversion/libsvn_client/relocate.lo: subversion/libsvn_client/relocate.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/repos_diff.lo: subversion/libsvn_client/repos_diff.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_subr_private.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/resolved.lo: subversion/libsvn_client/resolved.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/revert.lo: subversion/libsvn_client/revert.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/revisions.lo: subversion/libsvn_client/revisions.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/status.lo: subversion/libsvn_client/status.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/switch.lo: subversion/libsvn_client/switch.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/update.lo: subversion/libsvn_client/update.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/upgrade.lo: subversion/libsvn_client/upgrade.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/url.lo: subversion/libsvn_client/url.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/util.lo: subversion/libsvn_client/util.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_fspath.h subversion/include/private/svn_magic.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/svn_private_config.h
subversion/libsvn_client/version.lo: subversion/libsvn_client/version.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_wc.h
subversion/libsvn_delta/cancel.lo: subversion/libsvn_delta/cancel.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_string.h subversion/include/svn_types.h
subversion/libsvn_delta/compat.lo: subversion/libsvn_delta/compat.c subversion/include/private/svn_debug.h subversion/include/private/svn_delta_private.h subversion/include/private/svn_editor.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/svn_private_config.h
subversion/libsvn_delta/compose_delta.lo: subversion/libsvn_delta/compose_delta.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_delta/delta.h
subversion/libsvn_delta/debug_editor.lo: subversion/libsvn_delta/debug_editor.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_delta/debug_editor.h
subversion/libsvn_delta/default_editor.lo: subversion/libsvn_delta/default_editor.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_string.h subversion/include/svn_types.h
subversion/libsvn_delta/deprecated.lo: subversion/libsvn_delta/deprecated.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h
subversion/libsvn_delta/depth_filter_editor.lo: subversion/libsvn_delta/depth_filter_editor.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_string.h subversion/include/svn_types.h
subversion/libsvn_delta/editor.lo: subversion/libsvn_delta/editor.c subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h
subversion/libsvn_delta/path_driver.lo: subversion/libsvn_delta/path_driver.c subversion/include/private/svn_debug.h subversion/include/private/svn_fspath.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h
subversion/libsvn_delta/svndiff.lo: subversion/libsvn_delta/svndiff.c subversion/include/private/svn_debug.h subversion/include/private/svn_delta_private.h subversion/include/private/svn_editor.h subversion/include/private/svn_error_private.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_delta/delta.h subversion/svn_private_config.h
subversion/libsvn_delta/text_delta.lo: subversion/libsvn_delta/text_delta.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_delta/delta.h
subversion/libsvn_delta/version.lo: subversion/libsvn_delta/version.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h
subversion/libsvn_delta/xdelta.lo: subversion/libsvn_delta/xdelta.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_delta/delta.h
subversion/libsvn_diff/deprecated.lo: subversion/libsvn_diff/deprecated.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/svn_private_config.h
subversion/libsvn_diff/diff.lo: subversion/libsvn_diff/diff.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_diff/diff.h
subversion/libsvn_diff/diff3.lo: subversion/libsvn_diff/diff3.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_diff/diff.h
subversion/libsvn_diff/diff4.lo: subversion/libsvn_diff/diff4.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_diff/diff.h
subversion/libsvn_diff/diff_file.lo: subversion/libsvn_diff/diff_file.c subversion/include/private/svn_adler32.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_diff_private.h subversion/include/private/svn_eol_private.h subversion/include/private/svn_utf_private.h subversion/include/svn_checksum.h subversion/include/svn_ctype.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/libsvn_diff/diff.h subversion/svn_private_config.h
subversion/libsvn_diff/diff_memory.lo: subversion/libsvn_diff/diff_memory.c subversion/include/private/svn_adler32.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_private.h subversion/include/svn_checksum.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/libsvn_diff/diff.h subversion/svn_private_config.h
subversion/libsvn_diff/diff_tree.lo: subversion/libsvn_diff/diff_tree.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/svn_checksum.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/svn_private_config.h
subversion/libsvn_diff/lcs.lo: subversion/libsvn_diff/lcs.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_diff/diff.h
subversion/libsvn_diff/parse-diff.lo: subversion/libsvn_diff/parse-diff.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_eol_private.h subversion/include/svn_checksum.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h
subversion/libsvn_diff/token.lo: subversion/libsvn_diff/token.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_diff/diff.h
-subversion/libsvn_diff/util.lo: subversion/libsvn_diff/util.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_private.h subversion/include/svn_checksum.h subversion/include/svn_ctype.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_version.h subversion/libsvn_diff/diff.h subversion/svn_private_config.h
+subversion/libsvn_diff/util.lo: subversion/libsvn_diff/util.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_private.h subversion/include/svn_checksum.h subversion/include/svn_ctype.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_version.h subversion/libsvn_diff/diff.h subversion/svn_private_config.h
subversion/libsvn_fs/access.lo: subversion/libsvn_fs/access.c subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_fs_private.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h
subversion/libsvn_fs/editor.lo: subversion/libsvn_fs/editor.c subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_fspath.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/svn_private_config.h
subversion/libsvn_fs/fs-loader.lo: subversion/libsvn_fs/fs-loader.c subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_fs_util.h subversion/include/private/svn_mutex.h subversion/include/private/svn_subr_private.h subversion/include/private/svn_utf_private.h subversion/include/svn_checksum.h subversion/include/svn_ctype.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_dso.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_xml.h subversion/libsvn_fs/fs-loader.h subversion/svn_private_config.h
subversion/libsvn_fs_base/bdb/bdb-err.lo: subversion/libsvn_fs_base/bdb/bdb-err.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_base/bdb/bdb-err.h subversion/libsvn_fs_base/bdb/bdb_compat.h subversion/libsvn_fs_base/bdb/env.h subversion/libsvn_fs_base/err.h subversion/libsvn_fs_base/fs.h subversion/svn_private_config.h
subversion/libsvn_fs_base/bdb/bdb_compat.lo: subversion/libsvn_fs_base/bdb/bdb_compat.c subversion/libsvn_fs_base/bdb/bdb_compat.h subversion/svn_private_config.h
subversion/libsvn_fs_base/bdb/changes-table.lo: subversion/libsvn_fs_base/bdb/changes-table.c subversion/include/private/svn_debug.h subversion/include/private/svn_fs_util.h subversion/include/private/svn_fspath.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_base/bdb/bdb-err.h subversion/libsvn_fs_base/bdb/bdb_compat.h subversion/libsvn_fs_base/bdb/changes-table.h subversion/libsvn_fs_base/bdb/dbt.h subversion/libsvn_fs_base/bdb/env.h subversion/libsvn_fs_base/err.h subversion/libsvn_fs_base/fs.h subversion/libsvn_fs_base/id.h subversion/libsvn_fs_base/trail.h subversion/libsvn_fs_base/util/fs_skels.h subversion/svn_private_config.h
subversion/libsvn_fs_base/bdb/checksum-reps-table.lo: subversion/libsvn_fs_base/bdb/checksum-reps-table.c subversion/include/private/svn_debug.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_base/bdb/bdb-err.h subversion/libsvn_fs_base/bdb/bdb_compat.h subversion/libsvn_fs_base/bdb/checksum-reps-table.h subversion/libsvn_fs_base/bdb/dbt.h subversion/libsvn_fs_base/bdb/env.h subversion/libsvn_fs_base/err.h subversion/libsvn_fs_base/fs.h subversion/libsvn_fs_base/key-gen.h subversion/libsvn_fs_base/trail.h subversion/svn_private_config.h
subversion/libsvn_fs_base/bdb/copies-table.lo: subversion/libsvn_fs_base/bdb/copies-table.c subversion/include/private/svn_debug.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_base/bdb/bdb-err.h subversion/libsvn_fs_base/bdb/bdb_compat.h subversion/libsvn_fs_base/bdb/copies-table.h subversion/libsvn_fs_base/bdb/dbt.h subversion/libsvn_fs_base/bdb/env.h subversion/libsvn_fs_base/bdb/rev-table.h subversion/libsvn_fs_base/err.h subversion/libsvn_fs_base/fs.h subversion/libsvn_fs_base/key-gen.h subversion/libsvn_fs_base/trail.h subversion/libsvn_fs_base/util/fs_skels.h subversion/svn_private_config.h
subversion/libsvn_fs_base/bdb/dbt.lo: subversion/libsvn_fs_base/bdb/dbt.c subversion/include/private/svn_debug.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs_base/bdb/dbt.h subversion/libsvn_fs_base/id.h subversion/svn_private_config.h
subversion/libsvn_fs_base/bdb/env.lo: subversion/libsvn_fs_base/bdb/env.c subversion/include/private/svn_atomic.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_mutex.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/libsvn_fs_base/bdb/bdb-err.h subversion/libsvn_fs_base/bdb/bdb_compat.h subversion/libsvn_fs_base/bdb/env.h subversion/svn_private_config.h
subversion/libsvn_fs_base/bdb/lock-tokens-table.lo: subversion/libsvn_fs_base/bdb/lock-tokens-table.c subversion/include/private/svn_debug.h subversion/include/private/svn_fs_util.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_base/bdb/bdb-err.h subversion/libsvn_fs_base/bdb/bdb_compat.h subversion/libsvn_fs_base/bdb/dbt.h subversion/libsvn_fs_base/bdb/env.h subversion/libsvn_fs_base/bdb/lock-tokens-table.h subversion/libsvn_fs_base/bdb/locks-table.h subversion/libsvn_fs_base/err.h subversion/libsvn_fs_base/fs.h subversion/libsvn_fs_base/trail.h subversion/libsvn_fs_base/util/fs_skels.h subversion/svn_private_config.h
subversion/libsvn_fs_base/bdb/locks-table.lo: subversion/libsvn_fs_base/bdb/locks-table.c subversion/include/private/svn_debug.h subversion/include/private/svn_fs_util.h subversion/include/private/svn_fspath.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_base/bdb/bdb-err.h subversion/libsvn_fs_base/bdb/bdb_compat.h subversion/libsvn_fs_base/bdb/dbt.h subversion/libsvn_fs_base/bdb/env.h subversion/libsvn_fs_base/bdb/lock-tokens-table.h subversion/libsvn_fs_base/bdb/locks-table.h subversion/libsvn_fs_base/err.h subversion/libsvn_fs_base/fs.h subversion/libsvn_fs_base/trail.h subversion/libsvn_fs_base/util/fs_skels.h subversion/svn_private_config.h
subversion/libsvn_fs_base/bdb/miscellaneous-table.lo: subversion/libsvn_fs_base/bdb/miscellaneous-table.c subversion/include/private/svn_debug.h subversion/include/private/svn_fs_util.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_base/bdb/bdb-err.h subversion/libsvn_fs_base/bdb/bdb_compat.h subversion/libsvn_fs_base/bdb/dbt.h subversion/libsvn_fs_base/bdb/env.h subversion/libsvn_fs_base/bdb/miscellaneous-table.h subversion/libsvn_fs_base/err.h subversion/libsvn_fs_base/fs.h subversion/libsvn_fs_base/trail.h subversion/svn_private_config.h
subversion/libsvn_fs_base/bdb/node-origins-table.lo: subversion/libsvn_fs_base/bdb/node-origins-table.c subversion/include/private/svn_debug.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_base/bdb/bdb-err.h subversion/libsvn_fs_base/bdb/bdb_compat.h subversion/libsvn_fs_base/bdb/dbt.h subversion/libsvn_fs_base/bdb/env.h subversion/libsvn_fs_base/bdb/node-origins-table.h subversion/libsvn_fs_base/err.h subversion/libsvn_fs_base/fs.h subversion/libsvn_fs_base/id.h subversion/libsvn_fs_base/trail.h subversion/svn_private_config.h
subversion/libsvn_fs_base/bdb/nodes-table.lo: subversion/libsvn_fs_base/bdb/nodes-table.c subversion/include/private/svn_debug.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_base/bdb/bdb-err.h subversion/libsvn_fs_base/bdb/bdb_compat.h subversion/libsvn_fs_base/bdb/dbt.h subversion/libsvn_fs_base/bdb/env.h subversion/libsvn_fs_base/bdb/nodes-table.h subversion/libsvn_fs_base/err.h subversion/libsvn_fs_base/fs.h subversion/libsvn_fs_base/id.h subversion/libsvn_fs_base/key-gen.h subversion/libsvn_fs_base/trail.h subversion/libsvn_fs_base/util/fs_skels.h subversion/svn_private_config.h
subversion/libsvn_fs_base/bdb/reps-table.lo: subversion/libsvn_fs_base/bdb/reps-table.c subversion/include/private/svn_debug.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_base/bdb/bdb-err.h subversion/libsvn_fs_base/bdb/bdb_compat.h subversion/libsvn_fs_base/bdb/dbt.h subversion/libsvn_fs_base/bdb/env.h subversion/libsvn_fs_base/bdb/reps-table.h subversion/libsvn_fs_base/bdb/strings-table.h subversion/libsvn_fs_base/err.h subversion/libsvn_fs_base/fs.h subversion/libsvn_fs_base/key-gen.h subversion/libsvn_fs_base/trail.h subversion/libsvn_fs_base/util/fs_skels.h subversion/svn_private_config.h
subversion/libsvn_fs_base/bdb/rev-table.lo: subversion/libsvn_fs_base/bdb/rev-table.c subversion/include/private/svn_debug.h subversion/include/private/svn_fs_util.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_base/bdb/bdb-err.h subversion/libsvn_fs_base/bdb/bdb_compat.h subversion/libsvn_fs_base/bdb/dbt.h subversion/libsvn_fs_base/bdb/env.h subversion/libsvn_fs_base/bdb/rev-table.h subversion/libsvn_fs_base/err.h subversion/libsvn_fs_base/fs.h subversion/libsvn_fs_base/trail.h subversion/libsvn_fs_base/util/fs_skels.h subversion/svn_private_config.h
subversion/libsvn_fs_base/bdb/strings-table.lo: subversion/libsvn_fs_base/bdb/strings-table.c subversion/include/private/svn_debug.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_base/bdb/bdb-err.h subversion/libsvn_fs_base/bdb/bdb_compat.h subversion/libsvn_fs_base/bdb/dbt.h subversion/libsvn_fs_base/bdb/env.h subversion/libsvn_fs_base/bdb/strings-table.h subversion/libsvn_fs_base/err.h subversion/libsvn_fs_base/fs.h subversion/libsvn_fs_base/key-gen.h subversion/libsvn_fs_base/trail.h subversion/svn_private_config.h
subversion/libsvn_fs_base/bdb/txn-table.lo: subversion/libsvn_fs_base/bdb/txn-table.c subversion/include/private/svn_debug.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_base/bdb/bdb-err.h subversion/libsvn_fs_base/bdb/bdb_compat.h subversion/libsvn_fs_base/bdb/dbt.h subversion/libsvn_fs_base/bdb/env.h subversion/libsvn_fs_base/bdb/txn-table.h subversion/libsvn_fs_base/err.h subversion/libsvn_fs_base/fs.h subversion/libsvn_fs_base/key-gen.h subversion/libsvn_fs_base/trail.h subversion/libsvn_fs_base/util/fs_skels.h subversion/svn_private_config.h
subversion/libsvn_fs_base/bdb/uuids-table.lo: subversion/libsvn_fs_base/bdb/uuids-table.c subversion/include/private/svn_debug.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_base/bdb/bdb-err.h subversion/libsvn_fs_base/bdb/bdb_compat.h subversion/libsvn_fs_base/bdb/dbt.h subversion/libsvn_fs_base/bdb/env.h subversion/libsvn_fs_base/bdb/uuids-table.h subversion/libsvn_fs_base/err.h subversion/libsvn_fs_base/fs.h subversion/libsvn_fs_base/trail.h subversion/svn_private_config.h
subversion/libsvn_fs_base/dag.lo: subversion/libsvn_fs_base/dag.c subversion/include/private/svn_debug.h subversion/include/private/svn_fs_util.h subversion/include/private/svn_fspath.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_base/bdb/bdb_compat.h subversion/libsvn_fs_base/bdb/changes-table.h subversion/libsvn_fs_base/bdb/checksum-reps-table.h subversion/libsvn_fs_base/bdb/copies-table.h subversion/libsvn_fs_base/bdb/env.h subversion/libsvn_fs_base/bdb/node-origins-table.h subversion/libsvn_fs_base/bdb/nodes-table.h subversion/libsvn_fs_base/bdb/reps-table.h subversion/libsvn_fs_base/bdb/rev-table.h subversion/libsvn_fs_base/bdb/strings-table.h subversion/libsvn_fs_base/bdb/txn-table.h subversion/libsvn_fs_base/dag.h subversion/libsvn_fs_base/err.h subversion/libsvn_fs_base/fs.h subversion/libsvn_fs_base/id.h subversion/libsvn_fs_base/key-gen.h subversion/libsvn_fs_base/node-rev.h subversion/libsvn_fs_base/reps-strings.h subversion/libsvn_fs_base/revs-txns.h subversion/libsvn_fs_base/trail.h subversion/libsvn_fs_base/util/fs_skels.h subversion/svn_private_config.h
subversion/libsvn_fs_base/err.lo: subversion/libsvn_fs_base/err.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_base/err.h subversion/libsvn_fs_base/id.h subversion/svn_private_config.h
subversion/libsvn_fs_base/fs.lo: subversion/libsvn_fs_base/fs.c subversion/include/private/svn_debug.h subversion/include/private/svn_fs_util.h subversion/include/private/svn_subr_private.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_version.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_base/bdb/bdb-err.h subversion/libsvn_fs_base/bdb/bdb_compat.h subversion/libsvn_fs_base/bdb/changes-table.h subversion/libsvn_fs_base/bdb/checksum-reps-table.h subversion/libsvn_fs_base/bdb/copies-table.h subversion/libsvn_fs_base/bdb/env.h subversion/libsvn_fs_base/bdb/lock-tokens-table.h subversion/libsvn_fs_base/bdb/locks-table.h subversion/libsvn_fs_base/bdb/miscellaneous-table.h subversion/libsvn_fs_base/bdb/node-origins-table.h subversion/libsvn_fs_base/bdb/nodes-table.h subversion/libsvn_fs_base/bdb/reps-table.h subversion/libsvn_fs_base/bdb/rev-table.h subversion/libsvn_fs_base/bdb/strings-table.h subversion/libsvn_fs_base/bdb/txn-table.h subversion/libsvn_fs_base/bdb/uuids-table.h subversion/libsvn_fs_base/dag.h subversion/libsvn_fs_base/err.h subversion/libsvn_fs_base/fs.h subversion/libsvn_fs_base/id.h subversion/libsvn_fs_base/lock.h subversion/libsvn_fs_base/revs-txns.h subversion/libsvn_fs_base/trail.h subversion/libsvn_fs_base/tree.h subversion/libsvn_fs_base/uuid.h subversion/svn_private_config.h
subversion/libsvn_fs_base/id.lo: subversion/libsvn_fs_base/id.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_base/id.h
subversion/libsvn_fs_base/key-gen.lo: subversion/libsvn_fs_base/key-gen.c subversion/include/private/svn_debug.h subversion/include/private/svn_skel.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs_base/key-gen.h
subversion/libsvn_fs_base/lock.lo: subversion/libsvn_fs_base/lock.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_fs_util.h subversion/include/private/svn_skel.h subversion/include/private/svn_subr_private.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_base/bdb/bdb_compat.h subversion/libsvn_fs_base/bdb/env.h subversion/libsvn_fs_base/bdb/lock-tokens-table.h subversion/libsvn_fs_base/bdb/locks-table.h subversion/libsvn_fs_base/err.h subversion/libsvn_fs_base/fs.h subversion/libsvn_fs_base/lock.h subversion/libsvn_fs_base/trail.h subversion/libsvn_fs_base/tree.h subversion/libsvn_fs_base/util/fs_skels.h subversion/svn_private_config.h
subversion/libsvn_fs_base/node-rev.lo: subversion/libsvn_fs_base/node-rev.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_base/bdb/bdb_compat.h subversion/libsvn_fs_base/bdb/env.h subversion/libsvn_fs_base/bdb/node-origins-table.h subversion/libsvn_fs_base/bdb/nodes-table.h subversion/libsvn_fs_base/err.h subversion/libsvn_fs_base/fs.h subversion/libsvn_fs_base/id.h subversion/libsvn_fs_base/node-rev.h subversion/libsvn_fs_base/reps-strings.h subversion/libsvn_fs_base/trail.h subversion/svn_private_config.h
subversion/libsvn_fs_base/reps-strings.lo: subversion/libsvn_fs_base/reps-strings.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_base/bdb/bdb_compat.h subversion/libsvn_fs_base/bdb/env.h subversion/libsvn_fs_base/bdb/reps-table.h subversion/libsvn_fs_base/bdb/strings-table.h subversion/libsvn_fs_base/err.h subversion/libsvn_fs_base/fs.h subversion/libsvn_fs_base/reps-strings.h subversion/libsvn_fs_base/trail.h subversion/svn_private_config.h
subversion/libsvn_fs_base/revs-txns.lo: subversion/libsvn_fs_base/revs-txns.c subversion/include/private/svn_debug.h subversion/include/private/svn_fs_util.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_base/bdb/bdb_compat.h subversion/libsvn_fs_base/bdb/changes-table.h subversion/libsvn_fs_base/bdb/copies-table.h subversion/libsvn_fs_base/bdb/env.h subversion/libsvn_fs_base/bdb/rev-table.h subversion/libsvn_fs_base/bdb/txn-table.h subversion/libsvn_fs_base/dag.h subversion/libsvn_fs_base/err.h subversion/libsvn_fs_base/fs.h subversion/libsvn_fs_base/id.h subversion/libsvn_fs_base/key-gen.h subversion/libsvn_fs_base/revs-txns.h subversion/libsvn_fs_base/trail.h subversion/libsvn_fs_base/tree.h subversion/svn_private_config.h
subversion/libsvn_fs_base/trail.lo: subversion/libsvn_fs_base/trail.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_base/bdb/bdb-err.h subversion/libsvn_fs_base/bdb/bdb_compat.h subversion/libsvn_fs_base/bdb/env.h subversion/libsvn_fs_base/err.h subversion/libsvn_fs_base/fs.h subversion/libsvn_fs_base/trail.h subversion/svn_private_config.h
subversion/libsvn_fs_base/tree.lo: subversion/libsvn_fs_base/tree.c subversion/include/private/svn_debug.h subversion/include/private/svn_fs_util.h subversion/include/private/svn_fspath.h subversion/include/private/svn_mergeinfo_private.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_base/bdb/bdb_compat.h subversion/libsvn_fs_base/bdb/changes-table.h subversion/libsvn_fs_base/bdb/copies-table.h subversion/libsvn_fs_base/bdb/env.h subversion/libsvn_fs_base/bdb/miscellaneous-table.h subversion/libsvn_fs_base/bdb/node-origins-table.h subversion/libsvn_fs_base/bdb/nodes-table.h subversion/libsvn_fs_base/bdb/rev-table.h subversion/libsvn_fs_base/bdb/txn-table.h subversion/libsvn_fs_base/dag.h subversion/libsvn_fs_base/err.h subversion/libsvn_fs_base/fs.h subversion/libsvn_fs_base/id.h subversion/libsvn_fs_base/key-gen.h subversion/libsvn_fs_base/lock.h subversion/libsvn_fs_base/node-rev.h subversion/libsvn_fs_base/revs-txns.h subversion/libsvn_fs_base/trail.h subversion/libsvn_fs_base/tree.h subversion/svn_private_config.h
subversion/libsvn_fs_base/util/fs_skels.lo: subversion/libsvn_fs_base/util/fs_skels.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_skel.h subversion/include/private/svn_subr_private.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/libsvn_fs_base/bdb/bdb_compat.h subversion/libsvn_fs_base/bdb/env.h subversion/libsvn_fs_base/fs.h subversion/libsvn_fs_base/id.h subversion/libsvn_fs_base/util/fs_skels.h subversion/svn_private_config.h
subversion/libsvn_fs_base/uuid.lo: subversion/libsvn_fs_base/uuid.c subversion/include/private/svn_debug.h subversion/include/private/svn_fs_util.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_base/bdb/bdb_compat.h subversion/libsvn_fs_base/bdb/env.h subversion/libsvn_fs_base/bdb/uuids-table.h subversion/libsvn_fs_base/err.h subversion/libsvn_fs_base/fs.h subversion/libsvn_fs_base/trail.h subversion/libsvn_fs_base/uuid.h subversion/svn_private_config.h
subversion/libsvn_fs_fs/caching.lo: subversion/libsvn_fs_fs/caching.c subversion/include/private/svn_atomic.h subversion/include/private/svn_cache.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_editor.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_mutex.h subversion/include/private/svn_named_atomic.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_subr_private.h subversion/include/private/svn_token.h subversion/include/svn_cache_config.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_iter.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_fs/dag.h subversion/libsvn_fs_fs/fs.h subversion/libsvn_fs_fs/fs_fs.h subversion/libsvn_fs_fs/id.h subversion/libsvn_fs_fs/temp_serializer.h subversion/libsvn_fs_fs/tree.h subversion/svn_private_config.h
subversion/libsvn_fs_fs/dag.lo: subversion/libsvn_fs_fs/dag.c subversion/include/private/svn_atomic.h subversion/include/private/svn_cache.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_editor.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_fspath.h subversion/include/private/svn_mutex.h subversion/include/private/svn_named_atomic.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_temp_serializer.h subversion/include/private/svn_token.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_iter.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_fs/dag.h subversion/libsvn_fs_fs/fs.h subversion/libsvn_fs_fs/fs_fs.h subversion/libsvn_fs_fs/id.h subversion/libsvn_fs_fs/key-gen.h subversion/libsvn_fs_fs/temp_serializer.h subversion/svn_private_config.h
subversion/libsvn_fs_fs/fs.lo: subversion/libsvn_fs_fs/fs.c subversion/include/private/svn_atomic.h subversion/include/private/svn_cache.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_editor.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_fs_util.h subversion/include/private/svn_mutex.h subversion/include/private/svn_named_atomic.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_subr_private.h subversion/include/private/svn_token.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_iter.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_fs/fs.h subversion/libsvn_fs_fs/fs_fs.h subversion/libsvn_fs_fs/id.h subversion/libsvn_fs_fs/lock.h subversion/libsvn_fs_fs/rep-cache.h subversion/libsvn_fs_fs/tree.h subversion/svn_private_config.h
subversion/libsvn_fs_fs/fs_fs.lo: subversion/libsvn_fs_fs/fs_fs.c subversion/include/private/svn_atomic.h subversion/include/private/svn_cache.h subversion/include/private/svn_debug.h subversion/include/private/svn_delta_private.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_editor.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_fs_util.h subversion/include/private/svn_mutex.h subversion/include/private/svn_named_atomic.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_string_private.h subversion/include/private/svn_subr_private.h subversion/include/private/svn_token.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_ctype.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_iter.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_fs/fs.h subversion/libsvn_fs_fs/fs_fs.h subversion/libsvn_fs_fs/id.h subversion/libsvn_fs_fs/key-gen.h subversion/libsvn_fs_fs/lock.h subversion/libsvn_fs_fs/rep-cache.h subversion/libsvn_fs_fs/temp_serializer.h subversion/libsvn_fs_fs/tree.h subversion/svn_private_config.h
subversion/libsvn_fs_fs/id.lo: subversion/libsvn_fs_fs/id.c subversion/include/private/svn_debug.h subversion/include/private/svn_string_private.h subversion/include/private/svn_temp_serializer.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_fs/id.h
subversion/libsvn_fs_fs/key-gen.lo: subversion/libsvn_fs_fs/key-gen.c subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_fs_private.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs_fs/key-gen.h
subversion/libsvn_fs_fs/lock.lo: subversion/libsvn_fs_fs/lock.c subversion/include/private/svn_atomic.h subversion/include/private/svn_cache.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_editor.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_fs_util.h subversion/include/private/svn_fspath.h subversion/include/private/svn_mutex.h subversion/include/private/svn_named_atomic.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_iter.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_fs/fs.h subversion/libsvn_fs_fs/fs_fs.h subversion/libsvn_fs_fs/lock.h subversion/libsvn_fs_fs/tree.h subversion/svn_private_config.h
subversion/libsvn_fs_fs/rep-cache.lo: subversion/libsvn_fs_fs/rep-cache.c subversion/include/private/svn_atomic.h subversion/include/private/svn_cache.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_editor.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_mutex.h subversion/include/private/svn_named_atomic.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_iter.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_fs/fs.h subversion/libsvn_fs_fs/fs_fs.h subversion/libsvn_fs_fs/rep-cache-db.h subversion/libsvn_fs_fs/rep-cache.h subversion/svn_private_config.h
subversion/libsvn_fs_fs/temp_serializer.lo: subversion/libsvn_fs_fs/temp_serializer.c subversion/include/private/svn_atomic.h subversion/include/private/svn_cache.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_editor.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_fs_util.h subversion/include/private/svn_mutex.h subversion/include/private/svn_named_atomic.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_subr_private.h subversion/include/private/svn_temp_serializer.h subversion/include/private/svn_token.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_iter.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/libsvn_fs_fs/fs.h subversion/libsvn_fs_fs/id.h subversion/libsvn_fs_fs/temp_serializer.h
subversion/libsvn_fs_fs/tree.lo: subversion/libsvn_fs_fs/tree.c subversion/include/private/svn_atomic.h subversion/include/private/svn_cache.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_editor.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_fs_util.h subversion/include/private/svn_fspath.h subversion/include/private/svn_mergeinfo_private.h subversion/include/private/svn_mutex.h subversion/include/private/svn_named_atomic.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_subr_private.h subversion/include/private/svn_token.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_iter.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_fs/dag.h subversion/libsvn_fs_fs/fs.h subversion/libsvn_fs_fs/fs_fs.h subversion/libsvn_fs_fs/id.h subversion/libsvn_fs_fs/key-gen.h subversion/libsvn_fs_fs/lock.h subversion/libsvn_fs_fs/temp_serializer.h subversion/libsvn_fs_fs/tree.h subversion/svn_private_config.h
subversion/libsvn_fs_util/fs-util.lo: subversion/libsvn_fs_util/fs-util.c subversion/include/private/svn_debug.h subversion/include/private/svn_fs_util.h subversion/include/private/svn_fspath.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/svn_private_config.h
subversion/libsvn_ra/compat.lo: subversion/libsvn_ra/compat.c subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_fspath.h subversion/include/private/svn_ra_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_compat.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_ra/ra_loader.h subversion/svn_private_config.h
subversion/libsvn_ra/debug_reporter.lo: subversion/libsvn_ra/debug_reporter.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_ra/debug_reporter.h
subversion/libsvn_ra/deprecated.lo: subversion/libsvn_ra/deprecated.c subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_ra_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_compat.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_ra/deprecated.h subversion/libsvn_ra/ra_loader.h subversion/svn_private_config.h
subversion/libsvn_ra/editor.lo: subversion/libsvn_ra/editor.c subversion/include/private/svn_debug.h subversion/include/private/svn_delta_private.h subversion/include/private/svn_editor.h subversion/include/private/svn_ra_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_ra/ra_loader.h subversion/svn_private_config.h
subversion/libsvn_ra/ra_loader.lo: subversion/libsvn_ra/ra_loader.c subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_ra_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_dso.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_xml.h subversion/libsvn_ra/deprecated.h subversion/libsvn_ra/ra_loader.h subversion/svn_private_config.h
subversion/libsvn_ra/util.lo: subversion/libsvn_ra/util.c subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_ra_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/svn_private_config.h
subversion/libsvn_ra_local/ra_plugin.lo: subversion/libsvn_ra_local/ra_plugin.c subversion/include/private/svn_atomic.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_editor.h subversion/include/private/svn_fspath.h subversion/include/private/svn_mergeinfo_private.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_repos_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_cache_config.h subversion/include/svn_checksum.h subversion/include/svn_compat.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/libsvn_ra/ra_loader.h subversion/libsvn_ra/wrapper_template.h subversion/libsvn_ra_local/ra_local.h subversion/svn_private_config.h
subversion/libsvn_ra_local/split_url.lo: subversion/libsvn_ra_local/split_url.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_ra_local/ra_local.h subversion/svn_private_config.h
subversion/libsvn_ra_serf/blame.lo: subversion/libsvn_ra_serf/blame.c subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_string_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_base64.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_xml.h subversion/libsvn_ra/ra_loader.h subversion/libsvn_ra_serf/blncache.h subversion/libsvn_ra_serf/ra_serf.h subversion/svn_private_config.h
subversion/libsvn_ra_serf/blncache.lo: subversion/libsvn_ra_serf/blncache.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_ra_serf/blncache.h
subversion/libsvn_ra_serf/commit.lo: subversion/libsvn_ra_serf/commit.c subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_editor.h subversion/include/private/svn_fspath.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_skel.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_base64.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_xml.h subversion/libsvn_ra/ra_loader.h subversion/libsvn_ra_serf/blncache.h subversion/libsvn_ra_serf/ra_serf.h subversion/svn_private_config.h
subversion/libsvn_ra_serf/get_deleted_rev.lo: subversion/libsvn_ra_serf/get_deleted_rev.c subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_xml.h subversion/libsvn_ra/ra_loader.h subversion/libsvn_ra_serf/blncache.h subversion/libsvn_ra_serf/ra_serf.h subversion/svn_private_config.h
subversion/libsvn_ra_serf/getdate.lo: subversion/libsvn_ra_serf/getdate.c subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_xml.h subversion/libsvn_ra/ra_loader.h subversion/libsvn_ra_serf/blncache.h subversion/libsvn_ra_serf/ra_serf.h subversion/svn_private_config.h
subversion/libsvn_ra_serf/getlocations.lo: subversion/libsvn_ra_serf/getlocations.c subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_xml.h subversion/libsvn_ra/ra_loader.h subversion/libsvn_ra_serf/blncache.h subversion/libsvn_ra_serf/ra_serf.h subversion/svn_private_config.h
subversion/libsvn_ra_serf/getlocationsegments.lo: subversion/libsvn_ra_serf/getlocationsegments.c subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_xml.h subversion/libsvn_ra/ra_loader.h subversion/libsvn_ra_serf/blncache.h subversion/libsvn_ra_serf/ra_serf.h subversion/svn_private_config.h
subversion/libsvn_ra_serf/getlocks.lo: subversion/libsvn_ra_serf/getlocks.c subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_fspath.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_xml.h subversion/libsvn_ra/ra_loader.h subversion/libsvn_ra_serf/blncache.h subversion/libsvn_ra_serf/ra_serf.h subversion/svn_private_config.h
subversion/libsvn_ra_serf/inherited_props.lo: subversion/libsvn_ra_serf/inherited_props.c subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_base64.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_xml.h subversion/libsvn_ra/ra_loader.h subversion/libsvn_ra_serf/blncache.h subversion/libsvn_ra_serf/ra_serf.h subversion/svn_private_config.h
subversion/libsvn_ra_serf/locks.lo: subversion/libsvn_ra_serf/locks.c subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/libsvn_ra/ra_loader.h subversion/libsvn_ra_serf/blncache.h subversion/libsvn_ra_serf/ra_serf.h subversion/svn_private_config.h
subversion/libsvn_ra_serf/log.lo: subversion/libsvn_ra_serf/log.c subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_string_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_base64.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_xml.h subversion/libsvn_ra/ra_loader.h subversion/libsvn_ra_serf/blncache.h subversion/libsvn_ra_serf/ra_serf.h subversion/svn_private_config.h
subversion/libsvn_ra_serf/merge.lo: subversion/libsvn_ra_serf/merge.c subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_fspath.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_xml.h subversion/libsvn_ra/ra_loader.h subversion/libsvn_ra_serf/blncache.h subversion/libsvn_ra_serf/ra_serf.h subversion/svn_private_config.h
subversion/libsvn_ra_serf/mergeinfo.lo: subversion/libsvn_ra_serf/mergeinfo.c subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_xml.h subversion/libsvn_ra/ra_loader.h subversion/libsvn_ra_serf/blncache.h subversion/libsvn_ra_serf/ra_serf.h subversion/svn_private_config.h
-subversion/libsvn_ra_serf/options.lo: subversion/libsvn_ra_serf/options.c subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_fspath.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_xml.h subversion/libsvn_ra/ra_loader.h subversion/libsvn_ra_serf/blncache.h subversion/libsvn_ra_serf/ra_serf.h subversion/svn_private_config.h
+subversion/libsvn_ra_serf/options.lo: subversion/libsvn_ra_serf/options.c subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_fspath.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_ctype.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_xml.h subversion/libsvn_ra/ra_loader.h subversion/libsvn_ra_serf/blncache.h subversion/libsvn_ra_serf/ra_serf.h subversion/svn_private_config.h
subversion/libsvn_ra_serf/property.lo: subversion/libsvn_ra_serf/property.c subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_fspath.h subversion/include/private/svn_string_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_base64.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_xml.h subversion/libsvn_ra_serf/blncache.h subversion/libsvn_ra_serf/ra_serf.h subversion/svn_private_config.h
subversion/libsvn_ra_serf/replay.lo: subversion/libsvn_ra_serf/replay.c subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_string_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_base64.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_xml.h subversion/libsvn_ra/ra_loader.h subversion/libsvn_ra_serf/blncache.h subversion/libsvn_ra_serf/ra_serf.h subversion/svn_private_config.h
subversion/libsvn_ra_serf/sb_bucket.lo: subversion/libsvn_ra_serf/sb_bucket.c subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/libsvn_ra_serf/blncache.h subversion/libsvn_ra_serf/ra_serf.h subversion/svn_private_config.h
subversion/libsvn_ra_serf/serf.lo: subversion/libsvn_ra_serf/serf.c subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_editor.h subversion/include/private/svn_fspath.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_compat.h subversion/include/svn_config.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_xml.h subversion/libsvn_ra/ra_loader.h subversion/libsvn_ra/wrapper_template.h subversion/libsvn_ra_serf/blncache.h subversion/libsvn_ra_serf/ra_serf.h subversion/svn_private_config.h
subversion/libsvn_ra_serf/update.lo: subversion/libsvn_ra_serf/update.c subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_editor.h subversion/include/private/svn_fspath.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_string_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_base64.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_xml.h subversion/libsvn_ra/ra_loader.h subversion/libsvn_ra_serf/blncache.h subversion/libsvn_ra_serf/ra_serf.h subversion/svn_private_config.h
subversion/libsvn_ra_serf/util.lo: subversion/libsvn_ra_serf/util.c subversion/include/private/svn_auth_private.h subversion/include/private/svn_cert.h subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_editor.h subversion/include/private/svn_fspath.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_xml.h subversion/libsvn_ra/ra_loader.h subversion/libsvn_ra_serf/blncache.h subversion/libsvn_ra_serf/ra_serf.h subversion/svn_private_config.h
subversion/libsvn_ra_serf/util_error.lo: subversion/libsvn_ra_serf/util_error.c subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_error_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_version.h subversion/libsvn_ra_serf/blncache.h subversion/libsvn_ra_serf/ra_serf.h
subversion/libsvn_ra_serf/xml.lo: subversion/libsvn_ra_serf/xml.c subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_string_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_xml.h subversion/libsvn_ra/ra_loader.h subversion/libsvn_ra_serf/blncache.h subversion/libsvn_ra_serf/ra_serf.h subversion/svn_private_config.h
subversion/libsvn_ra_svn/client.lo: subversion/libsvn_ra_svn/client.c subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_fspath.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_ra_svn_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_compat.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_ra_svn.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/libsvn_ra/ra_loader.h subversion/libsvn_ra/wrapper_template.h subversion/libsvn_ra_svn/ra_svn.h subversion/svn_private_config.h
subversion/libsvn_ra_svn/cram.lo: subversion/libsvn_ra_svn/cram.c subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_ra_svn_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_ra.h subversion/include/svn_ra_svn.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_ra_svn/ra_svn.h subversion/svn_private_config.h
subversion/libsvn_ra_svn/cyrus_auth.lo: subversion/libsvn_ra_svn/cyrus_auth.c subversion/include/private/ra_svn_sasl.h subversion/include/private/svn_atomic.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_editor.h subversion/include/private/svn_mutex.h subversion/include/private/svn_ra_svn_private.h subversion/include/svn_auth.h subversion/include/svn_base64.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_ra_svn.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_ra_svn/ra_svn.h subversion/svn_private_config.h
subversion/libsvn_ra_svn/deprecated.lo: subversion/libsvn_ra_svn/deprecated.c subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_ra_svn_private.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_ra_svn.h subversion/include/svn_string.h subversion/include/svn_types.h
subversion/libsvn_ra_svn/editorp.lo: subversion/libsvn_ra_svn/editorp.c subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_fspath.h subversion/include/private/svn_ra_svn_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_ra_svn.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_ra_svn/ra_svn.h subversion/svn_private_config.h
subversion/libsvn_ra_svn/internal_auth.lo: subversion/libsvn_ra_svn/internal_auth.c subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_ra_svn_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_ra.h subversion/include/svn_ra_svn.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_ra_svn/ra_svn.h subversion/svn_private_config.h
subversion/libsvn_ra_svn/marshal.lo: subversion/libsvn_ra_svn/marshal.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_editor.h subversion/include/private/svn_error_private.h subversion/include/private/svn_ra_svn_private.h subversion/include/private/svn_string_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_ctype.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_ra_svn.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/libsvn_ra_svn/ra_svn.h subversion/svn_private_config.h
subversion/libsvn_ra_svn/streams.lo: subversion/libsvn_ra_svn/streams.c subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_ra_svn_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_ra_svn.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_ra_svn/ra_svn.h subversion/svn_private_config.h
subversion/libsvn_ra_svn/version.lo: subversion/libsvn_ra_svn/version.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_ra_svn.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h
subversion/libsvn_repos/authz.lo: subversion/libsvn_repos/authz.c subversion/include/private/svn_debug.h subversion/include/private/svn_fspath.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_ctype.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_repos/repos.h
subversion/libsvn_repos/commit.lo: subversion/libsvn_repos/commit.c subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_fspath.h subversion/include/private/svn_repos_private.h subversion/include/svn_checksum.h subversion/include/svn_compat.h subversion/include/svn_ctype.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_repos/repos.h subversion/svn_private_config.h
subversion/libsvn_repos/delta.lo: subversion/libsvn_repos/delta.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_repos/repos.h subversion/svn_private_config.h
subversion/libsvn_repos/deprecated.lo: subversion/libsvn_repos/deprecated.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_compat.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_repos/repos.h subversion/svn_private_config.h
subversion/libsvn_repos/dump.lo: subversion/libsvn_repos/dump.c subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_mergeinfo_private.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_iter.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/svn_private_config.h
subversion/libsvn_repos/fs-wrap.lo: subversion/libsvn_repos/fs-wrap.c subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_fspath.h subversion/include/private/svn_repos_private.h subversion/include/private/svn_utf_private.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/libsvn_repos/repos.h subversion/svn_private_config.h
subversion/libsvn_repos/hooks.lo: subversion/libsvn_repos/hooks.c subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_repos_private.h subversion/include/private/svn_string_private.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/libsvn_repos/repos.h subversion/svn_private_config.h
-subversion/libsvn_repos/load-fs-vtable.lo: subversion/libsvn_repos/load-fs-vtable.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_fspath.h subversion/include/private/svn_mergeinfo_private.h subversion/include/svn_checksum.h subversion/include/svn_ctype.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/libsvn_repos/repos.h subversion/svn_private_config.h
+subversion/libsvn_repos/load-fs-vtable.lo: subversion/libsvn_repos/load-fs-vtable.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_editor.h subversion/include/private/svn_fspath.h subversion/include/private/svn_mergeinfo_private.h subversion/include/private/svn_repos_private.h subversion/include/svn_checksum.h subversion/include/svn_ctype.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/libsvn_repos/repos.h subversion/svn_private_config.h
subversion/libsvn_repos/load.lo: subversion/libsvn_repos/load.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_mergeinfo_private.h subversion/include/svn_checksum.h subversion/include/svn_ctype.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/libsvn_repos/repos.h subversion/svn_private_config.h
subversion/libsvn_repos/log.lo: subversion/libsvn_repos/log.c subversion/include/private/svn_debug.h subversion/include/private/svn_fspath.h subversion/include/private/svn_mergeinfo_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_checksum.h subversion/include/svn_compat.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/libsvn_repos/repos.h subversion/svn_private_config.h
subversion/libsvn_repos/node_tree.lo: subversion/libsvn_repos/node_tree.c subversion/include/private/svn_debug.h subversion/include/private/svn_fspath.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_repos/repos.h subversion/svn_private_config.h
subversion/libsvn_repos/notify.lo: subversion/libsvn_repos/notify.c subversion/include/private/svn_debug.h subversion/include/private/svn_utf_private.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_repos/repos.h subversion/svn_private_config.h
subversion/libsvn_repos/replay.lo: subversion/libsvn_repos/replay.c subversion/include/private/svn_debug.h subversion/include/private/svn_delta_private.h subversion/include/private/svn_editor.h subversion/include/private/svn_fspath.h subversion/include/private/svn_repos_private.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/svn_private_config.h
subversion/libsvn_repos/reporter.lo: subversion/libsvn_repos/reporter.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_fspath.h subversion/include/private/svn_string_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/libsvn_repos/repos.h subversion/svn_private_config.h
subversion/libsvn_repos/repos.lo: subversion/libsvn_repos/repos.c subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_repos_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_version.h subversion/libsvn_repos/repos.h subversion/svn_private_config.h
subversion/libsvn_repos/rev_hunt.lo: subversion/libsvn_repos/rev_hunt.c subversion/include/private/svn_debug.h subversion/include/private/svn_fspath.h subversion/include/svn_checksum.h subversion/include/svn_compat.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/libsvn_repos/repos.h subversion/svn_private_config.h
subversion/libsvn_subr/adler32.lo: subversion/libsvn_subr/adler32.c subversion/include/private/svn_adler32.h
subversion/libsvn_subr/atomic.lo: subversion/libsvn_subr/atomic.c subversion/include/private/svn_atomic.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_types.h
subversion/libsvn_subr/auth.lo: subversion/libsvn_subr/auth.c subversion/include/private/svn_auth_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_dso.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/libsvn_subr/auth.h subversion/svn_private_config.h
subversion/libsvn_subr/base64.lo: subversion/libsvn_subr/base64.c subversion/include/private/svn_debug.h subversion/include/private/svn_string_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_base64.h subversion/include/svn_checksum.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h
subversion/libsvn_subr/cache-inprocess.lo: subversion/libsvn_subr/cache-inprocess.c subversion/include/private/svn_cache.h subversion/include/private/svn_debug.h subversion/include/private/svn_mutex.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_iter.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_subr/cache.h subversion/svn_private_config.h
subversion/libsvn_subr/cache-membuffer.lo: subversion/libsvn_subr/cache-membuffer.c subversion/include/private/svn_cache.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_mutex.h subversion/include/private/svn_pseudo_md5.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_iter.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_subr/cache.h subversion/libsvn_subr/md5.h subversion/svn_private_config.h
subversion/libsvn_subr/cache-memcache.lo: subversion/libsvn_subr/cache-memcache.c subversion/include/private/svn_cache.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/svn_base64.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_iter.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_subr/cache.h subversion/svn_private_config.h
subversion/libsvn_subr/cache.lo: subversion/libsvn_subr/cache.c subversion/include/private/svn_cache.h subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_iter.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_subr/cache.h
subversion/libsvn_subr/cache_config.lo: subversion/libsvn_subr/cache_config.c subversion/include/private/svn_atomic.h subversion/include/private/svn_cache.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/svn_cache_config.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_iter.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h
subversion/libsvn_subr/checksum.lo: subversion/libsvn_subr/checksum.c subversion/include/private/svn_debug.h subversion/include/private/svn_subr_private.h subversion/include/svn_checksum.h subversion/include/svn_ctype.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/libsvn_subr/md5.h subversion/libsvn_subr/sha1.h subversion/svn_private_config.h
subversion/libsvn_subr/cmdline.lo: subversion/libsvn_subr/cmdline.c subversion/include/private/svn_cmdline_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_string_private.h subversion/include/private/svn_utf_private.h subversion/include/svn_auth.h subversion/include/svn_base64.h subversion/include/svn_checksum.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_ctype.h subversion/include/svn_dirent_uri.h subversion/include/svn_dso.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_nls.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_xml.h subversion/libsvn_subr/win32_crashrpt.h subversion/svn_private_config.h
subversion/libsvn_subr/compat.lo: subversion/libsvn_subr/compat.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_compat.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_props.h subversion/include/svn_string.h subversion/include/svn_types.h
subversion/libsvn_subr/config.lo: subversion/libsvn_subr/config.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_subr/config_impl.h subversion/svn_private_config.h
subversion/libsvn_subr/config_auth.lo: subversion/libsvn_subr/config_auth.c subversion/include/private/svn_auth_private.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_subr/auth.h subversion/libsvn_subr/config_impl.h subversion/svn_private_config.h
subversion/libsvn_subr/config_file.lo: subversion/libsvn_subr/config_file.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_ctype.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/include/svn_user.h subversion/include/svn_utf.h subversion/libsvn_subr/config_impl.h subversion/svn_private_config.h
subversion/libsvn_subr/config_win.lo: subversion/libsvn_subr/config_win.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/libsvn_subr/config_impl.h subversion/svn_private_config.h
subversion/libsvn_subr/crypto.lo: subversion/libsvn_subr/crypto.c subversion/include/private/svn_atomic.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/svn_checksum.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_subr/crypto.h subversion/svn_private_config.h
subversion/libsvn_subr/ctype.lo: subversion/libsvn_subr/ctype.c subversion/include/svn_ctype.h
subversion/libsvn_subr/date.lo: subversion/libsvn_subr/date.c subversion/include/private/svn_debug.h subversion/include/private/svn_token.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/svn_private_config.h
subversion/libsvn_subr/debug.lo: subversion/libsvn_subr/debug.c subversion/include/private/svn_debug.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_string.h subversion/include/svn_types.h
subversion/libsvn_subr/deprecated.lo: subversion/libsvn_subr/deprecated.c subversion/include/private/svn_debug.h subversion/include/private/svn_mergeinfo_private.h subversion/include/private/svn_opt_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_dirent_uri.h subversion/include/svn_dso.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_version.h subversion/include/svn_xml.h subversion/libsvn_subr/opt.h subversion/svn_private_config.h
subversion/libsvn_subr/dirent_uri.lo: subversion/libsvn_subr/dirent_uri.c subversion/include/private/svn_cert.h subversion/include/private/svn_debug.h subversion/include/private/svn_fspath.h subversion/include/svn_ctype.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_path.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_subr/dirent_uri.h subversion/svn_private_config.h
-subversion/libsvn_subr/dso.lo: subversion/libsvn_subr/dso.c subversion/include/private/svn_debug.h subversion/include/private/svn_mutex.h subversion/include/svn_checksum.h subversion/include/svn_dso.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/svn_private_config.h
+subversion/libsvn_subr/dso.lo: subversion/libsvn_subr/dso.c subversion/include/private/svn_atomic.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_mutex.h subversion/include/svn_checksum.h subversion/include/svn_dso.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/svn_private_config.h
subversion/libsvn_subr/eol.lo: subversion/libsvn_subr/eol.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_eol_private.h subversion/include/svn_checksum.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_string.h subversion/include/svn_types.h
subversion/libsvn_subr/error.lo: subversion/libsvn_subr/error.c subversion/include/private/svn_debug.h subversion/include/private/svn_error_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/svn_private_config.h
-subversion/libsvn_subr/gpg_agent.lo: subversion/libsvn_subr/gpg_agent.c subversion/include/private/svn_auth_private.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/svn_private_config.h
+subversion/libsvn_subr/gpg_agent.lo: subversion/libsvn_subr/gpg_agent.c subversion/include/private/svn_auth_private.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_user.h subversion/svn_private_config.h
subversion/libsvn_subr/hash.lo: subversion/libsvn_subr/hash.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_subr_private.h subversion/include/svn_checksum.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/svn_private_config.h
subversion/libsvn_subr/io.lo: subversion/libsvn_subr/io.c subversion/include/private/svn_atomic.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_io_private.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_ctype.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/svn_private_config.h
subversion/libsvn_subr/iter.lo: subversion/libsvn_subr/iter.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_iter.h subversion/include/svn_pools.h subversion/include/svn_types.h
subversion/libsvn_subr/lock.lo: subversion/libsvn_subr/lock.c subversion/include/private/svn_debug.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_types.h
subversion/libsvn_subr/log.lo: subversion/libsvn_subr/log.c subversion/include/private/svn_debug.h subversion/include/private/svn_log.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h
subversion/libsvn_subr/macos_keychain.lo: subversion/libsvn_subr/macos_keychain.c subversion/include/private/svn_auth_private.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_user.h subversion/include/svn_utf.h subversion/svn_private_config.h
subversion/libsvn_subr/magic.lo: subversion/libsvn_subr/magic.c subversion/include/private/svn_debug.h subversion/include/private/svn_magic.h subversion/include/svn_checksum.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/svn_private_config.h
subversion/libsvn_subr/md5.lo: subversion/libsvn_subr/md5.c subversion/include/private/svn_debug.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_md5.h subversion/include/svn_types.h subversion/libsvn_subr/md5.h
subversion/libsvn_subr/mergeinfo.lo: subversion/libsvn_subr/mergeinfo.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_fspath.h subversion/include/private/svn_mergeinfo_private.h subversion/include/private/svn_string_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_checksum.h subversion/include/svn_ctype.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/svn_private_config.h
subversion/libsvn_subr/mutex.lo: subversion/libsvn_subr/mutex.c subversion/include/private/svn_debug.h subversion/include/private/svn_mutex.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_types.h subversion/svn_private_config.h
subversion/libsvn_subr/named_atomic.lo: subversion/libsvn_subr/named_atomic.c subversion/include/private/svn_atomic.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_mutex.h subversion/include/private/svn_named_atomic.h subversion/include/svn_checksum.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/svn_private_config.h
subversion/libsvn_subr/nls.lo: subversion/libsvn_subr/nls.c subversion/include/private/svn_debug.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_nls.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/svn_private_config.h
subversion/libsvn_subr/opt.lo: subversion/libsvn_subr/opt.c subversion/include/private/svn_debug.h subversion/include/private/svn_opt_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_ctype.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_props.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_version.h subversion/libsvn_subr/opt.h subversion/svn_private_config.h
subversion/libsvn_subr/path.lo: subversion/libsvn_subr/path.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_ctype.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/libsvn_subr/dirent_uri.h subversion/svn_private_config.h
subversion/libsvn_subr/pool.lo: subversion/libsvn_subr/pool.c subversion/include/private/svn_debug.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_pools.h subversion/include/svn_types.h
subversion/libsvn_subr/prompt.lo: subversion/libsvn_subr/prompt.c subversion/include/private/svn_cmdline_private.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_ctype.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/svn_private_config.h
subversion/libsvn_subr/properties.lo: subversion/libsvn_subr/properties.c subversion/include/private/svn_debug.h subversion/include/private/svn_subr_private.h subversion/include/svn_checksum.h subversion/include/svn_ctype.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_props.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h
subversion/libsvn_subr/pseudo_md5.lo: subversion/libsvn_subr/pseudo_md5.c subversion/include/private/svn_pseudo_md5.h
subversion/libsvn_subr/quoprint.lo: subversion/libsvn_subr/quoprint.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_quoprint.h subversion/include/svn_string.h subversion/include/svn_types.h
subversion/libsvn_subr/sha1.lo: subversion/libsvn_subr/sha1.c subversion/include/private/svn_debug.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_types.h subversion/libsvn_subr/sha1.h
subversion/libsvn_subr/simple_providers.lo: subversion/libsvn_subr/simple_providers.c subversion/include/private/svn_auth_private.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_user.h subversion/include/svn_utf.h subversion/libsvn_subr/auth.h subversion/svn_private_config.h
subversion/libsvn_subr/skel.lo: subversion/libsvn_subr/skel.c subversion/include/private/svn_debug.h subversion/include/private/svn_skel.h subversion/include/private/svn_string_private.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_string.h subversion/include/svn_types.h
subversion/libsvn_subr/sorts.lo: subversion/libsvn_subr/sorts.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h
subversion/libsvn_subr/spillbuf.lo: subversion/libsvn_subr/spillbuf.c subversion/include/private/svn_debug.h subversion/include/private/svn_subr_private.h subversion/include/svn_checksum.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h
subversion/libsvn_subr/sqlite.lo: subversion/libsvn_subr/sqlite.c subversion/include/private/svn_atomic.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/svn_checksum.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_subr/internal_statements.h subversion/svn_private_config.h
subversion/libsvn_subr/sqlite3wrapper.lo: subversion/libsvn_subr/sqlite3wrapper.c subversion/svn_private_config.h
subversion/libsvn_subr/ssl_client_cert_providers.lo: subversion/libsvn_subr/ssl_client_cert_providers.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_string.h subversion/include/svn_types.h
subversion/libsvn_subr/ssl_client_cert_pw_providers.lo: subversion/libsvn_subr/ssl_client_cert_pw_providers.c subversion/include/private/svn_auth_private.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/svn_private_config.h
subversion/libsvn_subr/ssl_server_trust_providers.lo: subversion/libsvn_subr/ssl_server_trust_providers.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_string.h subversion/include/svn_types.h
subversion/libsvn_subr/stream.lo: subversion/libsvn_subr/stream.c subversion/include/private/svn_debug.h subversion/include/private/svn_eol_private.h subversion/include/private/svn_error_private.h subversion/include/private/svn_io_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_checksum.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_version.h subversion/svn_private_config.h
subversion/libsvn_subr/string.lo: subversion/libsvn_subr/string.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_string_private.h subversion/include/svn_ctype.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/svn_private_config.h
subversion/libsvn_subr/subst.lo: subversion/libsvn_subr/subst.c subversion/include/private/svn_debug.h subversion/include/private/svn_io_private.h subversion/include/private/svn_string_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/svn_private_config.h
subversion/libsvn_subr/sysinfo.lo: subversion/libsvn_subr/sysinfo.c subversion/include/private/svn_debug.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/svn_checksum.h subversion/include/svn_ctype.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_version.h subversion/libsvn_subr/sysinfo.h subversion/svn_private_config.h
subversion/libsvn_subr/target.lo: subversion/libsvn_subr/target.c subversion/include/private/svn_debug.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h
subversion/libsvn_subr/temp_serializer.lo: subversion/libsvn_subr/temp_serializer.c subversion/include/private/svn_debug.h subversion/include/private/svn_temp_serializer.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_string.h subversion/include/svn_types.h
subversion/libsvn_subr/time.lo: subversion/libsvn_subr/time.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/svn_private_config.h
subversion/libsvn_subr/token.lo: subversion/libsvn_subr/token.c subversion/include/private/svn_debug.h subversion/include/private/svn_token.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_types.h subversion/svn_private_config.h
subversion/libsvn_subr/types.lo: subversion/libsvn_subr/types.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_props.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/svn_private_config.h
subversion/libsvn_subr/user.lo: subversion/libsvn_subr/user.c subversion/include/private/svn_debug.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_user.h subversion/include/svn_utf.h
subversion/libsvn_subr/username_providers.lo: subversion/libsvn_subr/username_providers.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_user.h subversion/include/svn_utf.h
subversion/libsvn_subr/utf.lo: subversion/libsvn_subr/utf.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_mutex.h subversion/include/private/svn_string_private.h subversion/include/private/svn_utf_private.h subversion/include/svn_checksum.h subversion/include/svn_ctype.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/libsvn_subr/win32_xlate.h subversion/svn_private_config.h
subversion/libsvn_subr/utf_validate.lo: subversion/libsvn_subr/utf_validate.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_eol_private.h subversion/include/private/svn_utf_private.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_types.h
subversion/libsvn_subr/utf_width.lo: subversion/libsvn_subr/utf_width.c subversion/include/private/svn_debug.h subversion/include/private/svn_utf_private.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/svn_private_config.h
subversion/libsvn_subr/validate.lo: subversion/libsvn_subr/validate.c subversion/include/private/svn_debug.h subversion/include/svn_ctype.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_types.h subversion/svn_private_config.h
subversion/libsvn_subr/version.lo: subversion/libsvn_subr/version.c subversion/include/private/svn_debug.h subversion/include/private/svn_subr_private.h subversion/include/svn_checksum.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/libsvn_subr/sysinfo.h subversion/svn_private_config.h
subversion/libsvn_subr/win32_crashrpt.lo: subversion/libsvn_subr/win32_crashrpt.c subversion/include/private/svn_debug.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/libsvn_subr/win32_crashrpt.h subversion/libsvn_subr/win32_crashrpt_dll.h
subversion/libsvn_subr/win32_crypto.lo: subversion/libsvn_subr/win32_crypto.c subversion/include/private/svn_auth_private.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_base64.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_user.h subversion/include/svn_utf.h subversion/svn_private_config.h
subversion/libsvn_subr/win32_xlate.lo: subversion/libsvn_subr/win32_xlate.c subversion/include/private/svn_atomic.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/libsvn_subr/win32_xlate.h
subversion/libsvn_subr/xml.lo: subversion/libsvn_subr/xml.c subversion/include/private/svn_debug.h subversion/include/private/svn_utf_private.h subversion/include/svn_checksum.h subversion/include/svn_ctype.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_xml.h subversion/svn_private_config.h
subversion/libsvn_wc/adm_crawler.lo: subversion/libsvn_wc/adm_crawler.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/adm_files.h subversion/libsvn_wc/conflicts.h subversion/libsvn_wc/props.h subversion/libsvn_wc/translate.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/libsvn_wc/workqueue.h subversion/svn_private_config.h
subversion/libsvn_wc/adm_files.lo: subversion/libsvn_wc/adm_files.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/adm_files.h subversion/libsvn_wc/entries.h subversion/libsvn_wc/lock.h subversion/libsvn_wc/props.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/svn_private_config.h
subversion/libsvn_wc/adm_ops.lo: subversion/libsvn_wc/adm_ops.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_subr_private.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_wc.h subversion/libsvn_wc/adm_files.h subversion/libsvn_wc/conflicts.h subversion/libsvn_wc/props.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/libsvn_wc/workqueue.h subversion/svn_private_config.h
subversion/libsvn_wc/ambient_depth_filter_editor.lo: subversion/libsvn_wc/ambient_depth_filter_editor.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/svn_private_config.h
subversion/libsvn_wc/cleanup.lo: subversion/libsvn_wc/cleanup.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/adm_files.h subversion/libsvn_wc/lock.h subversion/libsvn_wc/props.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/libsvn_wc/workqueue.h subversion/svn_private_config.h
subversion/libsvn_wc/conflicts.lo: subversion/libsvn_wc/conflicts.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_string_private.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/conflicts.h subversion/libsvn_wc/props.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/libsvn_wc/workqueue.h subversion/svn_private_config.h
subversion/libsvn_wc/context.lo: subversion/libsvn_wc/context.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/svn_private_config.h
subversion/libsvn_wc/copy.lo: subversion/libsvn_wc/copy.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/conflicts.h subversion/libsvn_wc/props.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/libsvn_wc/workqueue.h subversion/svn_private_config.h
subversion/libsvn_wc/crop.lo: subversion/libsvn_wc/crop.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/libsvn_wc/workqueue.h subversion/svn_private_config.h
subversion/libsvn_wc/delete.lo: subversion/libsvn_wc/delete.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/adm_files.h subversion/libsvn_wc/conflicts.h subversion/libsvn_wc/props.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/libsvn_wc/workqueue.h subversion/svn_private_config.h
subversion/libsvn_wc/deprecated.lo: subversion/libsvn_wc/deprecated.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_subr_private.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_wc.h subversion/libsvn_wc/entries.h subversion/libsvn_wc/lock.h subversion/libsvn_wc/props.h subversion/libsvn_wc/translate.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/libsvn_wc/workqueue.h subversion/svn_private_config.h
subversion/libsvn_wc/diff_editor.lo: subversion/libsvn_wc/diff_editor.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_subr_private.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_wc.h subversion/libsvn_wc/adm_files.h subversion/libsvn_wc/diff.h subversion/libsvn_wc/props.h subversion/libsvn_wc/translate.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/svn_private_config.h
subversion/libsvn_wc/diff_local.lo: subversion/libsvn_wc/diff_local.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/diff.h subversion/libsvn_wc/props.h subversion/libsvn_wc/translate.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/svn_private_config.h
subversion/libsvn_wc/entries.lo: subversion/libsvn_wc/entries.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_ctype.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/adm_files.h subversion/libsvn_wc/conflicts.h subversion/libsvn_wc/entries.h subversion/libsvn_wc/lock.h subversion/libsvn_wc/props.h subversion/libsvn_wc/tree_conflicts.h subversion/libsvn_wc/wc-queries.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/svn_private_config.h
subversion/libsvn_wc/externals.lo: subversion/libsvn_wc/externals.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_subr_private.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_wc.h subversion/libsvn_wc/adm_files.h subversion/libsvn_wc/conflicts.h subversion/libsvn_wc/props.h subversion/libsvn_wc/translate.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/libsvn_wc/workqueue.h subversion/svn_private_config.h
subversion/libsvn_wc/info.lo: subversion/libsvn_wc/info.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/svn_private_config.h
subversion/libsvn_wc/lock.lo: subversion/libsvn_wc/lock.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/adm_files.h subversion/libsvn_wc/lock.h subversion/libsvn_wc/props.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/svn_private_config.h
subversion/libsvn_wc/merge.lo: subversion/libsvn_wc/merge.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/adm_files.h subversion/libsvn_wc/conflicts.h subversion/libsvn_wc/props.h subversion/libsvn_wc/translate.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/libsvn_wc/workqueue.h subversion/svn_private_config.h
subversion/libsvn_wc/node.lo: subversion/libsvn_wc/node.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/entries.h subversion/libsvn_wc/props.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/svn_private_config.h
subversion/libsvn_wc/old-and-busted.lo: subversion/libsvn_wc/old-and-busted.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_ctype.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/include/svn_xml.h subversion/libsvn_wc/adm_files.h subversion/libsvn_wc/entries.h subversion/libsvn_wc/lock.h subversion/libsvn_wc/props.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/svn_private_config.h
subversion/libsvn_wc/props.lo: subversion/libsvn_wc/props.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_mergeinfo_private.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_string_private.h subversion/include/private/svn_subr_private.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_version.h subversion/include/svn_wc.h subversion/libsvn_wc/conflicts.h subversion/libsvn_wc/props.h subversion/libsvn_wc/translate.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/libsvn_wc/workqueue.h subversion/svn_private_config.h
subversion/libsvn_wc/questions.lo: subversion/libsvn_wc/questions.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/conflicts.h subversion/libsvn_wc/translate.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/svn_private_config.h
subversion/libsvn_wc/relocate.lo: subversion/libsvn_wc/relocate.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/props.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/svn_private_config.h
subversion/libsvn_wc/revert.lo: subversion/libsvn_wc/revert.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_io_private.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/adm_files.h subversion/libsvn_wc/props.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/libsvn_wc/workqueue.h subversion/svn_private_config.h
subversion/libsvn_wc/revision_status.lo: subversion/libsvn_wc/revision_status.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/props.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/svn_private_config.h
subversion/libsvn_wc/status.lo: subversion/libsvn_wc/status.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_fspath.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/entries.h subversion/libsvn_wc/props.h subversion/libsvn_wc/translate.h subversion/libsvn_wc/tree_conflicts.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/svn_private_config.h
subversion/libsvn_wc/translate.lo: subversion/libsvn_wc/translate.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/adm_files.h subversion/libsvn_wc/props.h subversion/libsvn_wc/translate.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/svn_private_config.h
subversion/libsvn_wc/tree_conflicts.lo: subversion/libsvn_wc/tree_conflicts.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/conflicts.h subversion/libsvn_wc/tree_conflicts.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/svn_private_config.h
subversion/libsvn_wc/update_editor.lo: subversion/libsvn_wc/update_editor.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_subr_private.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_wc.h subversion/libsvn_wc/adm_files.h subversion/libsvn_wc/conflicts.h subversion/libsvn_wc/props.h subversion/libsvn_wc/translate.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/libsvn_wc/workqueue.h subversion/svn_private_config.h
subversion/libsvn_wc/upgrade.lo: subversion/libsvn_wc/upgrade.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/adm_files.h subversion/libsvn_wc/conflicts.h subversion/libsvn_wc/entries.h subversion/libsvn_wc/props.h subversion/libsvn_wc/tree_conflicts.h subversion/libsvn_wc/wc-queries.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/libsvn_wc/workqueue.h subversion/svn_private_config.h
subversion/libsvn_wc/util.lo: subversion/libsvn_wc/util.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_wc.h subversion/libsvn_wc/entries.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/svn_private_config.h
subversion/libsvn_wc/wc_db.lo: subversion/libsvn_wc/wc_db.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/adm_files.h subversion/libsvn_wc/conflicts.h subversion/libsvn_wc/entries.h subversion/libsvn_wc/lock.h subversion/libsvn_wc/props.h subversion/libsvn_wc/token-map.h subversion/libsvn_wc/wc-queries.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/libsvn_wc/wc_db_private.h subversion/libsvn_wc/workqueue.h subversion/svn_private_config.h
subversion/libsvn_wc/wc_db_pristine.lo: subversion/libsvn_wc/wc_db_pristine.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/wc-queries.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/libsvn_wc/wc_db_private.h subversion/svn_private_config.h
subversion/libsvn_wc/wc_db_update_move.lo: subversion/libsvn_wc/wc_db_update_move.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/conflicts.h subversion/libsvn_wc/props.h subversion/libsvn_wc/token-map.h subversion/libsvn_wc/wc-queries.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/libsvn_wc/wc_db_private.h subversion/libsvn_wc/workqueue.h subversion/svn_private_config.h
subversion/libsvn_wc/wc_db_util.lo: subversion/libsvn_wc/wc_db_util.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/adm_files.h subversion/libsvn_wc/props.h subversion/libsvn_wc/wc-queries.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/libsvn_wc/wc_db_private.h subversion/svn_private_config.h
subversion/libsvn_wc/wc_db_wcroot.lo: subversion/libsvn_wc/wc_db_wcroot.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_wc.h subversion/libsvn_wc/adm_files.h subversion/libsvn_wc/props.h subversion/libsvn_wc/wc-queries.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/libsvn_wc/wc_db_private.h subversion/svn_private_config.h
subversion/libsvn_wc/wcroot_anchor.lo: subversion/libsvn_wc/wcroot_anchor.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/svn_private_config.h
subversion/libsvn_wc/workqueue.lo: subversion/libsvn_wc/workqueue.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/adm_files.h subversion/libsvn_wc/conflicts.h subversion/libsvn_wc/props.h subversion/libsvn_wc/translate.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/libsvn_wc/workqueue.h subversion/svn_private_config.h
-subversion/mod_authz_svn/mod_authz_svn.lo: subversion/mod_authz_svn/mod_authz_svn.c subversion/include/mod_authz_svn.h subversion/include/mod_dav_svn.h subversion/include/private/svn_debug.h subversion/include/private/svn_fspath.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h
+subversion/mod_authz_svn/mod_authz_svn.lo: subversion/mod_authz_svn/mod_authz_svn.c subversion/include/mod_authz_svn.h subversion/include/mod_dav_svn.h subversion/include/private/svn_debug.h subversion/include/private/svn_fspath.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/svn_private_config.h
if $(INSTALL_APACHE_MODS) ; then $(COMPILE_APACHE_MOD) $(canonicalized_srcdir)subversion/mod_authz_svn/mod_authz_svn.c ; else echo "fake" > subversion/mod_authz_svn/mod_authz_svn.lo ; fi
subversion/mod_dav_svn/activity.lo: subversion/mod_dav_svn/activity.c subversion/include/mod_authz_svn.h subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_xml.h subversion/mod_dav_svn/dav_svn.h
if $(INSTALL_APACHE_MODS) ; then $(COMPILE_APACHE_MOD) $(canonicalized_srcdir)subversion/mod_dav_svn/activity.c ; else echo "fake" > subversion/mod_dav_svn/activity.lo ; fi
subversion/mod_dav_svn/authz.lo: subversion/mod_dav_svn/authz.c subversion/include/mod_authz_svn.h subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_fspath.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_xml.h subversion/mod_dav_svn/dav_svn.h
if $(INSTALL_APACHE_MODS) ; then $(COMPILE_APACHE_MOD) $(canonicalized_srcdir)subversion/mod_dav_svn/authz.c ; else echo "fake" > subversion/mod_dav_svn/authz.lo ; fi
subversion/mod_dav_svn/deadprops.lo: subversion/mod_dav_svn/deadprops.c subversion/include/mod_authz_svn.h subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_log.h subversion/include/private/svn_skel.h subversion/include/svn_base64.h subversion/include/svn_checksum.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_xml.h subversion/mod_dav_svn/dav_svn.h
if $(INSTALL_APACHE_MODS) ; then $(COMPILE_APACHE_MOD) $(canonicalized_srcdir)subversion/mod_dav_svn/deadprops.c ; else echo "fake" > subversion/mod_dav_svn/deadprops.lo ; fi
subversion/mod_dav_svn/liveprops.lo: subversion/mod_dav_svn/liveprops.c subversion/include/mod_authz_svn.h subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_xml.h subversion/mod_dav_svn/dav_svn.h
if $(INSTALL_APACHE_MODS) ; then $(COMPILE_APACHE_MOD) $(canonicalized_srcdir)subversion/mod_dav_svn/liveprops.c ; else echo "fake" > subversion/mod_dav_svn/liveprops.lo ; fi
subversion/mod_dav_svn/lock.lo: subversion/mod_dav_svn/lock.c subversion/include/mod_authz_svn.h subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_log.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_xml.h subversion/mod_dav_svn/dav_svn.h
if $(INSTALL_APACHE_MODS) ; then $(COMPILE_APACHE_MOD) $(canonicalized_srcdir)subversion/mod_dav_svn/lock.c ; else echo "fake" > subversion/mod_dav_svn/lock.lo ; fi
subversion/mod_dav_svn/merge.lo: subversion/mod_dav_svn/merge.c subversion/include/mod_authz_svn.h subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_fspath.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_xml.h subversion/mod_dav_svn/dav_svn.h
if $(INSTALL_APACHE_MODS) ; then $(COMPILE_APACHE_MOD) $(canonicalized_srcdir)subversion/mod_dav_svn/merge.c ; else echo "fake" > subversion/mod_dav_svn/merge.lo ; fi
subversion/mod_dav_svn/mirror.lo: subversion/mod_dav_svn/mirror.c subversion/include/mod_authz_svn.h subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_fspath.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_xml.h subversion/mod_dav_svn/dav_svn.h
if $(INSTALL_APACHE_MODS) ; then $(COMPILE_APACHE_MOD) $(canonicalized_srcdir)subversion/mod_dav_svn/mirror.c ; else echo "fake" > subversion/mod_dav_svn/mirror.lo ; fi
subversion/mod_dav_svn/mod_dav_svn.lo: subversion/mod_dav_svn/mod_dav_svn.c subversion/include/mod_authz_svn.h subversion/include/mod_dav_svn.h subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_fspath.h subversion/include/private/svn_skel.h subversion/include/private/svn_subr_private.h subversion/include/svn_cache_config.h subversion/include/svn_checksum.h subversion/include/svn_ctype.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_dso.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_version.h subversion/include/svn_xml.h subversion/mod_dav_svn/dav_svn.h
if $(INSTALL_APACHE_MODS) ; then $(COMPILE_APACHE_MOD) $(canonicalized_srcdir)subversion/mod_dav_svn/mod_dav_svn.c ; else echo "fake" > subversion/mod_dav_svn/mod_dav_svn.lo ; fi
subversion/mod_dav_svn/posts/create_txn.lo: subversion/mod_dav_svn/posts/create_txn.c subversion/include/mod_authz_svn.h subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_xml.h subversion/mod_dav_svn/dav_svn.h
if $(INSTALL_APACHE_MODS) ; then $(COMPILE_APACHE_MOD) $(canonicalized_srcdir)subversion/mod_dav_svn/posts/create_txn.c ; else echo "fake" > subversion/mod_dav_svn/posts/create_txn.lo ; fi
subversion/mod_dav_svn/reports/dated-rev.lo: subversion/mod_dav_svn/reports/dated-rev.c subversion/include/mod_authz_svn.h subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_skel.h subversion/include/svn_base64.h subversion/include/svn_checksum.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_xml.h subversion/mod_dav_svn/dav_svn.h
if $(INSTALL_APACHE_MODS) ; then $(COMPILE_APACHE_MOD) $(canonicalized_srcdir)subversion/mod_dav_svn/reports/dated-rev.c ; else echo "fake" > subversion/mod_dav_svn/reports/dated-rev.lo ; fi
subversion/mod_dav_svn/reports/deleted-rev.lo: subversion/mod_dav_svn/reports/deleted-rev.c subversion/include/mod_authz_svn.h subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_fspath.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_xml.h subversion/mod_dav_svn/dav_svn.h
if $(INSTALL_APACHE_MODS) ; then $(COMPILE_APACHE_MOD) $(canonicalized_srcdir)subversion/mod_dav_svn/reports/deleted-rev.c ; else echo "fake" > subversion/mod_dav_svn/reports/deleted-rev.lo ; fi
subversion/mod_dav_svn/reports/file-revs.lo: subversion/mod_dav_svn/reports/file-revs.c subversion/include/mod_authz_svn.h subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_fspath.h subversion/include/private/svn_log.h subversion/include/private/svn_skel.h subversion/include/svn_base64.h subversion/include/svn_checksum.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_xml.h subversion/mod_dav_svn/dav_svn.h
if $(INSTALL_APACHE_MODS) ; then $(COMPILE_APACHE_MOD) $(canonicalized_srcdir)subversion/mod_dav_svn/reports/file-revs.c ; else echo "fake" > subversion/mod_dav_svn/reports/file-revs.lo ; fi
subversion/mod_dav_svn/reports/get-location-segments.lo: subversion/mod_dav_svn/reports/get-location-segments.c subversion/include/mod_authz_svn.h subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_fspath.h subversion/include/private/svn_skel.h subversion/include/svn_base64.h subversion/include/svn_checksum.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_xml.h subversion/mod_dav_svn/dav_svn.h
if $(INSTALL_APACHE_MODS) ; then $(COMPILE_APACHE_MOD) $(canonicalized_srcdir)subversion/mod_dav_svn/reports/get-location-segments.c ; else echo "fake" > subversion/mod_dav_svn/reports/get-location-segments.lo ; fi
subversion/mod_dav_svn/reports/get-locations.lo: subversion/mod_dav_svn/reports/get-locations.c subversion/include/mod_authz_svn.h subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_fspath.h subversion/include/private/svn_skel.h subversion/include/svn_base64.h subversion/include/svn_checksum.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_xml.h subversion/mod_dav_svn/dav_svn.h
if $(INSTALL_APACHE_MODS) ; then $(COMPILE_APACHE_MOD) $(canonicalized_srcdir)subversion/mod_dav_svn/reports/get-locations.c ; else echo "fake" > subversion/mod_dav_svn/reports/get-locations.lo ; fi
subversion/mod_dav_svn/reports/get-locks.lo: subversion/mod_dav_svn/reports/get-locks.c subversion/include/mod_authz_svn.h subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_skel.h subversion/include/svn_base64.h subversion/include/svn_checksum.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_xml.h subversion/mod_dav_svn/dav_svn.h
if $(INSTALL_APACHE_MODS) ; then $(COMPILE_APACHE_MOD) $(canonicalized_srcdir)subversion/mod_dav_svn/reports/get-locks.c ; else echo "fake" > subversion/mod_dav_svn/reports/get-locks.lo ; fi
subversion/mod_dav_svn/reports/inherited-props.lo: subversion/mod_dav_svn/reports/inherited-props.c subversion/include/mod_authz_svn.h subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_fspath.h subversion/include/private/svn_log.h subversion/include/private/svn_mergeinfo_private.h subversion/include/private/svn_skel.h subversion/include/svn_base64.h subversion/include/svn_checksum.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_xml.h subversion/mod_dav_svn/dav_svn.h
if $(INSTALL_APACHE_MODS) ; then $(COMPILE_APACHE_MOD) $(canonicalized_srcdir)subversion/mod_dav_svn/reports/inherited-props.c ; else echo "fake" > subversion/mod_dav_svn/reports/inherited-props.lo ; fi
subversion/mod_dav_svn/reports/log.lo: subversion/mod_dav_svn/reports/log.c subversion/include/mod_authz_svn.h subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_fspath.h subversion/include/private/svn_log.h subversion/include/private/svn_skel.h subversion/include/svn_base64.h subversion/include/svn_checksum.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_xml.h subversion/mod_dav_svn/dav_svn.h
if $(INSTALL_APACHE_MODS) ; then $(COMPILE_APACHE_MOD) $(canonicalized_srcdir)subversion/mod_dav_svn/reports/log.c ; else echo "fake" > subversion/mod_dav_svn/reports/log.lo ; fi
subversion/mod_dav_svn/reports/mergeinfo.lo: subversion/mod_dav_svn/reports/mergeinfo.c subversion/include/mod_authz_svn.h subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_fspath.h subversion/include/private/svn_log.h subversion/include/private/svn_mergeinfo_private.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_xml.h subversion/mod_dav_svn/dav_svn.h
if $(INSTALL_APACHE_MODS) ; then $(COMPILE_APACHE_MOD) $(canonicalized_srcdir)subversion/mod_dav_svn/reports/mergeinfo.c ; else echo "fake" > subversion/mod_dav_svn/reports/mergeinfo.lo ; fi
subversion/mod_dav_svn/reports/replay.lo: subversion/mod_dav_svn/reports/replay.c subversion/include/mod_authz_svn.h subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_log.h subversion/include/private/svn_skel.h subversion/include/svn_base64.h subversion/include/svn_checksum.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_xml.h subversion/mod_dav_svn/dav_svn.h
if $(INSTALL_APACHE_MODS) ; then $(COMPILE_APACHE_MOD) $(canonicalized_srcdir)subversion/mod_dav_svn/reports/replay.c ; else echo "fake" > subversion/mod_dav_svn/reports/replay.lo ; fi
subversion/mod_dav_svn/reports/update.lo: subversion/mod_dav_svn/reports/update.c subversion/include/mod_authz_svn.h subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_fspath.h subversion/include/private/svn_log.h subversion/include/private/svn_skel.h subversion/include/svn_base64.h subversion/include/svn_checksum.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_xml.h subversion/mod_dav_svn/dav_svn.h
if $(INSTALL_APACHE_MODS) ; then $(COMPILE_APACHE_MOD) $(canonicalized_srcdir)subversion/mod_dav_svn/reports/update.c ; else echo "fake" > subversion/mod_dav_svn/reports/update.lo ; fi
subversion/mod_dav_svn/repos.lo: subversion/mod_dav_svn/repos.c subversion/include/mod_authz_svn.h subversion/include/mod_dav_svn.h subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_fspath.h subversion/include/private/svn_log.h subversion/include/private/svn_repos_private.h subversion/include/private/svn_skel.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_ctype.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_xml.h subversion/mod_dav_svn/dav_svn.h
if $(INSTALL_APACHE_MODS) ; then $(COMPILE_APACHE_MOD) $(canonicalized_srcdir)subversion/mod_dav_svn/repos.c ; else echo "fake" > subversion/mod_dav_svn/repos.lo ; fi
subversion/mod_dav_svn/util.lo: subversion/mod_dav_svn/util.c subversion/include/mod_authz_svn.h subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_fspath.h subversion/include/private/svn_skel.h subversion/include/private/svn_string_private.h subversion/include/svn_base64.h subversion/include/svn_checksum.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_xml.h subversion/mod_dav_svn/dav_svn.h
if $(INSTALL_APACHE_MODS) ; then $(COMPILE_APACHE_MOD) $(canonicalized_srcdir)subversion/mod_dav_svn/util.c ; else echo "fake" > subversion/mod_dav_svn/util.lo ; fi
subversion/mod_dav_svn/version.lo: subversion/mod_dav_svn/version.c subversion/include/mod_authz_svn.h subversion/include/private/svn_dav_protocol.h subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_fspath.h subversion/include/private/svn_log.h subversion/include/private/svn_repos_private.h subversion/include/private/svn_skel.h subversion/include/private/svn_subr_private.h subversion/include/svn_base64.h subversion/include/svn_checksum.h subversion/include/svn_dav.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_xml.h subversion/mod_dav_svn/dav_svn.h
if $(INSTALL_APACHE_MODS) ; then $(COMPILE_APACHE_MOD) $(canonicalized_srcdir)subversion/mod_dav_svn/version.c ; else echo "fake" > subversion/mod_dav_svn/version.lo ; fi
subversion/po/de.mo: subversion/po/de.po
subversion/po/es.mo: subversion/po/es.po
subversion/po/fr.mo: subversion/po/fr.po
subversion/po/it.mo: subversion/po/it.po
subversion/po/ja.mo: subversion/po/ja.po
subversion/po/ko.mo: subversion/po/ko.po
subversion/po/nb.mo: subversion/po/nb.po
subversion/po/pl.mo: subversion/po/pl.po
subversion/po/pt_BR.mo: subversion/po/pt_BR.po
subversion/po/sv.mo: subversion/po/sv.po
subversion/po/zh_CN.mo: subversion/po/zh_CN.po
subversion/po/zh_TW.mo: subversion/po/zh_TW.po
subversion/svn/add-cmd.lo: subversion/svn/add-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/blame-cmd.lo: subversion/svn/blame-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/include/svn_xml.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/cat-cmd.lo: subversion/svn/cat-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/changelist-cmd.lo: subversion/svn/changelist-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/checkout-cmd.lo: subversion/svn/checkout-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/cl-conflicts.lo: subversion/svn/cl-conflicts.c subversion/include/private/svn_debug.h subversion/include/private/svn_token.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/include/svn_xml.h subversion/svn/cl-conflicts.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/cleanup-cmd.lo: subversion/svn/cleanup-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/commit-cmd.lo: subversion/svn/commit-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/conflict-callbacks.lo: subversion/svn/conflict-callbacks.c subversion/include/private/svn_cmdline_private.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_wc.h subversion/svn/cl-conflicts.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/copy-cmd.lo: subversion/svn/copy-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/delete-cmd.lo: subversion/svn/delete-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/deprecated.lo: subversion/svn/deprecated.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn/cl.h
subversion/svn/diff-cmd.lo: subversion/svn/diff-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/include/svn_xml.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/export-cmd.lo: subversion/svn/export-cmd.c subversion/include/private/svn_debug.h subversion/include/private/svn_opt_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/file-merge.lo: subversion/svn/file-merge.c subversion/include/private/svn_cmdline_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_utf_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_wc.h subversion/include/svn_xml.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/help-cmd.lo: subversion/svn/help-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/import-cmd.lo: subversion/svn/import-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/info-cmd.lo: subversion/svn/info-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/include/svn_xml.h subversion/svn/cl-conflicts.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/list-cmd.lo: subversion/svn/list-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_wc.h subversion/include/svn_xml.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/lock-cmd.lo: subversion/svn/lock-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/log-cmd.lo: subversion/svn/log-cmd.c subversion/include/private/svn_cmdline_private.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_compat.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/include/svn_xml.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/merge-cmd.lo: subversion/svn/merge-cmd.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/mergeinfo-cmd.lo: subversion/svn/mergeinfo-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/mkdir-cmd.lo: subversion/svn/mkdir-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/move-cmd.lo: subversion/svn/move-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/notify.lo: subversion/svn/notify.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/patch-cmd.lo: subversion/svn/patch-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/propdel-cmd.lo: subversion/svn/propdel-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/propedit-cmd.lo: subversion/svn/propedit-cmd.c subversion/include/private/svn_cmdline_private.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/propget-cmd.lo: subversion/svn/propget-cmd.c subversion/include/private/svn_cmdline_private.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_wc.h subversion/include/svn_xml.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/proplist-cmd.lo: subversion/svn/proplist-cmd.c subversion/include/private/svn_cmdline_private.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/include/svn_xml.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/props.lo: subversion/svn/props.c subversion/include/private/svn_cmdline_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_string_private.h subversion/include/svn_auth.h subversion/include/svn_base64.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/include/svn_xml.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/propset-cmd.lo: subversion/svn/propset-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/relocate-cmd.lo: subversion/svn/relocate-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/resolve-cmd.lo: subversion/svn/resolve-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/resolved-cmd.lo: subversion/svn/resolved-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/revert-cmd.lo: subversion/svn/revert-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/status-cmd.lo: subversion/svn/status-cmd.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/include/svn_xml.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/status.lo: subversion/svn/status.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/include/svn_xml.h subversion/svn/cl-conflicts.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/svn.lo: subversion/svn/svn.c subversion/include/private/svn_cmdline_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_opt_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_version.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/switch-cmd.lo: subversion/svn/switch-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/unlock-cmd.lo: subversion/svn/unlock-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/update-cmd.lo: subversion/svn/update-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/upgrade-cmd.lo: subversion/svn/upgrade-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svn/util.lo: subversion/svn/util.c subversion/include/private/svn_client_private.h subversion/include/private/svn_cmdline_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_opt_private.h subversion/include/private/svn_string_private.h subversion/include/private/svn_token.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_ctype.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_wc.h subversion/include/svn_xml.h subversion/svn/cl.h subversion/svn_private_config.h
subversion/svnadmin/svnadmin.lo: subversion/svnadmin/svnadmin.c subversion/include/private/svn_cmdline_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_opt_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_cache_config.h subversion/include/svn_checksum.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_user.h subversion/include/svn_utf.h subversion/include/svn_version.h subversion/include/svn_xml.h subversion/svn_private_config.h
subversion/svndumpfilter/svndumpfilter.lo: subversion/svndumpfilter/svndumpfilter.c subversion/include/private/svn_cmdline_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_mergeinfo_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_version.h subversion/svn_private_config.h
subversion/svnlook/svnlook.lo: subversion/svnlook/svnlook.c subversion/include/private/svn_cmdline_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_private.h subversion/include/private/svn_fspath.h subversion/include/private/svn_io_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_version.h subversion/include/svn_xml.h subversion/svn_private_config.h
subversion/svnmucc/svnmucc.lo: subversion/svnmucc/svnmucc.c subversion/include/private/svn_cmdline_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_string_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_version.h subversion/include/svn_wc.h subversion/svn_private_config.h
subversion/svnrdump/dump_editor.lo: subversion/svnrdump/dump_editor.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_editor.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/svnrdump/svnrdump.h
subversion/svnrdump/load_editor.lo: subversion/svnrdump/load_editor.c subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_fspath.h subversion/include/private/svn_mergeinfo_private.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_repos_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/svn_private_config.h subversion/svnrdump/svnrdump.h
subversion/svnrdump/svnrdump.lo: subversion/svnrdump/svnrdump.c subversion/include/private/svn_cmdline_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_ra_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_wc.h subversion/svn_private_config.h subversion/svnrdump/svnrdump.h
subversion/svnrdump/util.lo: subversion/svnrdump/util.c subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/svnrdump/svnrdump.h
subversion/svnserve/cyrus_auth.lo: subversion/svnserve/cyrus_auth.c subversion/include/private/ra_svn_sasl.h subversion/include/private/svn_atomic.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_editor.h subversion/include/private/svn_ra_svn_private.h subversion/include/svn_base64.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_ra_svn.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/svn_private_config.h subversion/svnserve/server.h
subversion/svnserve/log-escape.lo: subversion/svnserve/log-escape.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_ctype.h subversion/include/svn_delta.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_ra_svn.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/svnserve/server.h
subversion/svnserve/serve.lo: subversion/svnserve/serve.c subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_fspath.h subversion/include/private/svn_log.h subversion/include/private/svn_mergeinfo_private.h subversion/include/private/svn_ra_svn_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_compat.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_ra_svn.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_user.h subversion/svn_private_config.h subversion/svnserve/server.h
subversion/svnserve/svnserve.lo: subversion/svnserve/svnserve.c subversion/include/private/svn_atomic.h subversion/include/private/svn_cmdline_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_cache_config.h subversion/include/svn_checksum.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra_svn.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_version.h subversion/svn_private_config.h subversion/svnserve/server.h subversion/svnserve/winservice.h
subversion/svnserve/winservice.lo: subversion/svnserve/winservice.c subversion/include/private/svn_debug.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_types.h subversion/svn_private_config.h subversion/svnserve/winservice.h
subversion/svnsync/svnsync.lo: subversion/svnsync/svnsync.c subversion/include/private/svn_cmdline_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_opt_private.h subversion/include/private/svn_ra_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_version.h subversion/svn_private_config.h subversion/svnsync/sync.h
-subversion/svnsync/sync.lo: subversion/svnsync/sync.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/svn_private_config.h subversion/svnsync/sync.h
+subversion/svnsync/sync.lo: subversion/svnsync/sync.c subversion/include/private/svn_debug.h subversion/include/private/svn_string_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/svn_private_config.h subversion/svnsync/sync.h
subversion/svnversion/svnversion.lo: subversion/svnversion/svnversion.c subversion/include/private/svn_cmdline_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_opt_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_version.h subversion/include/svn_wc.h subversion/svn_private_config.h
subversion/tests/cmdline/atomic-ra-revprop-change.lo: subversion/tests/cmdline/atomic-ra-revprop-change.c subversion/include/private/svn_debug.h subversion/include/private/svn_skel.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/svn_private_config.h
subversion/tests/cmdline/entries-dump.lo: subversion/tests/cmdline/entries-dump.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/lock.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/svn_private_config.h
subversion/tests/libsvn_client/client-test.lo: subversion/tests/libsvn_client/client-test.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_editor.h subversion/include/private/svn_magic.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_client/client.h subversion/libsvn_client/mergeinfo.h subversion/tests/svn_test.h subversion/tests/svn_test_fs.h
subversion/tests/libsvn_delta/random-test.lo: subversion/tests/libsvn_delta/random-test.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_ctype.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_delta/compose_delta.c subversion/libsvn_delta/delta.h subversion/tests/libsvn_delta/delta-window-test.h subversion/tests/libsvn_delta/range-index-test.h subversion/tests/svn_test.h
subversion/tests/libsvn_delta/svndiff-test.lo: subversion/tests/libsvn_delta/svndiff-test.c subversion/include/private/svn_debug.h subversion/include/svn_base64.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_quoprint.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/tests/svn_test.h
subversion/tests/libsvn_delta/vdelta-test.lo: subversion/tests/libsvn_delta/vdelta-test.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_ctype.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_delta/delta.h subversion/tests/libsvn_delta/delta-window-test.h subversion/tests/svn_test.h
subversion/tests/libsvn_delta/window-test.lo: subversion/tests/libsvn_delta/window-test.c subversion/include/private/svn_debug.h subversion/include/private/svn_subr_private.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/tests/svn_test.h
subversion/tests/libsvn_diff/diff-diff3-test.lo: subversion/tests/libsvn_diff/diff-diff3-test.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/tests/svn_test.h
subversion/tests/libsvn_diff/parse-diff-test.lo: subversion/tests/libsvn_diff/parse-diff-test.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/tests/svn_test.h
subversion/tests/libsvn_fs/fs-test.lo: subversion/tests/libsvn_fs/fs-test.c subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_fs_private.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/libsvn_delta/delta.h subversion/tests/svn_test.h subversion/tests/svn_test_fs.h
subversion/tests/libsvn_fs/locks-test.lo: subversion/tests/libsvn_fs/locks-test.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/tests/svn_test.h subversion/tests/svn_test_fs.h
subversion/tests/libsvn_fs_base/changes-test.lo: subversion/tests/libsvn_fs_base/changes-test.c subversion/include/private/svn_debug.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs_base/bdb/bdb_compat.h subversion/libsvn_fs_base/bdb/changes-table.h subversion/libsvn_fs_base/bdb/env.h subversion/libsvn_fs_base/fs.h subversion/libsvn_fs_base/trail.h subversion/libsvn_fs_base/util/fs_skels.h subversion/svn_private_config.h subversion/tests/svn_test.h subversion/tests/svn_test_fs.h
subversion/tests/libsvn_fs_base/fs-base-test.lo: subversion/tests/libsvn_fs_base/fs-base-test.c subversion/include/private/svn_debug.h subversion/include/private/svn_fs_util.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/libsvn_delta/delta.h subversion/libsvn_fs_base/bdb/bdb_compat.h subversion/libsvn_fs_base/bdb/env.h subversion/libsvn_fs_base/bdb/nodes-table.h subversion/libsvn_fs_base/bdb/txn-table.h subversion/libsvn_fs_base/fs.h subversion/libsvn_fs_base/id.h subversion/libsvn_fs_base/key-gen.h subversion/libsvn_fs_base/trail.h subversion/svn_private_config.h subversion/tests/svn_test.h subversion/tests/svn_test_fs.h
subversion/tests/libsvn_fs_base/strings-reps-test.lo: subversion/tests/libsvn_fs_base/strings-reps-test.c subversion/include/private/svn_debug.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_base/bdb/bdb_compat.h subversion/libsvn_fs_base/bdb/env.h subversion/libsvn_fs_base/bdb/reps-table.h subversion/libsvn_fs_base/bdb/strings-table.h subversion/libsvn_fs_base/fs.h subversion/libsvn_fs_base/trail.h subversion/libsvn_fs_base/util/fs_skels.h subversion/svn_private_config.h subversion/tests/svn_test.h subversion/tests/svn_test_fs.h
-subversion/tests/libsvn_fs_fs/fs-pack-test.lo: subversion/tests/libsvn_fs_fs/fs-pack-test.c subversion/include/private/svn_atomic.h subversion/include/private/svn_cache.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_editor.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_mutex.h subversion/include/private/svn_named_atomic.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_string_private.h subversion/include/private/svn_token.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_iter.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs_fs/fs.h subversion/tests/svn_test.h subversion/tests/svn_test_fs.h
+subversion/tests/libsvn_fs_fs/fs-pack-test.lo: subversion/tests/libsvn_fs_fs/fs-pack-test.c subversion/include/private/svn_atomic.h subversion/include/private/svn_cache.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_editor.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_mutex.h subversion/include/private/svn_named_atomic.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_string_private.h subversion/include/private/svn_token.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_iter.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_fs/fs.h subversion/tests/svn_test.h subversion/tests/svn_test_fs.h
-subversion/tests/libsvn_ra/ra-test.lo: subversion/tests/libsvn_ra/ra-test.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_ra_local/ra_local.h subversion/tests/svn_test.h subversion/tests/svn_test_fs.h
+subversion/tests/libsvn_ra/ra-test.lo: subversion/tests/libsvn_ra/ra-test.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/libsvn_ra_local/ra_local.h subversion/tests/svn_test.h subversion/tests/svn_test_fs.h
subversion/tests/libsvn_ra_local/ra-local-test.lo: subversion/tests/libsvn_ra_local/ra-local-test.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_ra_local/ra_local.h subversion/tests/svn_test.h subversion/tests/svn_test_fs.h
subversion/tests/libsvn_repos/dir-delta-editor.lo: subversion/tests/libsvn_repos/dir-delta-editor.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/tests/libsvn_repos/dir-delta-editor.h subversion/tests/svn_test.h
-subversion/tests/libsvn_repos/repos-test.lo: subversion/tests/libsvn_repos/repos-test.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/tests/libsvn_repos/dir-delta-editor.h subversion/tests/svn_test.h subversion/tests/svn_test_fs.h
+subversion/tests/libsvn_repos/repos-test.lo: subversion/tests/libsvn_repos/repos-test.c subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_repos_private.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/tests/libsvn_repos/dir-delta-editor.h subversion/tests/svn_test.h subversion/tests/svn_test_fs.h
subversion/tests/libsvn_subr/auth-test.lo: subversion/tests/libsvn_subr/auth-test.c subversion/include/private/svn_auth_private.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/svn_private_config.h subversion/tests/svn_test.h
subversion/tests/libsvn_subr/cache-test.lo: subversion/tests/libsvn_subr/cache-test.c subversion/include/private/svn_cache.h subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_iter.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/svn_private_config.h subversion/tests/svn_test.h
subversion/tests/libsvn_subr/checksum-test.lo: subversion/tests/libsvn_subr/checksum-test.c subversion/include/private/svn_debug.h subversion/include/private/svn_pseudo_md5.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/tests/svn_test.h
subversion/tests/libsvn_subr/compat-test.lo: subversion/tests/libsvn_subr/compat-test.c subversion/include/private/svn_debug.h subversion/include/private/svn_subr_private.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/svn_private_config.h subversion/tests/svn_test.h
subversion/tests/libsvn_subr/config-test.lo: subversion/tests/libsvn_subr/config-test.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/tests/svn_test.h
subversion/tests/libsvn_subr/crypto-test.lo: subversion/tests/libsvn_subr/crypto-test.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_subr/crypto.h subversion/tests/svn_test.h
subversion/tests/libsvn_subr/dirent_uri-test.lo: subversion/tests/libsvn_subr/dirent_uri-test.c subversion/include/private/svn_cert.h subversion/include/private/svn_debug.h subversion/include/private/svn_fspath.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/tests/svn_test.h
subversion/tests/libsvn_subr/error-code-test.lo: subversion/tests/libsvn_subr/error-code-test.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/tests/svn_test.h
subversion/tests/libsvn_subr/error-test.lo: subversion/tests/libsvn_subr/error-test.c subversion/include/private/svn_debug.h subversion/include/private/svn_error_private.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/tests/svn_test.h
subversion/tests/libsvn_subr/hashdump-test.lo: subversion/tests/libsvn_subr/hashdump-test.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/tests/svn_test.h
subversion/tests/libsvn_subr/io-test.lo: subversion/tests/libsvn_subr/io-test.c subversion/include/private/svn_debug.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/tests/svn_test.h subversion/tests/svn_test_fs.h
subversion/tests/libsvn_subr/mergeinfo-test.lo: subversion/tests/libsvn_subr/mergeinfo-test.c subversion/include/private/svn_debug.h subversion/include/private/svn_mergeinfo_private.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/tests/svn_test.h
subversion/tests/libsvn_subr/named_atomic-test-proc.lo: subversion/tests/libsvn_subr/named_atomic-test-proc.c subversion/include/private/svn_debug.h subversion/include/private/svn_named_atomic.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/tests/libsvn_subr/named_atomic-test-common.h subversion/tests/svn_test.h
subversion/tests/libsvn_subr/named_atomic-test.lo: subversion/tests/libsvn_subr/named_atomic-test.c subversion/include/private/svn_debug.h subversion/include/private/svn_named_atomic.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/tests/libsvn_subr/named_atomic-test-common.h subversion/tests/svn_test.h
subversion/tests/libsvn_subr/opt-test.lo: subversion/tests/libsvn_subr/opt-test.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/tests/svn_test.h
subversion/tests/libsvn_subr/path-test.lo: subversion/tests/libsvn_subr/path-test.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/tests/svn_test.h
subversion/tests/libsvn_subr/revision-test.lo: subversion/tests/libsvn_subr/revision-test.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/tests/svn_test.h
subversion/tests/libsvn_subr/skel-test.lo: subversion/tests/libsvn_subr/skel-test.c subversion/include/private/svn_debug.h subversion/include/private/svn_skel.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/tests/svn_test.h subversion/tests/svn_test_fs.h
subversion/tests/libsvn_subr/spillbuf-test.lo: subversion/tests/libsvn_subr/spillbuf-test.c subversion/include/private/svn_debug.h subversion/include/private/svn_subr_private.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/tests/svn_test.h
subversion/tests/libsvn_subr/stream-test.lo: subversion/tests/libsvn_subr/stream-test.c subversion/include/private/svn_debug.h subversion/include/private/svn_io_private.h subversion/include/svn_base64.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/tests/svn_test.h
subversion/tests/libsvn_subr/string-test.lo: subversion/tests/libsvn_subr/string-test.c subversion/include/private/svn_debug.h subversion/include/private/svn_string_private.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/tests/svn_test.h
subversion/tests/libsvn_subr/subst_translate-test.lo: subversion/tests/libsvn_subr/subst_translate-test.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/tests/svn_test.h
subversion/tests/libsvn_subr/time-test.lo: subversion/tests/libsvn_subr/time-test.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/tests/svn_test.h
subversion/tests/libsvn_subr/translate-test.lo: subversion/tests/libsvn_subr/translate-test.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_subst.h subversion/include/svn_types.h subversion/tests/svn_test.h
subversion/tests/libsvn_subr/utf-test.lo: subversion/tests/libsvn_subr/utf-test.c subversion/include/private/svn_debug.h subversion/include/private/svn_utf_private.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/tests/svn_test.h
subversion/tests/libsvn_wc/conflict-data-test.lo: subversion/tests/libsvn_wc/conflict-data-test.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/conflicts.h subversion/libsvn_wc/tree_conflicts.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/svn_private_config.h subversion/tests/libsvn_wc/utils.h subversion/tests/svn_test.h
subversion/tests/libsvn_wc/db-test.lo: subversion/tests/libsvn_wc/db-test.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/wc_db.h subversion/svn_private_config.h subversion/tests/libsvn_wc/utils.h subversion/tests/svn_test.h
subversion/tests/libsvn_wc/entries-compat.lo: subversion/tests/libsvn_wc/entries-compat.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/svn_private_config.h subversion/tests/libsvn_wc/utils.h subversion/tests/svn_test.h
subversion/tests/libsvn_wc/op-depth-test.lo: subversion/tests/libsvn_wc/op-depth-test.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/conflicts.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/libsvn_wc/wc_db_private.h subversion/libsvn_wc/workqueue.h subversion/svn_private_config.h subversion/tests/libsvn_wc/utils.h subversion/tests/svn_test.h
subversion/tests/libsvn_wc/pristine-store-test.lo: subversion/tests/libsvn_wc/pristine-store-test.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/wc-queries.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/libsvn_wc/workqueue.h subversion/svn_private_config.h subversion/tests/libsvn_wc/utils.h subversion/tests/svn_test.h
subversion/tests/libsvn_wc/utils.lo: subversion/tests/libsvn_wc/utils.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/wc-queries.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/libsvn_wc/wc_db_private.h subversion/svn_private_config.h subversion/tests/libsvn_wc/utils.h subversion/tests/svn_test.h subversion/tests/svn_test_fs.h
subversion/tests/libsvn_wc/utils.lo: subversion/tests/libsvn_wc/utils.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/wc-queries.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/libsvn_wc/wc_db_private.h subversion/svn_private_config.h subversion/tests/libsvn_wc/utils.h subversion/tests/svn_test.h subversion/tests/svn_test_fs.h
subversion/tests/libsvn_wc/utils.lo: subversion/tests/libsvn_wc/utils.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/wc-queries.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/libsvn_wc/wc_db_private.h subversion/svn_private_config.h subversion/tests/libsvn_wc/utils.h subversion/tests/svn_test.h subversion/tests/svn_test_fs.h
subversion/tests/libsvn_wc/utils.lo: subversion/tests/libsvn_wc/utils.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/wc-queries.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/libsvn_wc/wc_db_private.h subversion/svn_private_config.h subversion/tests/libsvn_wc/utils.h subversion/tests/svn_test.h subversion/tests/svn_test_fs.h
subversion/tests/libsvn_wc/utils.lo: subversion/tests/libsvn_wc/utils.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/wc-queries.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/libsvn_wc/wc_db_private.h subversion/svn_private_config.h subversion/tests/libsvn_wc/utils.h subversion/tests/svn_test.h subversion/tests/svn_test_fs.h
subversion/tests/libsvn_wc/utils.lo: subversion/tests/libsvn_wc/utils.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/wc-queries.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/libsvn_wc/wc_db_private.h subversion/svn_private_config.h subversion/tests/libsvn_wc/utils.h subversion/tests/svn_test.h subversion/tests/svn_test_fs.h
subversion/tests/libsvn_wc/wc-incomplete-tester.lo: subversion/tests/libsvn_wc/wc-incomplete-tester.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/svn_private_config.h
subversion/tests/libsvn_wc/wc-lock-tester.lo: subversion/tests/libsvn_wc/wc-lock-tester.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/svn_private_config.h
subversion/tests/libsvn_wc/wc-queries-test.lo: subversion/tests/libsvn_wc/wc-queries-test.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/svn_checksum.h subversion/include/svn_ctype.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_wc/wc-queries.h subversion/svn_private_config.h subversion/tests/svn_test.h
subversion/tests/libsvn_wc/wc-test.lo: subversion/tests/libsvn_wc/wc-test.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_skel.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/libsvn_wc/wc_db_private.h subversion/svn_private_config.h subversion/tests/libsvn_wc/utils.h subversion/tests/svn_test.h
subversion/tests/svn_test_fs.lo: subversion/tests/svn_test_fs.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/tests/svn_test.h subversion/tests/svn_test_fs.h
subversion/tests/svn_test_main.lo: subversion/tests/svn_test_main.c subversion/include/private/svn_cmdline_private.h subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_ctype.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/svn_private_config.h subversion/tests/svn_test.h
tools/client-side/svn-bench/help-cmd.lo: tools/client-side/svn-bench/help-cmd.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/include/svn_wc.h subversion/svn_private_config.h tools/client-side/svn-bench/cl.h
tools/client-side/svn-bench/notify.lo: tools/client-side/svn-bench/notify.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn_private_config.h tools/client-side/svn-bench/cl.h
tools/client-side/svn-bench/null-export-cmd.lo: tools/client-side/svn-bench/null-export-cmd.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_string_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn_private_config.h tools/client-side/svn-bench/cl.h
tools/client-side/svn-bench/null-list-cmd.lo: tools/client-side/svn-bench/null-list-cmd.c subversion/include/private/svn_debug.h subversion/include/private/svn_string_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_wc.h subversion/include/svn_xml.h subversion/svn_private_config.h tools/client-side/svn-bench/cl.h
tools/client-side/svn-bench/null-log-cmd.lo: tools/client-side/svn-bench/null-log-cmd.c subversion/include/private/svn_debug.h subversion/include/private/svn_string_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_compat.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_props.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn_private_config.h tools/client-side/svn-bench/cl.h
tools/client-side/svn-bench/svn-bench.lo: tools/client-side/svn-bench/svn-bench.c subversion/include/private/svn_cmdline_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_opt_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_version.h subversion/include/svn_wc.h subversion/svn_private_config.h tools/client-side/svn-bench/cl.h
tools/client-side/svn-bench/util.lo: tools/client-side/svn-bench/util.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_client.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn_private_config.h tools/client-side/svn-bench/cl.h
tools/dev/fsfs-access-map.lo: tools/dev/fsfs-access-map.c subversion/include/private/svn_debug.h subversion/include/private/svn_string_private.h subversion/include/svn_checksum.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h
tools/dev/fsfs-reorg.lo: tools/dev/fsfs-reorg.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_string_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_checksum.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_version.h
tools/dev/svnraisetreeconflict/svnraisetreeconflict.lo: tools/dev/svnraisetreeconflict/svnraisetreeconflict.c subversion/include/private/svn_cmdline_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.h subversion/include/private/svn_wc_private.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_ra.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_version.h subversion/include/svn_wc.h subversion/svn_private_config.h
tools/diff/diff.lo: tools/diff/diff.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h
tools/diff/diff3.lo: tools/diff/diff3.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h
tools/diff/diff4.lo: tools/diff/diff4.c subversion/include/private/svn_debug.h subversion/include/svn_checksum.h subversion/include/svn_diff.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h
tools/server-side/fsfs-stats.lo: tools/server-side/fsfs-stats.c subversion/include/private/svn_cache.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_string_private.h subversion/include/private/svn_subr_private.h subversion/include/svn_cache_config.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_diff.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_hash.h subversion/include/svn_io.h subversion/include/svn_iter.h subversion/include/svn_pools.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_version.h
tools/server-side/mod_dontdothat/mod_dontdothat.lo: tools/server-side/mod_dontdothat/mod_dontdothat.c subversion/include/mod_dav_svn.h subversion/include/private/svn_debug.h subversion/include/private/svn_fspath.h subversion/include/svn_checksum.h subversion/include/svn_config.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_io.h subversion/include/svn_path.h subversion/include/svn_string.h subversion/include/svn_types.h
if $(INSTALL_APACHE_MODS) ; then $(COMPILE_APACHE_MOD) $(canonicalized_srcdir)tools/server-side/mod_dontdothat/mod_dontdothat.c ; else echo "fake" > tools/server-side/mod_dontdothat/mod_dontdothat.lo ; fi
tools/server-side/svn-populate-node-origins-index.lo: tools/server-side/svn-populate-node-origins-index.c subversion/include/private/svn_debug.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h
tools/server-side/svn-rep-sharing-stats.lo: tools/server-side/svn-rep-sharing-stats.c subversion/include/private/svn_atomic.h subversion/include/private/svn_cache.h subversion/include/private/svn_cmdline_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_editor.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_mutex.h subversion/include/private/svn_named_atomic.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_token.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_iter.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_pools.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_version.h subversion/libsvn_fs_fs/fs.h subversion/libsvn_fs_fs/fs_fs.h subversion/libsvn_fs_fs/id.h subversion/svn_private_config.h
tools/server-side/svnauthz.lo: tools/server-side/svnauthz.c subversion/include/private/svn_cmdline_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_fspath.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h
tools/server-side/svnauthz.lo: tools/server-side/svnauthz.c subversion/include/private/svn_cmdline_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_fspath.h subversion/include/svn_auth.h subversion/include/svn_checksum.h subversion/include/svn_cmdline.h subversion/include/svn_config.h subversion/include/svn_delta.h subversion/include/svn_dirent_uri.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_fs.h subversion/include/svn_io.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.h subversion/include/svn_path.h subversion/include/svn_pools.h subversion/include/svn_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.h
Index: vendor/subversion/dist/build.conf
===================================================================
--- vendor/subversion/dist/build.conf (revision 286500)
+++ vendor/subversion/dist/build.conf (revision 286501)
@@ -1,1378 +1,1379 @@
#
# build.conf -- configuration information for building Subversion
#
######################################################################
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
######################################################################
#
# This file is processed by gen-make.py, creating build-outputs.mk
#
# ----------------------------------------------------------------------------
#
# PREDEFINED SECTION
#
# This [options] section is global in scope, providing information to the
# process, rather than defining a build target, as all other sections do.
#
[options]
includes = subversion/include/*.h
include-wildcards = *.h *.i *.swg
private-includes =
subversion/include/private/*.h
subversion/bindings/swig/include/*.swg
subversion/libsvn_delta/compose_delta.c
subversion/bindings/cxxhl/include/*.hpp
subversion/bindings/cxxhl/include/svncxxhl/*.hpp
private-built-includes =
subversion/svn_private_config.h
subversion/libsvn_fs_fs/rep-cache-db.h
subversion/libsvn_wc/wc-metadata.h
subversion/libsvn_wc/wc-queries.h
subversion/libsvn_wc/wc-checks.h
subversion/libsvn_subr/internal_statements.h
subversion/bindings/swig/proxy/swig_python_external_runtime.swg
subversion/bindings/swig/proxy/swig_perl_external_runtime.swg
subversion/bindings/swig/proxy/swig_ruby_external_runtime.swg
subversion/bindings/swig/proxy/rubyhead.swg
subversion/bindings/javahl/include/org_apache_subversion_javahl_CommitItemStateFlags.h
subversion/bindings/javahl/include/org_apache_subversion_javahl_NativeResources.h
subversion/bindings/javahl/include/org_apache_subversion_javahl_Path.h
subversion/bindings/javahl/include/org_apache_subversion_javahl_SVNRepos.h
subversion/bindings/javahl/include/org_apache_subversion_javahl_SVNClient.h
subversion/bindings/javahl/include/org_apache_subversion_javahl_types_Version.h
subversion/bindings/javahl/include/org_apache_subversion_javahl_types_VersionExtended.h
subversion/bindings/javahl/include/org_apache_subversion_javahl_types_VersionExtended_LinkedLib.h
subversion/bindings/javahl/include/org_apache_subversion_javahl_types_VersionExtended_LinkedLibIterator.h
subversion/bindings/javahl/include/org_apache_subversion_javahl_types_VersionExtended_LoadedLib.h
subversion/bindings/javahl/include/org_apache_subversion_javahl_types_VersionExtended_LoadedLibIterator.h
subversion/bindings/javahl/include/org_apache_subversion_javahl_types_Revision.h
subversion/bindings/javahl/include/org_apache_subversion_javahl_callback_UserPasswordCallback.h
test-scripts =
subversion/tests/cmdline/*_tests.py
bdb-test-scripts =
swig-python-opts = $(SWIG_CPPFLAGS) -python -classic
swig-perl-opts = $(SWIG_CPPFLAGS) -perl -nopm -noproxy
swig-ruby-opts = $(SWIG_CPPFLAGS) -ruby
swig-languages = python perl ruby
swig-dirs =
subversion/bindings/swig/python
subversion/bindings/swig/perl
subversion/bindings/swig/ruby
subversion/bindings/swig/proxy
swig-proxy-dir = subversion/bindings/swig/proxy
swig-checkout-files = common.swg swigrun.swg runtime.swg
ruby/rubydef.swg ruby/rubyhead.swg ruby/rubytracking.swg
perl5/perlrun.swg python/pyrun.swg python/python.swg
# ----------------------------------------------------------------------------
#
# BUILD TARGETS
#
# Target parameters:
# description - optional build target description
# type - the target type, defines how to build it
# when - the name of an autoconf-substed variable that muset be
# defined to either "true" or "false", that determines
# whether this target should be built and installed.
# path - relative path to target sources
# sources - explicit list of target sources
# install - the installation group/type
# manpages - the man pages associated with this target
# libs - libraries that this target depends on
# nonlibs - dependencies that are not linked into the target
# lang - bindings for language $(lang)
# msvc-libs - additional libraries to link with on Windows
# msvc-export - additional list of files to expose in dsp/vc(x)proj
# msvc-static - visual studio target produces only a static lib
# add-deps - expands to additional autoconf-defined dependencies
# add-install-deps - like add-deps, but for the install step
# external-lib - expands to additional autoconf-defined libs
# external-project - visual studio project to depend on
#
# The subversion command-line client
[svn]
description = Subversion Client
type = exe
path = subversion/svn
libs = libsvn_client libsvn_wc libsvn_ra libsvn_delta libsvn_diff libsvn_subr
apriconv apr
manpages = subversion/svn/svn.1
install = bin
# The subversion repository administration tool
[svnadmin]
description = Subversion Repository Administrator
type = exe
path = subversion/svnadmin
install = bin
manpages = subversion/svnadmin/svnadmin.1
libs = libsvn_repos libsvn_fs libsvn_delta libsvn_subr apriconv apr
# The subversion repository dump filtering tool
[svndumpfilter]
description = Subversion Dumpfile Filter
type = exe
path = subversion/svndumpfilter
install = bin
manpages = subversion/svndumpfilter/svndumpfilter.1
libs = libsvn_repos libsvn_fs libsvn_delta libsvn_subr apriconv apr
# The subversion repository inspection tool
[svnlook]
description = Subversion Repository Browser
type = exe
path = subversion/svnlook
install = bin
manpages = subversion/svnlook/svnlook.1
libs = libsvn_repos libsvn_fs libsvn_delta libsvn_diff libsvn_subr apriconv apr
[svnserve]
description = Subversion Server
type = exe
path = subversion/svnserve
install = bin
manpages = subversion/svnserve/svnserve.8 subversion/svnserve/svnserve.conf.5
libs = libsvn_repos libsvn_fs libsvn_delta libsvn_subr libsvn_ra_svn
apriconv apr sasl
msvc-libs = advapi32.lib ws2_32.lib
[svnsync]
description = Subversion repository replicator
type = exe
path = subversion/svnsync
libs = libsvn_ra libsvn_delta libsvn_subr apr
install = bin
manpages = subversion/svnsync/svnsync.1
[svnversion]
description = Subversion Revision Extractor
type = exe
path = subversion/svnversion
libs = libsvn_wc libsvn_subr apriconv apr
install = bin
manpages = subversion/svnversion/svnversion.1
[svnrdump]
description = Subversion remote repository dumper and loader
type = exe
path = subversion/svnrdump
libs = libsvn_client libsvn_ra libsvn_repos libsvn_delta libsvn_subr aprutil apr
install = bin
manpages = subversion/svnrdump/svnrdump.1
[svnmucc]
description = Subversion Multiple URL Command Client
type = exe
path = subversion/svnmucc
libs = libsvn_client libsvn_ra libsvn_subr libsvn_delta apriconv apr
install = bin
manpages = subversion/svnmucc/svnmucc.1
# Support for GNOME Keyring
[libsvn_auth_gnome_keyring]
description = Subversion GNOME Keyring Library
type = lib
install = gnome-keyring-lib
path = subversion/libsvn_auth_gnome_keyring
libs = libsvn_subr apr gnome-keyring
# Support for KWallet
[libsvn_auth_kwallet]
description = Subversion KWallet Library
type = lib
install = kwallet-lib
path = subversion/libsvn_auth_kwallet
libs = libsvn_subr apr kwallet
link-cmd = $(LINK_CXX_LIB)
# Library needed by all subversion clients
[libsvn_client]
description = Subversion Client Library
type = lib
path = subversion/libsvn_client
libs = libsvn_wc libsvn_ra libsvn_delta libsvn_diff libsvn_subr apriconv apr
install = lib
msvc-export = svn_client.h private/svn_client_private.h
# Routines for binary diffing and tree-deltas
[libsvn_delta]
description = Subversion Delta Library
type = lib
install = fsmod-lib
path = subversion/libsvn_delta
libs = libsvn_subr aprutil apriconv apr zlib
msvc-export = svn_delta.h private/svn_editor.h private/svn_delta_private.h
# Routines for diffing
[libsvn_diff]
description = Subversion Diff Library
type = lib
path = subversion/libsvn_diff
libs = libsvn_subr apriconv apr zlib
install = lib
msvc-export = svn_diff.h private/svn_diff_private.h private/svn_diff_tree.h
# The repository filesystem library
[libsvn_fs]
description = Subversion Repository Filesystem Library
type = lib
path = subversion/libsvn_fs
install = ramod-lib
libs = libsvn_fs_util libsvn_delta libsvn_subr fs-libs aprutil apr
# conditionally add more dependencies
add-deps = $(SVN_FS_LIB_DEPS)
add-install-deps = $(SVN_FS_LIB_INSTALL_DEPS)
msvc-export = svn_fs.h private/svn_fs_private.h
[libsvn_fs_base]
type = fs-module
path = subversion/libsvn_fs_base
sources = *.c bdb/*.c util/*.c
install = bdb-lib
libs = libsvn_delta libsvn_subr aprutil apriconv apr bdb libsvn_fs_util
msvc-static = yes
[libsvn_fs_fs]
type = fs-module
path = subversion/libsvn_fs_fs
install = fsmod-lib
libs = libsvn_delta libsvn_subr aprutil apriconv apr libsvn_fs_util
msvc-static = yes
# Low-level grab bag of utilities
[libsvn_fs_util]
type = lib
install = fsmod-lib
path = subversion/libsvn_fs_util
libs = libsvn_subr aprutil apriconv apr
msvc-libs = advapi32.lib shfolder.lib
msvc-static = yes
# General API for accessing repositories
[libsvn_ra]
description = Subversion Repository Access Library
type = lib
path = subversion/libsvn_ra
libs = libsvn_delta libsvn_subr ra-libs apriconv apr
# conditionally add more dependencies
add-deps = $(SVN_RA_LIB_DEPS)
add-install-deps = $(SVN_RA_LIB_INSTALL_DEPS)
install = lib
msvc-export = svn_ra.h private\svn_ra_private.h
# Accessing repositories via DAV through serf
[libsvn_ra_serf]
type = ra-module
path = subversion/libsvn_ra_serf
install = serf-lib
libs = libsvn_delta libsvn_subr aprutil apriconv apr serf xml
msvc-libs = secur32.lib
msvc-static = yes
# Accessing repositories via SVN
[libsvn_ra_svn]
type = ra-module
path = subversion/libsvn_ra_svn
install = ramod-lib
libs = libsvn_delta libsvn_subr aprutil apriconv apr sasl
msvc-static = yes
# Accessing repositories via direct libsvn_fs
[libsvn_ra_local]
type = ra-module
path = subversion/libsvn_ra_local
install = ramod-lib
libs = libsvn_repos libsvn_fs libsvn_delta libsvn_subr apriconv apr
msvc-static = yes
# Routines built on top of libsvn_fs
[libsvn_repos]
description = Subversion Repository Library
type = lib
path = subversion/libsvn_repos
install = ramod-lib
libs = libsvn_fs libsvn_delta libsvn_subr apriconv apr
msvc-export = svn_repos.h private/svn_repos_private.h
# Low-level grab bag of utilities
[libsvn_subr]
description = Subversion General Utility Library
type = lib
install = fsmod-lib
path = subversion/libsvn_subr
libs = aprutil apriconv apr xml zlib apr_memcache sqlite magic
msvc-libs = kernel32.lib advapi32.lib shfolder.lib ole32.lib
crypt32.lib version.lib
msvc-export =
svn_auth.h svn_base64.h svn_cache_config.h svn_checksum.h svn_cmdline.h
svn_compat.h svn_config.h svn_ctype.h svn_dirent_uri.h svn_dso.h
svn_error.h svn_hash.h svn_io.h svn_iter.h svn_md5.h svn_mergeinfo.h
svn_nls.h svn_opt.h svn_path.h svn_pools.h svn_props.h svn_quoprint.h
svn_sorts.h svn_string.h svn_subst.h svn_time.h svn_types.h svn_user.h
svn_utf.h svn_version.h svn_xml.h
private\svn_atomic.h private\svn_cache.h private\svn_cmdline_private.h
private\svn_debug.h private\svn_error_private.h private\svn_fspath.h
private\svn_log.h private\svn_mergeinfo_private.h
private\svn_opt_private.h private\svn_skel.h private\svn_sqlite.h
private\svn_utf_private.h private\svn_eol_private.h
private\svn_token.h private\svn_adler32.h
private\svn_temp_serializer.h private\svn_io_private.h
private\svn_string_private.h private\svn_magic.h
private\svn_subr_private.h private\svn_mutex.h private\svn_named_atomic.h
private\svn_cert.h
# Working copy management lib
[libsvn_wc]
description = Subversion Working Copy Library
type = lib
path = subversion/libsvn_wc
libs = libsvn_delta libsvn_diff libsvn_subr aprutil apriconv apr
install = lib
msvc-export = svn_wc.h private\svn_wc_private.h
# Subversion plugin for Apache's mod_dav
[mod_dav_svn]
description = Subversion plug-in for the Apache DAV module
when = INSTALL_APACHE_MODS
type = apache-mod
path = subversion/mod_dav_svn
sources = *.c reports/*.c posts/*.c
libs = libsvn_repos libsvn_fs libsvn_delta libsvn_subr
nonlibs = apr aprutil
install = apache-mod
msvc-libs = mod_dav.lib libhttpd.lib
[mod_authz_svn]
description = Subversion path-based authorization module for Apache
when = INSTALL_APACHE_MODS
type = apache-mod
path = subversion/mod_authz_svn
nonlibs = mod_dav_svn apr aprutil
libs = libsvn_repos libsvn_subr
install = apache-mod
msvc-libs = libhttpd.lib
[mod_dontdothat]
description = Apache Httpd module to block certain kinds of Apache Subversion requests
when = INSTALL_APACHE_MODS
type = apache-mod
path = tools/server-side/mod_dontdothat
nonlibs = mod_dav_svn apr aprutil
libs = libsvn_subr xml
install = tools
msvc-libs = libhttpd.lib
# ----------------------------------------------------------------------------
#
# CONSTRUCTED HEADERS
#
[rep_cache]
description = Schema for the rep-sharing feature
type = sql-header
path = subversion/libsvn_fs_fs
sources = rep-cache-db.sql
[wc_queries]
desription = Queries on the WC database
type = sql-header
path = subversion/libsvn_wc
sources = wc-queries.sql
[subr_sqlite]
description = Internal statements for SQLite interface
type = sql-header
path = subversion/libsvn_subr
sources = internal_statements.sql
# ----------------------------------------------------------------------------
#
# TARGETS FOR I18N SUPPORT
#
[locale]
type = i18n
path = subversion/po
install = locale
external-project = svn_locale
# ----------------------------------------------------------------------------
#
# TARGETS FOR SWIG SUPPORT
#
[swig_core]
type = swig
path = subversion/bindings/swig
sources = core.i
libs = libsvn_swig_py libsvn_swig_perl libsvn_swig_ruby
libsvn_diff libsvn_subr apr
description = Subversion core library bindings
include-runtime = yes
[swig_client]
type = swig
path = subversion/bindings/swig
sources = svn_client.i
libs = libsvn_swig_py libsvn_swig_perl libsvn_swig_ruby
libsvn_client libsvn_subr apr
nonlibs = swig_core
description = Subversion client library bindings
[swig_delta]
type = swig
path = subversion/bindings/swig
sources = svn_delta.i
libs = libsvn_swig_py libsvn_swig_perl libsvn_swig_ruby
libsvn_delta libsvn_subr apr
nonlibs = swig_core
description = Subversion delta library bindings
[swig_diff]
type = swig
path = subversion/bindings/swig
sources = svn_diff.i
libs = libsvn_swig_py libsvn_swig_perl libsvn_swig_ruby
libsvn_diff libsvn_subr apr
nonlibs = swig_core
description = Subversion diff library bindings
[swig_fs]
type = swig
path = subversion/bindings/swig
sources = svn_fs.i
libs = libsvn_swig_py libsvn_swig_perl libsvn_swig_ruby
libsvn_fs libsvn_subr apr
nonlibs = swig_core
description = Subversion FS library bindings
[swig_ra]
type = swig
path = subversion/bindings/swig
sources = svn_ra.i
libs = libsvn_swig_py libsvn_swig_perl libsvn_swig_ruby
libsvn_ra libsvn_subr apr
nonlibs = swig_core
description = Subversion RA library bindings
[swig_repos]
type = swig
path = subversion/bindings/swig
sources = svn_repos.i
libs = libsvn_swig_py libsvn_swig_perl libsvn_swig_ruby
libsvn_repos libsvn_subr apr
nonlibs = swig_core
description = Subversion repository library bindings
[swig_wc]
type = swig
path = subversion/bindings/swig
sources = svn_wc.i
libs = libsvn_swig_py libsvn_swig_perl libsvn_swig_ruby
libsvn_wc libsvn_subr apr
nonlibs = swig_core
description = Subversion WC library bindings
# SWIG utility library for Python modules
[libsvn_swig_py]
type = swig_lib
lang = python
path = subversion/bindings/swig/python/libsvn_swig_py
libs = libsvn_client libsvn_wc libsvn_ra libsvn_delta libsvn_subr apriconv apr
link-cmd = $(LINK)
install = swig-py-lib
# need special build rule to include -DSWIGPYTHON
compile-cmd = $(COMPILE_SWIG_PY)
msvc-static = no
# SWIG utility library for Perl modules
[libsvn_swig_perl]
type = swig_lib
lang = perl
path = subversion/bindings/swig/perl/libsvn_swig_perl
libs = libsvn_delta libsvn_subr apriconv apr
install = swig-pl-lib
# need special build rule to include
compile-cmd = $(COMPILE_SWIG_PL)
msvc-static = yes
# SWIG utility library for Ruby modules
[libsvn_swig_ruby]
type = swig_lib
lang = ruby
path = subversion/bindings/swig/ruby/libsvn_swig_ruby
libs = libsvn_client libsvn_wc libsvn_delta libsvn_subr apriconv apr
link-cmd = $(LINK) $(SWIG_RB_LIBS)
install = swig-rb-lib
# need special build rule to include
compile-cmd = $(COMPILE_SWIG_RB)
msvc-static = no
# ----------------------------------------------------------------------------
#
# JavaHL targets
#
[javahl-java]
type = java
path = subversion/bindings/javahl/src/org/apache/subversion/javahl
subversion/bindings/javahl/src/org/apache/subversion/javahl/callback
subversion/bindings/javahl/src/org/apache/subversion/javahl/types
src-root = subversion/bindings/javahl/src
sources = *.java
install = javahl-java
link-cmd = $(COMPILE_JAVAHL_JAVAC)
classes = subversion/bindings/javahl/classes
package-roots = org
[javahl-compat-java]
type = java
path = subversion/bindings/javahl/src/org/tigris/subversion/javahl
sources = *.java
install = javahl-java
link-cmd = $(COMPILE_JAVAHL_JAVAC)
classes = subversion/bindings/javahl/classes
add-deps = $(javahl_java_DEPS)
### Replace JAR call in INSTALL_EXTRA_JAVAHL_JAVA macro Makefile.in.
#jar = svn-javahl.jar
package-roots = org
[javahl-tests]
type = java
path = subversion/bindings/javahl/tests/org/apache/subversion/javahl
sources = *.java
install = javahl-java
link-cmd = $(COMPILE_JAVAHL_JAVAC)
classes = subversion/bindings/javahl/classes
package-roots = org
### Java targets don't do up-to-date checks yet.
#add-deps = javahl-java
add-deps = $(javahl_java_DEPS)
[javahl-compat-tests]
type = java
path = subversion/bindings/javahl/tests/org/tigris/subversion/javahl
sources = *.java
install = javahl-java
link-cmd = $(COMPILE_JAVAHL_JAVAC)
classes = subversion/bindings/javahl/classes
package-roots = org
### Java targets don't do up-to-date checks yet.
#add-deps = javahl-compat-java
add-deps = $(javahl_compat_java_DEPS)
[javahl-types-javah]
type = javah
path = subversion/bindings/javahl/src/org/apache/subversion/javahl/types
classes = subversion/bindings/javahl/classes
headers = subversion/bindings/javahl/include
package = org.apache.subversion.javahl.types
sources = *.java
add-deps = $(javahl_java_DEPS)
install = javahl-javah
link-cmd = $(COMPILE_JAVAHL_JAVAH) -force
[javahl-callback-javah]
type = javah
path = subversion/bindings/javahl/src/org/apache/subversion/javahl/callback
classes = subversion/bindings/javahl/classes
headers = subversion/bindings/javahl/include
package = org.apache.subversion.javahl.callback
sources = *.java
add-deps = $(javahl_java_DEPS)
install = javahl-javah
link-cmd = $(COMPILE_JAVAHL_JAVAH) -force
[javahl-javah]
type = javah
path = subversion/bindings/javahl/src/org/apache/subversion/javahl
classes = subversion/bindings/javahl/classes
headers = subversion/bindings/javahl/include
package = org.apache.subversion.javahl
sources = *.java
add-deps = $(javahl_java_DEPS)
install = javahl-javah
link-cmd = $(COMPILE_JAVAHL_JAVAH) -force
[libsvnjavahl]
description = Subversion Java HighLevel binding
type = lib
path = subversion/bindings/javahl/native
libs = libsvn_repos libsvn_client libsvn_wc libsvn_ra libsvn_delta libsvn_diff
libsvn_subr libsvn_fs aprutil apriconv apr
sources = *.cpp *.c
add-deps = $(javahl_javah_DEPS) $(javahl_java_DEPS) $(javahl_callback_javah_DEPS) $(javahl_types_javah_DEPS)
install = javahl-lib
# need special build rule to include -I$(JDK)/include/jni.h
compile-cmd = $(COMPILE_JAVAHL_CXX)
link-cmd = $(LINK_JAVAHL_CXX)
# ----------------------------------------------------------------------------
#
# C++HL targets
#
[libsvncxxhl]
description = Subversion C++ HighLevel bindings
type = lib
path = subversion/bindings/cxxhl
libs = libsvn_repos libsvn_client libsvn_wc libsvn_ra libsvn_delta libsvn_diff
libsvn_subr libsvn_fs aprutil apriconv apr
sources = src/*.cpp
install = cxxhl-lib
msvc-static = yes
compile-cmd = $(COMPILE_CXXHL_CXX)
link-cmd = $(LINK_CXX_LIB)
[cxxhl-tests]
description = Unit tests for Subversion C++ HighLevel bindings
type = exe
path = subversion/bindings/cxxhl
libs = libsvncxxhl libsvn_subr
sources = tests/*.cpp
install = tests
compile-cmd = $(COMPILE_CXXHL_CXX)
link-cmd = $(LINK_CXX)
# ----------------------------------------------------------------------------
#
# TESTING TARGETS
#
# general library: our C testing framework
[libsvn_test]
type = lib
path = subversion/tests
install = test
libs = libsvn_repos libsvn_fs libsvn_delta libsvn_subr aprutil apriconv apr
msvc-static = yes
undefined-lib-symbols = yes
# ----------------------------------------------------------------------------
# Tests for libsvn_fs_base
[fs-base-test]
description = Tests for *public* fs API (svn_fs.h)
type = exe
path = subversion/tests/libsvn_fs_base
sources = fs-base-test.c
install = bdb-test
libs = libsvn_test libsvn_fs libsvn_fs_base libsvn_delta
libsvn_fs_util libsvn_subr apriconv apr
[strings-reps-test]
description = Test strings/reps in libsvn_fs_base
type = exe
path = subversion/tests/libsvn_fs_base
sources = strings-reps-test.c
install = bdb-test
libs = libsvn_test libsvn_fs libsvn_fs_base libsvn_delta
libsvn_subr apriconv apr
[changes-test]
description = Test changes in libsvn_fs_base
type = exe
path = subversion/tests/libsvn_fs_base
sources = changes-test.c
install = bdb-test
libs = libsvn_test libsvn_fs libsvn_fs_base libsvn_delta
libsvn_subr apriconv apr
# ----------------------------------------------------------------------------
# Tests for libsvn_fs_fs
[fs-pack-test]
description = Test fsfs packing in libsvn_fs_fs
type = exe
path = subversion/tests/libsvn_fs_fs
sources = fs-pack-test.c
install = test
libs = libsvn_test libsvn_fs libsvn_fs_fs libsvn_delta
libsvn_subr apriconv apr
# ----------------------------------------------------------------------------
# Tests for libsvn_fs
[locks-test]
description = Test locks in libsvn_fs
type = exe
path = subversion/tests/libsvn_fs
sources = locks-test.c
install = test
libs = libsvn_test libsvn_fs libsvn_delta libsvn_subr apriconv apr
[fs-test]
description = Test locks in libsvn_fs
type = exe
path = subversion/tests/libsvn_fs
sources = fs-test.c
install = test
libs = libsvn_test libsvn_fs libsvn_delta
libsvn_subr aprutil apriconv apr
# ----------------------------------------------------------------------------
# Tests for libsvn_repos
[repos-test]
description = Test delta editor in libsvn_repos
type = exe
path = subversion/tests/libsvn_repos
sources = repos-test.c dir-delta-editor.c
install = test
libs = libsvn_test libsvn_repos libsvn_fs libsvn_delta libsvn_subr apriconv apr
# ----------------------------------------------------------------------------
# Tests for libsvn_subr
[auth-test]
description = Test platform-specific auth provider access
type = exe
path = subversion/tests/libsvn_subr
sources = auth-test.c
install = test
libs = libsvn_test libsvn_subr apr
[cache-test]
description = Test in-memory cache
type = exe
path = subversion/tests/libsvn_subr
sources = cache-test.c
install = test
libs = libsvn_test libsvn_subr apr
[checksum-test]
description = Test checksum functions
type = exe
path = subversion/tests/libsvn_subr
sources = checksum-test.c
install = test
libs = libsvn_test libsvn_subr apr zlib
[compat-test]
description = Test compatibility functions
type = exe
path = subversion/tests/libsvn_subr
sources = compat-test.c
install = test
libs = libsvn_test libsvn_subr apr
[config-test]
description = Test svn_config utilities
type = exe
path = subversion/tests/libsvn_subr
sources = config-test.c
install = test
libs = libsvn_test libsvn_subr apriconv apr
[crypto-test]
description = Test svn_crypto utilities
type = exe
path = subversion/tests/libsvn_subr
sources = crypto-test.c
install = test
libs = libsvn_test libsvn_subr aprutil apr
[dirent_uri-test]
description = Test dirent_uri library
type = exe
path = subversion/tests/libsvn_subr
sources = dirent_uri-test.c
install = test
libs = libsvn_test libsvn_subr apriconv apr
[error-test]
description = Test error library
type = exe
path = subversion/tests/libsvn_subr
sources = error-test.c
install = test
libs = libsvn_test libsvn_subr apriconv apr
[error-code-test]
description = Test error library
type = exe
path = subversion/tests/libsvn_subr
sources = error-code-test.c
install = test
libs = libsvn_test libsvn_subr apriconv apr
[hashdump-test]
description = Test hashfile format for props
type = exe
path = subversion/tests/libsvn_subr
sources = hashdump-test.c
install = test
libs = libsvn_test libsvn_subr apriconv apr
[io-test]
description = Test I/O Operations
type = exe
path = subversion/tests/libsvn_subr
sources = io-test.c
install = test
libs = libsvn_test libsvn_subr apriconv apr
[opt-test]
description = Test options library
type = exe
path = subversion/tests/libsvn_subr
sources = opt-test.c
install = test
libs = libsvn_test libsvn_subr apr
[mergeinfo-test]
description = Test mergeinfo library
type = exe
path = subversion/tests/libsvn_subr
sources = mergeinfo-test.c
install = test
libs = libsvn_test libsvn_subr apr
[named_atomic-test]
description = Test named atomics
type = exe
path = subversion/tests/libsvn_subr
sources = named_atomic-test.c
install = test
libs = libsvn_test libsvn_subr apr
[named_atomic-proc-test]
description = Sub-process for named atomics
type = exe
path = subversion/tests/libsvn_subr
sources = named_atomic-test-proc.c
install = sub-test
libs = libsvn_subr apr
[path-test]
description = Test path library
type = exe
path = subversion/tests/libsvn_subr
sources = path-test.c
install = test
libs = libsvn_test libsvn_subr apriconv apr
[revision-test]
description = Test revision library
type = exe
path = subversion/tests/libsvn_subr
sources = revision-test.c
install = test
libs = libsvn_test libsvn_subr apr
[skel-test]
description = Test skels in libsvn_subr
type = exe
path = subversion/tests/libsvn_subr
sources = skel-test.c
install = test
libs = libsvn_test libsvn_subr apriconv apr
[spillbuf-test]
description = Test spillbuf in libsvn_subr
type = exe
path = subversion/tests/libsvn_subr
sources = spillbuf-test.c
install = test
libs = libsvn_test libsvn_subr apriconv apr
[stream-test]
description = Test stream library
type = exe
path = subversion/tests/libsvn_subr
sources = stream-test.c
install = test
libs = libsvn_test libsvn_subr apriconv apr
[string-test]
description = Test svn_stringbuf_t utilities
type = exe
path = subversion/tests/libsvn_subr
sources = string-test.c
install = test
libs = libsvn_test libsvn_subr apriconv apr
[time-test]
description = Test time functions
type = exe
path = subversion/tests/libsvn_subr
sources = time-test.c
install = test
libs = libsvn_test libsvn_subr apriconv apr
[utf-test]
description = Test UTF-8 functions
type = exe
path = subversion/tests/libsvn_subr
sources = utf-test.c
install = test
libs = libsvn_test libsvn_subr apriconv apr
[subst_translate-test]
description = Test the svn_subst_translate* functions
type = exe
path = subversion/tests/libsvn_subr
sources = subst_translate-test.c
install = test
libs = libsvn_test libsvn_subr apriconv apr
[translate-test]
description = Test eol conversion and keyword substitution routines
type = exe
path = subversion/tests/libsvn_subr
sources = translate-test.c
install = test
libs = libsvn_test libsvn_subr apriconv apr
# ----------------------------------------------------------------------------
# Tests for libsvn_delta
[random-test]
description = Use random data to test delta processing
type = exe
path = subversion/tests/libsvn_delta
sources = random-test.c
install = test
libs = libsvn_test libsvn_delta libsvn_subr apriconv apr
[window-test]
description = Test delta window generation
type = exe
path = subversion/tests/libsvn_delta
sources = window-test.c
install = test
libs = libsvn_test libsvn_delta libsvn_subr apriconv apr
# ----------------------------------------------------------------------------
# Tests for libsvn_client
[client-test]
description = Test low-level functionality in libsvn_client
type = exe
path = subversion/tests/libsvn_client
sources = client-test.c
install = test
libs = libsvn_test libsvn_client libsvn_wc libsvn_repos libsvn_ra libsvn_fs libsvn_delta libsvn_subr apriconv apr
# ----------------------------------------------------------------------------
# Tests for libsvn_diff
[diff-diff3-test]
description = Test the diff/diff3 library
type = exe
path = subversion/tests/libsvn_diff
sources = diff-diff3-test.c
install = test
libs = libsvn_test libsvn_diff libsvn_subr apriconv apr
[parse-diff-test]
description = Test unidiff parsing
type = exe
path = subversion/tests/libsvn_diff
sources = parse-diff-test.c
install = test
libs = libsvn_test libsvn_diff libsvn_subr apriconv apr
# ----------------------------------------------------------------------------
# Tests for libsvn_ra
[ra-test]
description = Test a few things in libsvn_ra
type = exe
path = subversion/tests/libsvn_ra
sources = ra-test.c
install = test
libs = libsvn_test libsvn_ra libsvn_fs libsvn_delta libsvn_subr
apriconv apr
# ----------------------------------------------------------------------------
# Tests for libsvn_ra_local
[ra-local-test]
description = Test a few things in libsvn_ra_local
type = exe
path = subversion/tests/libsvn_ra_local
sources = ra-local-test.c
install = test
libs = libsvn_test libsvn_ra_local libsvn_ra libsvn_fs libsvn_delta libsvn_subr
apriconv apr
# ----------------------------------------------------------------------------
# Tests for libsvn_wc
[conflict-data-test]
description = Test the storage of tree conflict data
type = exe
path = subversion/tests/libsvn_wc
sources = conflict-data-test.c utils.c
install = test
libs = libsvn_client libsvn_test libsvn_wc libsvn_subr apriconv apr
[db-test]
description = Test the wc-ng database subsystem
type = exe
path = subversion/tests/libsvn_wc
sources = db-test.c utils.c
install = test
libs = libsvn_client libsvn_test libsvn_wc libsvn_subr apriconv apr
[pristine-store-test]
description = Test the wc-ng pristine text storage subsystem
type = exe
path = subversion/tests/libsvn_wc
sources = pristine-store-test.c utils.c
install = test
libs = libsvn_client libsvn_test libsvn_wc libsvn_subr apriconv apr
[entries-compat-test]
description = Test backwards compat for the entry interface
type = exe
path = subversion/tests/libsvn_wc
sources = entries-compat.c utils.c
install = test
libs = libsvn_client libsvn_test libsvn_wc libsvn_subr apriconv apr
[op-depth-test]
description = Test layered tree changes
type = exe
path = subversion/tests/libsvn_wc
sources = op-depth-test.c utils.c
install = test
libs = libsvn_client libsvn_test libsvn_wc libsvn_subr apriconv apr
[wc-queries-test]
description = Test Sqlite query evaluation
type = exe
path = subversion/tests/libsvn_wc
sources = wc-queries-test.c
install = test
libs = libsvn_test libsvn_subr apriconv apr sqlite
[wc-test]
description = Test the main WC API functions
type = exe
path = subversion/tests/libsvn_wc
sources = wc-test.c utils.c
install = test
libs = libsvn_client libsvn_test libsvn_wc libsvn_subr apriconv apr
# ----------------------------------------------------------------------------
# These are not unit tests at all, they are small programs that exercise
# parts of the libsvn_delta API from the command line. They are stuck here
# because of some historical association with the test-suite, but should
# really be put somewhere else.
# test our textdelta encoding
[svndiff-test]
type = exe
path = subversion/tests/libsvn_delta
sources = svndiff-test.c
install = test
libs = libsvn_delta libsvn_subr apriconv apr
testing = skip
# compare two files, print txdelta windows
[vdelta-test]
type = exe
path = subversion/tests/libsvn_delta
sources = vdelta-test.c
install = test
libs = libsvn_delta libsvn_subr apriconv apr
testing = skip
[entries-dump]
type = exe
path = subversion/tests/cmdline
sources = entries-dump.c
install = test
libs = libsvn_wc libsvn_subr apriconv apr
testing = skip
[atomic-ra-revprop-change]
type = exe
path = subversion/tests/cmdline
sources = atomic-ra-revprop-change.c
install = test
libs = libsvn_ra libsvn_subr apriconv apr
testing = skip
[wc-lock-tester]
type = exe
path = subversion/tests/libsvn_wc
sources = wc-lock-tester.c
install = test
libs = libsvn_wc libsvn_subr apriconv apr
testing = skip
[wc-incomplete-tester]
type = exe
path = subversion/tests/libsvn_wc
sources = wc-incomplete-tester.c
install = test
libs = libsvn_wc libsvn_subr apriconv apr
testing = skip
# ----------------------------------------------------------------------------
#
# EXTERNAL TARGETS (NO BUILD NEEDED)
#
[apr]
type = lib
external-lib = $(SVN_APR_LIBS)
msvc-libs = ws2_32.lib rpcrt4.lib mswsock.lib
[aprutil]
type = lib
external-lib = $(SVN_APRUTIL_LIBS)
[apriconv]
type = lib
external-lib = $(SVN_APRUTIL_LIBS)
[bdb]
type = lib
external-lib = $(SVN_DB_LIBS)
[gnome-keyring]
type = lib
external-lib = $(SVN_GNOME_KEYRING_LIBS)
[kwallet]
type = lib
external-lib = $(SVN_KWALLET_LIBS)
[magic]
type = lib
external-lib = $(SVN_MAGIC_LIBS)
[sasl]
type = lib
external-lib = $(SVN_SASL_LIBS)
[zlib]
type = lib
external-lib = $(SVN_ZLIB_LIBS)
external-project = zlib
msvc-static = yes
[apr_memcache]
type = lib
external-lib = $(SVN_APR_MEMCACHE_LIBS)
[serf]
type = lib
external-lib = $(SVN_SERF_LIBS)
external-project = serf/serf
libs = apr aprutil xml
msvc-static = yes
[sqlite]
type = lib
external-lib = $(SVN_SQLITE_LIBS)
[xml]
type = lib
external-lib = $(SVN_XML_LIBS)
[ra-libs]
type = lib
external-lib = $(SVN_RA_LIB_LINK)
libs = libsvn_ra_serf libsvn_ra_local libsvn_ra_svn
[fs-libs]
type = lib
external-lib = $(SVN_FS_LIB_LINK)
libs = libsvn_fs_base libsvn_fs_fs
[__ALL__]
type = project
path = build/win32
libs = svn svnadmin svndumpfilter svnlook svnmucc svnserve svnrdump svnsync
svnversion
mod_authz_svn mod_dav_svn mod_dontdothat
svnauthz svnauthz-validate svnraisetreeconflict
[__ALL_TESTS__]
type = project
path = build/win32
libs = __ALL__
fs-test fs-base-test fs-fsfs-test fs-pack-test skel-test
strings-reps-test changes-test locks-test repos-test
checksum-test compat-test config-test hashdump-test mergeinfo-test
opt-test path-test stream-test string-test time-test utf-test
error-test error-code-test cache-test spillbuf-test crypto-test
named_atomic-test named_atomic-proc-test revision-test
subst_translate-test io-test
translate-test
random-test window-test
diff-diff3-test
ra-test
ra-local-test
svndiff-test vdelta-test
entries-dump atomic-ra-revprop-change wc-lock-tester wc-incomplete-tester
client-test
conflict-data-test db-test pristine-store-test entries-compat-test
op-depth-test dirent_uri-test wc-queries-test wc-test
auth-test
parse-diff-test
[__MORE__]
type = project
path = build/win32
libs = __ALL_TESTS__
diff diff3 diff4 fsfs-reorg fsfs-stats fsfs-access-map svn-bench
svn-rep-sharing-stats svn-populate-node-origins-index
[__LIBS__]
type = project
path = build/win32
libs = fs-libs ra-libs libsvn_client libsvn_subr libsvn_wc
aprutil apriconv apr
[__CONFIG__]
type = lib
external-project = svn_config
[__SWIG_PYTHON__]
type = swig_project
path = build/win32
libs = swig_client swig_delta swig_diff swig_fs swig_ra swig_repos swig_wc swig_core
lang = python
[__SWIG_PERL__]
type = swig_project
path = build/win32
libs = swig_client swig_delta swig_diff swig_fs swig_ra swig_repos swig_wc swig_core
lang = perl
[__SWIG_RUBY__]
type = swig_project
path = build/win32
libs = swig_client swig_delta swig_diff swig_fs swig_ra swig_repos swig_wc swig_core
lang = ruby
[__JAVAHL__]
type = project
path = build/win32
libs = javahl-java javahl-javah libsvnjavahl
[__JAVAHL_TESTS__]
type = project
path = build/win32
libs = __JAVAHL__ javahl-tests javahl-compat-tests
# ----------------------------------------------------------------------------
# Contrib and tools
[fsfs-reorg]
type = exe
path = tools/dev
sources = fsfs-reorg.c
install = tools
libs = libsvn_delta libsvn_subr apr
[fsfs-stats]
type = exe
path = tools/server-side
sources = fsfs-stats.c
install = tools
libs = libsvn_delta libsvn_subr apr
[fsfs-access-map]
type = exe
path = tools/dev
sources = fsfs-access-map.c
install = tools
libs = libsvn_subr apr
[diff]
type = exe
path = tools/diff
sources = diff.c
install = tools
libs = libsvn_diff libsvn_subr apriconv apr
[diff3]
type = exe
path = tools/diff
sources = diff3.c
install = tools
libs = libsvn_diff libsvn_subr apriconv apr
[diff4]
type = exe
path = tools/diff
sources = diff4.c
install = tools
libs = libsvn_diff libsvn_subr apriconv apr
[svn-bench]
type = exe
path = tools/client-side/svn-bench
install = tools
libs = libsvn_client libsvn_wc libsvn_ra libsvn_subr libsvn_delta
apriconv apr
[svnauthz]
description = Authz config file tool
type = exe
path = tools/server-side
sources = svnauthz.c
install = tools
libs = libsvn_repos libsvn_fs libsvn_subr apr
# svnauthz-validate is the compat mode of the new svnauthz tool. It is
# exactly the same code as svnauthz. This duplicated target is needed
# in order to easily test both commands as part of the build since libtool
# does not provide a way to set argv[0] different from the commands actual
# name in the wrapper script.
[svnauthz-validate]
description = Authz config file validator
type = exe
path = tools/server-side
sources = svnauthz.c
install = tools
libs = libsvn_repos libsvn_fs libsvn_subr apr
[svn-populate-node-origins-index]
+description = Tool to populate the node origins index of a repository
type = exe
path = tools/server-side
sources = svn-populate-node-origins-index.c
install = tools
libs = libsvn_repos libsvn_fs libsvn_subr apr
[svnraisetreeconflict]
description = Tool to Flag a Tree Conflict
type = exe
path = tools/dev/svnraisetreeconflict
libs = libsvn_wc libsvn_subr apriconv apr
install = tools
[svn-rep-sharing-stats]
type = exe
path = tools/server-side
sources = svn-rep-sharing-stats.c
install = tools
libs = libsvn_repos libsvn_fs libsvn_fs_fs libsvn_subr apriconv apr
Index: vendor/subversion/dist/configure
===================================================================
--- vendor/subversion/dist/configure (revision 286500)
+++ vendor/subversion/dist/configure (revision 286501)
@@ -1,27827 +1,27880 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for subversion 1.8.10.
+# Generated by GNU Autoconf 2.69 for subversion 1.8.14.
#
# Report bugs to <http://subversion.apache.org/>.
#
#
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
#
#
# This configure script is free software; the Free Software Foundation
# gives unlimited permission to copy, distribute and modify it.
## -------------------- ##
## M4sh Initialization. ##
## -------------------- ##
# Be more Bourne compatible
DUALCASE=1; export DUALCASE # for MKS sh
if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
emulate sh
NULLCMD=:
# Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
# is contrary to our usage. Disable this feature.
alias -g '${1+"$@"}'='"$@"'
setopt NO_GLOB_SUBST
else
case `(set -o) 2>/dev/null` in #(
*posix*) :
set -o posix ;; #(
*) :
;;
esac
fi
as_nl='
'
export as_nl
# Printing a long string crashes Solaris 7 /usr/bin/printf.
as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
# Prefer a ksh shell builtin over an external printf program on Solaris,
# but without wasting forks for bash or zsh.
if test -z "$BASH_VERSION$ZSH_VERSION" \
&& (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
as_echo='print -r --'
as_echo_n='print -rn --'
elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
as_echo='printf %s\n'
as_echo_n='printf %s'
else
if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
as_echo_n='/usr/ucb/echo -n'
else
as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
as_echo_n_body='eval
arg=$1;
case $arg in #(
*"$as_nl"*)
expr "X$arg" : "X\\(.*\\)$as_nl";
arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
esac;
expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
'
export as_echo_n_body
as_echo_n='sh -c $as_echo_n_body as_echo'
fi
export as_echo_body
as_echo='sh -c $as_echo_body as_echo'
fi
# The user is always right.
if test "${PATH_SEPARATOR+set}" != set; then
PATH_SEPARATOR=:
(PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
(PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
PATH_SEPARATOR=';'
}
fi
# IFS
# We need space, tab and new line, in precisely that order. Quoting is
# there to prevent editors from complaining about space-tab.
# (If _AS_PATH_WALK were called with IFS unset, it would disable word
# splitting by setting IFS to empty value.)
IFS=" "" $as_nl"
# Find who we are. Look in the path if we contain no directory separator.
as_myself=
case $0 in #((
*[\\/]* ) as_myself=$0 ;;
*) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
done
IFS=$as_save_IFS
;;
esac
# We did not find ourselves, most probably we were run as `sh COMMAND'
# in which case we are not to be found in the path.
if test "x$as_myself" = x; then
as_myself=$0
fi
if test ! -f "$as_myself"; then
$as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
exit 1
fi
# Unset variables that we do not need and which cause bugs (e.g. in
# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
# suppresses any "Segmentation fault" message there. '((' could
# trigger a bug in pdksh 5.2.14.
for as_var in BASH_ENV ENV MAIL MAILPATH
do eval test x\${$as_var+set} = xset \
&& ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
done
PS1='$ '
PS2='> '
PS4='+ '
# NLS nuisances.
LC_ALL=C
export LC_ALL
LANGUAGE=C
export LANGUAGE
# CDPATH.
(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
# Use a proper internal environment variable to ensure we don't fall
# into an infinite loop, continuously re-executing ourselves.
if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
_as_can_reexec=no; export _as_can_reexec;
# We cannot yet assume a decent shell, so we have to provide a
# neutralization value for shells without unset; and this also
# works around shells that cannot unset nonexistent variables.
# Preserve -v and -x to the replacement shell.
BASH_ENV=/dev/null
ENV=/dev/null
(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
case $- in # ((((
*v*x* | *x*v* ) as_opts=-vx ;;
*v* ) as_opts=-v ;;
*x* ) as_opts=-x ;;
* ) as_opts= ;;
esac
exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
# Admittedly, this is quite paranoid, since all the known shells bail
# out after a failed `exec'.
$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
as_fn_exit 255
fi
# We don't want this to propagate to other subprocesses.
{ _as_can_reexec=; unset _as_can_reexec;}
if test "x$CONFIG_SHELL" = x; then
as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
emulate sh
NULLCMD=:
# Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
# is contrary to our usage. Disable this feature.
alias -g '\${1+\"\$@\"}'='\"\$@\"'
setopt NO_GLOB_SUBST
else
case \`(set -o) 2>/dev/null\` in #(
*posix*) :
set -o posix ;; #(
*) :
;;
esac
fi
"
as_required="as_fn_return () { (exit \$1); }
as_fn_success () { as_fn_return 0; }
as_fn_failure () { as_fn_return 1; }
as_fn_ret_success () { return 0; }
as_fn_ret_failure () { return 1; }
exitcode=0
as_fn_success || { exitcode=1; echo as_fn_success failed.; }
as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
else
exitcode=1; echo positional parameters were not saved.
fi
test x\$exitcode = x0 || exit 1
test -x / || exit 1"
as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
test \$(( 1 + 1 )) = 2 || exit 1
test -n \"\${ZSH_VERSION+set}\${BASH_VERSION+set}\" || (
ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO
ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO
PATH=/empty FPATH=/empty; export PATH FPATH
test \"X\`printf %s \$ECHO\`\" = \"X\$ECHO\" \\
|| test \"X\`print -r -- \$ECHO\`\" = \"X\$ECHO\" ) || exit 1"
if (eval "$as_required") 2>/dev/null; then :
as_have_required=yes
else
as_have_required=no
fi
if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
as_found=false
for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
as_found=:
case $as_dir in #(
/*)
for as_base in sh bash ksh sh5; do
# Try only shells that exist, to save several forks.
as_shell=$as_dir/$as_base
if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
{ $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
CONFIG_SHELL=$as_shell as_have_required=yes
if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
break 2
fi
fi
done;;
esac
as_found=false
done
$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
{ $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
CONFIG_SHELL=$SHELL as_have_required=yes
fi; }
IFS=$as_save_IFS
if test "x$CONFIG_SHELL" != x; then :
export CONFIG_SHELL
# We cannot yet assume a decent shell, so we have to provide a
# neutralization value for shells without unset; and this also
# works around shells that cannot unset nonexistent variables.
# Preserve -v and -x to the replacement shell.
BASH_ENV=/dev/null
ENV=/dev/null
(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
case $- in # ((((
*v*x* | *x*v* ) as_opts=-vx ;;
*v* ) as_opts=-v ;;
*x* ) as_opts=-x ;;
* ) as_opts= ;;
esac
exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
# Admittedly, this is quite paranoid, since all the known shells bail
# out after a failed `exec'.
$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
exit 255
fi
if test x$as_have_required = xno; then :
$as_echo "$0: This script requires a shell more modern than all"
$as_echo "$0: the shells that I found on your system."
if test x${ZSH_VERSION+set} = xset ; then
$as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
$as_echo "$0: be upgraded to zsh 4.3.4 or later."
else
$as_echo "$0: Please tell bug-autoconf@gnu.org and
$0: http://subversion.apache.org/ about your system,
$0: including any error possibly output before this
$0: message. Then install a modern shell, or manually run
$0: the script under such a shell if you do have one."
fi
exit 1
fi
fi
fi
SHELL=${CONFIG_SHELL-/bin/sh}
export SHELL
# Unset more variables known to interfere with behavior of common tools.
CLICOLOR_FORCE= GREP_OPTIONS=
unset CLICOLOR_FORCE GREP_OPTIONS
## --------------------- ##
## M4sh Shell Functions. ##
## --------------------- ##
# as_fn_unset VAR
# ---------------
# Portably unset VAR.
as_fn_unset ()
{
{ eval $1=; unset $1;}
}
as_unset=as_fn_unset
# as_fn_set_status STATUS
# -----------------------
# Set $? to STATUS, without forking.
as_fn_set_status ()
{
return $1
} # as_fn_set_status
# as_fn_exit STATUS
# -----------------
# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
as_fn_exit ()
{
set +e
as_fn_set_status $1
exit $1
} # as_fn_exit
# as_fn_mkdir_p
# -------------
# Create "$as_dir" as a directory, including parents if necessary.
as_fn_mkdir_p ()
{
case $as_dir in #(
-*) as_dir=./$as_dir;;
esac
test -d "$as_dir" || eval $as_mkdir_p || {
as_dirs=
while :; do
case $as_dir in #(
*\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
*) as_qdir=$as_dir;;
esac
as_dirs="'$as_qdir' $as_dirs"
as_dir=`$as_dirname -- "$as_dir" ||
$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
X"$as_dir" : 'X\(//\)[^/]' \| \
X"$as_dir" : 'X\(//\)$' \| \
X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
$as_echo X"$as_dir" |
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
s//\1/
q
}
/^X\(\/\/\)[^/].*/{
s//\1/
q
}
/^X\(\/\/\)$/{
s//\1/
q
}
/^X\(\/\).*/{
s//\1/
q
}
s/.*/./; q'`
test -d "$as_dir" && break
done
test -z "$as_dirs" || eval "mkdir $as_dirs"
} || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
} # as_fn_mkdir_p
# as_fn_executable_p FILE
# -----------------------
# Test if FILE is an executable regular file.
as_fn_executable_p ()
{
test -f "$1" && test -x "$1"
} # as_fn_executable_p
# as_fn_append VAR VALUE
# ----------------------
# Append the text in VALUE to the end of the definition contained in VAR. Take
# advantage of any shell optimizations that allow amortized linear growth over
# repeated appends, instead of the typical quadratic growth present in naive
# implementations.
if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
eval 'as_fn_append ()
{
eval $1+=\$2
}'
else
as_fn_append ()
{
eval $1=\$$1\$2
}
fi # as_fn_append
# as_fn_arith ARG...
# ------------------
# Perform arithmetic evaluation on the ARGs, and store the result in the
# global $as_val. Take advantage of shells that can avoid forks. The arguments
# must be portable across $(()) and expr.
if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
eval 'as_fn_arith ()
{
as_val=$(( $* ))
}'
else
as_fn_arith ()
{
as_val=`expr "$@" || test $? -eq 1`
}
fi # as_fn_arith
# as_fn_error STATUS ERROR [LINENO LOG_FD]
# ----------------------------------------
# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
# script with STATUS, using 1 if that was 0.
as_fn_error ()
{
as_status=$1; test $as_status -eq 0 && as_status=1
if test "$4"; then
as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
$as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
fi
$as_echo "$as_me: error: $2" >&2
as_fn_exit $as_status
} # as_fn_error
if expr a : '\(a\)' >/dev/null 2>&1 &&
test "X`expr 00001 : '.*\(...\)'`" = X001; then
as_expr=expr
else
as_expr=false
fi
if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
as_basename=basename
else
as_basename=false
fi
if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
as_dirname=dirname
else
as_dirname=false
fi
as_me=`$as_basename -- "$0" ||
$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
X"$0" : 'X\(//\)$' \| \
X"$0" : 'X\(/\)' \| . 2>/dev/null ||
$as_echo X/"$0" |
sed '/^.*\/\([^/][^/]*\)\/*$/{
s//\1/
q
}
/^X\/\(\/\/\)$/{
s//\1/
q
}
/^X\/\(\/\).*/{
s//\1/
q
}
s/.*/./; q'`
# Avoid depending upon Character Ranges.
as_cr_letters='abcdefghijklmnopqrstuvwxyz'
as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
as_cr_Letters=$as_cr_letters$as_cr_LETTERS
as_cr_digits='0123456789'
as_cr_alnum=$as_cr_Letters$as_cr_digits
as_lineno_1=$LINENO as_lineno_1a=$LINENO
as_lineno_2=$LINENO as_lineno_2a=$LINENO
eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
# Blame Lee E. McMahon (1931-1989) for sed's syntax. :-)
sed -n '
p
/[$]LINENO/=
' <$as_myself |
sed '
s/[$]LINENO.*/&-/
t lineno
b
:lineno
N
:loop
s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
t loop
s/-\n.*//
' >$as_me.lineno &&
chmod +x "$as_me.lineno" ||
{ $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
# If we had to re-execute with $CONFIG_SHELL, we're ensured to have
# already done that, so ensure we don't try to do so again and fall
# in an infinite loop. This has already happened in practice.
_as_can_reexec=no; export _as_can_reexec
# Don't try to exec as it changes $[0], causing all sort of problems
# (the dirname of $[0] is not the place where we might find the
# original and so on. Autoconf is especially sensitive to this).
. "./$as_me.lineno"
# Exit status is that of the last command.
exit
}
ECHO_C= ECHO_N= ECHO_T=
case `echo -n x` in #(((((
-n*)
case `echo 'xy\c'` in
*c*) ECHO_T=' ';; # ECHO_T is single tab character.
xy) ECHO_C='\c';;
*) echo `echo ksh88 bug on AIX 6.1` > /dev/null
ECHO_T=' ';;
esac;;
*)
ECHO_N='-n';;
esac
rm -f conf$$ conf$$.exe conf$$.file
if test -d conf$$.dir; then
rm -f conf$$.dir/conf$$.file
else
rm -f conf$$.dir
mkdir conf$$.dir 2>/dev/null
fi
if (echo >conf$$.file) 2>/dev/null; then
if ln -s conf$$.file conf$$ 2>/dev/null; then
as_ln_s='ln -s'
# ... but there are two gotchas:
# 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
# 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
# In both cases, we have to default to `cp -pR'.
ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
as_ln_s='cp -pR'
elif ln conf$$.file conf$$ 2>/dev/null; then
as_ln_s=ln
else
as_ln_s='cp -pR'
fi
else
as_ln_s='cp -pR'
fi
rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
rmdir conf$$.dir 2>/dev/null
if mkdir -p . 2>/dev/null; then
as_mkdir_p='mkdir -p "$as_dir"'
else
test -d ./-p && rmdir ./-p
as_mkdir_p=false
fi
as_test_x='test -x'
as_executable_p=as_fn_executable_p
# Sed expression to map a string onto a valid CPP name.
as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
# Sed expression to map a string onto a valid variable name.
as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
SHELL=${CONFIG_SHELL-/bin/sh}
test -n "$DJDIR" || exec 7<&0 </dev/null
exec 6>&1
# Name of the host.
# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
# so uname gets run too.
ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
#
# Initializations.
#
ac_default_prefix=/usr/local
ac_clean_files=
ac_config_libobj_dir=.
LIBOBJS=
cross_compiling=no
subdirs=
MFLAGS=
MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='subversion'
PACKAGE_TARNAME='subversion'
-PACKAGE_VERSION='1.8.10'
-PACKAGE_STRING='subversion 1.8.10'
+PACKAGE_VERSION='1.8.14'
+PACKAGE_STRING='subversion 1.8.14'
PACKAGE_BUGREPORT='http://subversion.apache.org/'
PACKAGE_URL=''
ac_unique_file="subversion/include/svn_types.h"
# Factoring default headers for most tests.
ac_includes_default="\
#include <stdio.h>
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif
#ifdef HAVE_STRING_H
# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
# include <memory.h>
# endif
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#ifdef HAVE_INTTYPES_H
# include <inttypes.h>
#endif
#ifdef HAVE_STDINT_H
# include <stdint.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif"
ac_subst_vars='LTLIBOBJS
SVN_CONFIG_SCRIPT_FILES
INCLUDE_OUTPUTS
SWIG_CPPFLAGS
JAVAHL_COMPAT_TESTS_TARGET
JAVAHL_TESTS_TARGET
JAVA_CLASSPATH
LT_CXX_LIBADD
FIX_JAVAHL_LIB
JAVAHL_OBJDIR
INSTALL_EXTRA_JAVAHL_LIB
SVN_FS_LIB_LINK
SVN_FS_LIB_INSTALL_DEPS
SVN_FS_LIB_DEPS
SVN_RA_LIB_LINK
SVN_RA_LIB_INSTALL_DEPS
SVN_RA_LIB_DEPS
CTYPESGEN
SWIG_RB_TEST_VERBOSE
SWIG_RB_SITE_ARCH_DIR
SWIG_RB_SITE_LIB_DIR
SWIG_RB_COMPILE
SWIG_RB_INCLUDES
SWIG_RB_LIBS
SWIG_RB_LINK
SWIG_PL_INCLUDES
SWIG_PY_LIBS
SWIG_PY_LINK
SWIG_PY_COMPILE
SWIG_PY_INCLUDES
SWIG
RUBY_TEENY
RUBY_MINOR
RUBY_MAJOR
RDOC
RUBY
PERL
JNI_INCLUDES
JAR
JAVAH
JAVADOC
JAVAC_FLAGS
JAVAC
JAVA
JDK
PYTHON
MOD_ACTIVATION
SVN_ZLIB_LIBS
SVN_ZLIB_INCLUDES
libsvn_wc_LDFLAGS
libsvn_subr_LDFLAGS
libsvn_repos_LDFLAGS
libsvn_ra_svn_LDFLAGS
libsvn_ra_serf_LDFLAGS
libsvn_ra_local_LDFLAGS
libsvn_ra_LDFLAGS
libsvn_fs_util_LDFLAGS
libsvn_fs_fs_LDFLAGS
libsvn_fs_base_LDFLAGS
libsvn_fs_LDFLAGS
libsvn_diff_LDFLAGS
libsvn_delta_LDFLAGS
libsvn_client_LDFLAGS
libsvn_auth_kwallet_LDFLAGS
libsvn_auth_gnome_keyring_LDFLAGS
LIBOBJS
BDB_TEST_PROGRAMS
BDB_TEST_DEPS
INSTALL_RULES
INSTALL_STATIC_RULES
BUILD_RULES
SVN_KWALLET_LIBS
SVN_KWALLET_INCLUDES
KDE4_CONFIG
SVN_MAGIC_LIBS
SVN_MAGIC_INCLUDES
MSGFMTFLAGS
NO_GETTEXT_CODESET
GETTEXT_CODESET
XGETTEXT
MSGMERGE
MSGFMT
SVN_GNOME_KEYRING_LIBS
SVN_GNOME_KEYRING_INCLUDES
SVN_HAVE_GPG_AGENT
SVN_SASL_LIBS
SVN_SASL_INCLUDES
SVN_DB_LIBS
SVN_DB_INCLUDES
SVN_XML_LIBS
SVN_XML_INCLUDES
DOXYGEN
TRANG
LT_NO_UNDEFINED
TRANSFORM_LIBTOOL_SCRIPTS
LT_LDFLAGS
LT_CFLAGS
SVN_LIBTOOL
CXXCPP
OTOOL64
OTOOL
LIPO
NMEDIT
DSYMUTIL
MANIFEST_TOOL
AWK
RANLIB
STRIP
ac_ct_AR
AR
DLLTOOL
OBJDUMP
NM
ac_ct_DUMPBIN
DUMPBIN
LD
FGREP
LIBTOOL
SVN_BINDIR
SVN_SQLITE_LIBS
SVN_SQLITE_INCLUDES
+HTTPD_VERSION
INSTALL_APACHE_MODS
APACHE_LIBEXECDIR
APACHE_INCLUDES
APACHE_LDFLAGS
APXS
SVN_APR_MEMCACHE_LIBS
SVN_APR_MEMCACHE_INCLUDES
SVN_SERF_LIBS
SVN_SERF_INCLUDES
PKG_CONFIG
SVN_LT_SOVERSION
SVN_APRUTIL_LIBS
SVN_APRUTIL_CONFIG
SVN_APRUTIL_INCLUDES
SVN_APR_SHLIB_PATH_VAR
SVN_APR_LIBS
SVN_APR_INCLUDES
SVN_APR_CONFIG
MKDIR
INSTALL_DATA
INSTALL_SCRIPT
INSTALL_PROGRAM
LN_S
EGREP
GREP
target_os
target_vendor
target_cpu
target
host_os
host_vendor
host_cpu
host
build_os
build_vendor
build_cpu
build
SED
CPP
CXXMAINTAINERFLAGS
CXXMODEFLAGS
ac_ct_CXX
CXXFLAGS
CXX
CMAINTAINERFLAGS
CMODEFLAGS
OBJEXT
EXEEXT
ac_ct_CC
CPPFLAGS
LDFLAGS
CFLAGS
CC
SWIG_LDFLAGS
canonicalized_srcdir
abs_builddir
abs_srcdir
target_alias
host_alias
build_alias
LIBS
ECHO_T
ECHO_N
ECHO_C
DEFS
mandir
localedir
libdir
psdir
pdfdir
dvidir
htmldir
infodir
docdir
oldincludedir
includedir
localstatedir
sharedstatedir
sysconfdir
datadir
datarootdir
libexecdir
sbindir
bindir
program_transform_name
prefix
exec_prefix
PACKAGE_URL
PACKAGE_BUGREPORT
PACKAGE_STRING
PACKAGE_VERSION
PACKAGE_TARNAME
PACKAGE_NAME
PATH_SEPARATOR
SHELL'
ac_subst_files=''
ac_user_opts='
enable_option_checking
with_apr
with_apr_util
with_serf
with_apr_memcache
with_apxs
with_apache_libexecdir
+enable_broken_httpd_auth
with_sqlite
enable_sqlite_compatibility_version
enable_shared
enable_static
with_pic
enable_fast_install
with_gnu_ld
with_sysroot
enable_libtool_lock
enable_experimental_libtool
enable_all_static
enable_local_library_preloading
with_trang
with_doxygen
with_expat
with_berkeley_db
enable_bdb6
with_sasl
enable_keychain
with_gpg_agent
with_gnome_keyring
enable_ev2_impl
enable_nls
with_libmagic
with_kwallet
enable_plaintext_password_storage
with_openssl
enable_debug
enable_optimize
enable_disallowing_of_undefined_references
enable_maintainer_mode
enable_full_version_match
with_editor
with_zlib
enable_mod_activation
enable_gcov
enable_gprof
with_jdk
with_jikes
with_swig
with_ruby_sitedir
with_ruby_test_verbose
with_ctypesgen
enable_runtime_module_search
enable_javahl
with_junit
'
ac_precious_vars='build_alias
host_alias
target_alias
CC
CFLAGS
LDFLAGS
LIBS
CPPFLAGS
CXX
CXXFLAGS
CCC
CPP
CXXCPP'
# Initialize some variables set by options.
ac_init_help=
ac_init_version=false
ac_unrecognized_opts=
ac_unrecognized_sep=
# The variables have the same names as the options, with
# dashes changed to underlines.
cache_file=/dev/null
exec_prefix=NONE
no_create=
no_recursion=
prefix=NONE
program_prefix=NONE
program_suffix=NONE
program_transform_name=s,x,x,
silent=
site=
srcdir=
verbose=
x_includes=NONE
x_libraries=NONE
# Installation directory options.
# These are left unexpanded so users can "make install exec_prefix=/foo"
# and all the variables that are supposed to be based on exec_prefix
# by default will actually change.
# Use braces instead of parens because sh, perl, etc. also accept them.
# (The list follows the same order as the GNU Coding Standards.)
bindir='${exec_prefix}/bin'
sbindir='${exec_prefix}/sbin'
libexecdir='${exec_prefix}/libexec'
datarootdir='${prefix}/share'
datadir='${datarootdir}'
sysconfdir='${prefix}/etc'
sharedstatedir='${prefix}/com'
localstatedir='${prefix}/var'
includedir='${prefix}/include'
oldincludedir='/usr/include'
docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
infodir='${datarootdir}/info'
htmldir='${docdir}'
dvidir='${docdir}'
pdfdir='${docdir}'
psdir='${docdir}'
libdir='${exec_prefix}/lib'
localedir='${datarootdir}/locale'
mandir='${datarootdir}/man'
ac_prev=
ac_dashdash=
for ac_option
do
# If the previous option needs an argument, assign it.
if test -n "$ac_prev"; then
eval $ac_prev=\$ac_option
ac_prev=
continue
fi
case $ac_option in
*=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
*=) ac_optarg= ;;
*) ac_optarg=yes ;;
esac
# Accept the important Cygnus configure options, so we can diagnose typos.
case $ac_dashdash$ac_option in
--)
ac_dashdash=yes ;;
-bindir | --bindir | --bindi | --bind | --bin | --bi)
ac_prev=bindir ;;
-bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
bindir=$ac_optarg ;;
-build | --build | --buil | --bui | --bu)
ac_prev=build_alias ;;
-build=* | --build=* | --buil=* | --bui=* | --bu=*)
build_alias=$ac_optarg ;;
-cache-file | --cache-file | --cache-fil | --cache-fi \
| --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
ac_prev=cache_file ;;
-cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
| --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
cache_file=$ac_optarg ;;
--config-cache | -C)
cache_file=config.cache ;;
-datadir | --datadir | --datadi | --datad)
ac_prev=datadir ;;
-datadir=* | --datadir=* | --datadi=* | --datad=*)
datadir=$ac_optarg ;;
-datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
| --dataroo | --dataro | --datar)
ac_prev=datarootdir ;;
-datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
| --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
datarootdir=$ac_optarg ;;
-disable-* | --disable-*)
ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
# Reject names that are not valid shell variable names.
expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
as_fn_error $? "invalid feature name: $ac_useropt"
ac_useropt_orig=$ac_useropt
ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
case $ac_user_opts in
*"
"enable_$ac_useropt"
"*) ;;
*) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
ac_unrecognized_sep=', ';;
esac
eval enable_$ac_useropt=no ;;
-docdir | --docdir | --docdi | --doc | --do)
ac_prev=docdir ;;
-docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
docdir=$ac_optarg ;;
-dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
ac_prev=dvidir ;;
-dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
dvidir=$ac_optarg ;;
-enable-* | --enable-*)
ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
# Reject names that are not valid shell variable names.
expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
as_fn_error $? "invalid feature name: $ac_useropt"
ac_useropt_orig=$ac_useropt
ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
case $ac_user_opts in
*"
"enable_$ac_useropt"
"*) ;;
*) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
ac_unrecognized_sep=', ';;
esac
eval enable_$ac_useropt=\$ac_optarg ;;
-exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
| --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
| --exec | --exe | --ex)
ac_prev=exec_prefix ;;
-exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
| --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
| --exec=* | --exe=* | --ex=*)
exec_prefix=$ac_optarg ;;
-gas | --gas | --ga | --g)
# Obsolete; use --with-gas.
with_gas=yes ;;
-help | --help | --hel | --he | -h)
ac_init_help=long ;;
-help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
ac_init_help=recursive ;;
-help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
ac_init_help=short ;;
-host | --host | --hos | --ho)
ac_prev=host_alias ;;
-host=* | --host=* | --hos=* | --ho=*)
host_alias=$ac_optarg ;;
-htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
ac_prev=htmldir ;;
-htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
| --ht=*)
htmldir=$ac_optarg ;;
-includedir | --includedir | --includedi | --included | --include \
| --includ | --inclu | --incl | --inc)
ac_prev=includedir ;;
-includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
| --includ=* | --inclu=* | --incl=* | --inc=*)
includedir=$ac_optarg ;;
-infodir | --infodir | --infodi | --infod | --info | --inf)
ac_prev=infodir ;;
-infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
infodir=$ac_optarg ;;
-libdir | --libdir | --libdi | --libd)
ac_prev=libdir ;;
-libdir=* | --libdir=* | --libdi=* | --libd=*)
libdir=$ac_optarg ;;
-libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
| --libexe | --libex | --libe)
ac_prev=libexecdir ;;
-libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
| --libexe=* | --libex=* | --libe=*)
libexecdir=$ac_optarg ;;
-localedir | --localedir | --localedi | --localed | --locale)
ac_prev=localedir ;;
-localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
localedir=$ac_optarg ;;
-localstatedir | --localstatedir | --localstatedi | --localstated \
| --localstate | --localstat | --localsta | --localst | --locals)
ac_prev=localstatedir ;;
-localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
| --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
localstatedir=$ac_optarg ;;
-mandir | --mandir | --mandi | --mand | --man | --ma | --m)
ac_prev=mandir ;;
-mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
mandir=$ac_optarg ;;
-nfp | --nfp | --nf)
# Obsolete; use --without-fp.
with_fp=no ;;
-no-create | --no-create | --no-creat | --no-crea | --no-cre \
| --no-cr | --no-c | -n)
no_create=yes ;;
-no-recursion | --no-recursion | --no-recursio | --no-recursi \
| --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
no_recursion=yes ;;
-oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
| --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
| --oldin | --oldi | --old | --ol | --o)
ac_prev=oldincludedir ;;
-oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
| --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
| --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
oldincludedir=$ac_optarg ;;
-prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
ac_prev=prefix ;;
-prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
prefix=$ac_optarg ;;
-program-prefix | --program-prefix | --program-prefi | --program-pref \
| --program-pre | --program-pr | --program-p)
ac_prev=program_prefix ;;
-program-prefix=* | --program-prefix=* | --program-prefi=* \
| --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
program_prefix=$ac_optarg ;;
-program-suffix | --program-suffix | --program-suffi | --program-suff \
| --program-suf | --program-su | --program-s)
ac_prev=program_suffix ;;
-program-suffix=* | --program-suffix=* | --program-suffi=* \
| --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
program_suffix=$ac_optarg ;;
-program-transform-name | --program-transform-name \
| --program-transform-nam | --program-transform-na \
| --program-transform-n | --program-transform- \
| --program-transform | --program-transfor \
| --program-transfo | --program-transf \
| --program-trans | --program-tran \
| --progr-tra | --program-tr | --program-t)
ac_prev=program_transform_name ;;
-program-transform-name=* | --program-transform-name=* \
| --program-transform-nam=* | --program-transform-na=* \
| --program-transform-n=* | --program-transform-=* \
| --program-transform=* | --program-transfor=* \
| --program-transfo=* | --program-transf=* \
| --program-trans=* | --program-tran=* \
| --progr-tra=* | --program-tr=* | --program-t=*)
program_transform_name=$ac_optarg ;;
-pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
ac_prev=pdfdir ;;
-pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
pdfdir=$ac_optarg ;;
-psdir | --psdir | --psdi | --psd | --ps)
ac_prev=psdir ;;
-psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
psdir=$ac_optarg ;;
-q | -quiet | --quiet | --quie | --qui | --qu | --q \
| -silent | --silent | --silen | --sile | --sil)
silent=yes ;;
-sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
ac_prev=sbindir ;;
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
| --sbi=* | --sb=*)
sbindir=$ac_optarg ;;
-sharedstatedir | --sharedstatedir | --sharedstatedi \
| --sharedstated | --sharedstate | --sharedstat | --sharedsta \
| --sharedst | --shareds | --shared | --share | --shar \
| --sha | --sh)
ac_prev=sharedstatedir ;;
-sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
| --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
| --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
| --sha=* | --sh=*)
sharedstatedir=$ac_optarg ;;
-site | --site | --sit)
ac_prev=site ;;
-site=* | --site=* | --sit=*)
site=$ac_optarg ;;
-srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
ac_prev=srcdir ;;
-srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
srcdir=$ac_optarg ;;
-sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
| --syscon | --sysco | --sysc | --sys | --sy)
ac_prev=sysconfdir ;;
-sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
| --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
sysconfdir=$ac_optarg ;;
-target | --target | --targe | --targ | --tar | --ta | --t)
ac_prev=target_alias ;;
-target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
target_alias=$ac_optarg ;;
-v | -verbose | --verbose | --verbos | --verbo | --verb)
verbose=yes ;;
-version | --version | --versio | --versi | --vers | -V)
ac_init_version=: ;;
-with-* | --with-*)
ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
# Reject names that are not valid shell variable names.
expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
as_fn_error $? "invalid package name: $ac_useropt"
ac_useropt_orig=$ac_useropt
ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
case $ac_user_opts in
*"
"with_$ac_useropt"
"*) ;;
*) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
ac_unrecognized_sep=', ';;
esac
eval with_$ac_useropt=\$ac_optarg ;;
-without-* | --without-*)
ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
# Reject names that are not valid shell variable names.
expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
as_fn_error $? "invalid package name: $ac_useropt"
ac_useropt_orig=$ac_useropt
ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
case $ac_user_opts in
*"
"with_$ac_useropt"
"*) ;;
*) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
ac_unrecognized_sep=', ';;
esac
eval with_$ac_useropt=no ;;
--x)
# Obsolete; use --with-x.
with_x=yes ;;
-x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
| --x-incl | --x-inc | --x-in | --x-i)
ac_prev=x_includes ;;
-x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
| --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
x_includes=$ac_optarg ;;
-x-libraries | --x-libraries | --x-librarie | --x-librari \
| --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
ac_prev=x_libraries ;;
-x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
| --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
x_libraries=$ac_optarg ;;
-*) as_fn_error $? "unrecognized option: \`$ac_option'
Try \`$0 --help' for more information"
;;
*=*)
ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
# Reject names that are not valid shell variable names.
case $ac_envvar in #(
'' | [0-9]* | *[!_$as_cr_alnum]* )
as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
esac
eval $ac_envvar=\$ac_optarg
export $ac_envvar ;;
*)
# FIXME: should be removed in autoconf 3.0.
$as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
$as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
: "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
;;
esac
done
if test -n "$ac_prev"; then
ac_option=--`echo $ac_prev | sed 's/_/-/g'`
as_fn_error $? "missing argument to $ac_option"
fi
if test -n "$ac_unrecognized_opts"; then
case $enable_option_checking in
no) ;;
fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
*) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
esac
fi
# Check all directory arguments for consistency.
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
datadir sysconfdir sharedstatedir localstatedir includedir \
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
libdir localedir mandir
do
eval ac_val=\$$ac_var
# Remove trailing slashes.
case $ac_val in
*/ )
ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
eval $ac_var=\$ac_val;;
esac
# Be sure to have absolute directory names.
case $ac_val in
[\\/$]* | ?:[\\/]* ) continue;;
NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
esac
as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
done
# There might be people who depend on the old broken behavior: `$host'
# used to hold the argument of --host etc.
# FIXME: To remove some day.
build=$build_alias
host=$host_alias
target=$target_alias
# FIXME: To remove some day.
if test "x$host_alias" != x; then
if test "x$build_alias" = x; then
cross_compiling=maybe
elif test "x$build_alias" != "x$host_alias"; then
cross_compiling=yes
fi
fi
ac_tool_prefix=
test -n "$host_alias" && ac_tool_prefix=$host_alias-
test "$silent" = yes && exec 6>/dev/null
ac_pwd=`pwd` && test -n "$ac_pwd" &&
ac_ls_di=`ls -di .` &&
ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
as_fn_error $? "working directory cannot be determined"
test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
as_fn_error $? "pwd does not report name of working directory"
# Find the source files, if location was not specified.
if test -z "$srcdir"; then
ac_srcdir_defaulted=yes
# Try the directory containing this script, then the parent directory.
ac_confdir=`$as_dirname -- "$as_myself" ||
$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
X"$as_myself" : 'X\(//\)[^/]' \| \
X"$as_myself" : 'X\(//\)$' \| \
X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
$as_echo X"$as_myself" |
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
s//\1/
q
}
/^X\(\/\/\)[^/].*/{
s//\1/
q
}
/^X\(\/\/\)$/{
s//\1/
q
}
/^X\(\/\).*/{
s//\1/
q
}
s/.*/./; q'`
srcdir=$ac_confdir
if test ! -r "$srcdir/$ac_unique_file"; then
srcdir=..
fi
else
ac_srcdir_defaulted=no
fi
if test ! -r "$srcdir/$ac_unique_file"; then
test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
fi
ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
ac_abs_confdir=`(
cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
pwd)`
# When building in place, set srcdir=.
if test "$ac_abs_confdir" = "$ac_pwd"; then
srcdir=.
fi
# Remove unnecessary trailing slashes from srcdir.
# Double slashes in file names in object file debugging info
# mess up M-x gdb in Emacs.
case $srcdir in
*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
esac
for ac_var in $ac_precious_vars; do
eval ac_env_${ac_var}_set=\${${ac_var}+set}
eval ac_env_${ac_var}_value=\$${ac_var}
eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
eval ac_cv_env_${ac_var}_value=\$${ac_var}
done
#
# Report the --help message.
#
if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures subversion 1.8.10 to adapt to many kinds of systems.
+\`configure' configures subversion 1.8.14 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
To assign environment variables (e.g., CC, CFLAGS...), specify them as
VAR=VALUE. See below for descriptions of some of the useful variables.
Defaults for the options are specified in brackets.
Configuration:
-h, --help display this help and exit
--help=short display options specific to this package
--help=recursive display the short help of all the included packages
-V, --version display version information and exit
-q, --quiet, --silent do not print \`checking ...' messages
--cache-file=FILE cache test results in FILE [disabled]
-C, --config-cache alias for \`--cache-file=config.cache'
-n, --no-create do not create output files
--srcdir=DIR find the sources in DIR [configure dir or \`..']
Installation directories:
--prefix=PREFIX install architecture-independent files in PREFIX
[$ac_default_prefix]
--exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
[PREFIX]
By default, \`make install' will install all the files in
\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
an installation prefix other than \`$ac_default_prefix' using \`--prefix',
for instance \`--prefix=\$HOME'.
For better control, use the options below.
Fine tuning of the installation directories:
--bindir=DIR user executables [EPREFIX/bin]
--sbindir=DIR system admin executables [EPREFIX/sbin]
--libexecdir=DIR program executables [EPREFIX/libexec]
--sysconfdir=DIR read-only single-machine data [PREFIX/etc]
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
--localstatedir=DIR modifiable single-machine data [PREFIX/var]
--libdir=DIR object code libraries [EPREFIX/lib]
--includedir=DIR C header files [PREFIX/include]
--oldincludedir=DIR C header files for non-gcc [/usr/include]
--datarootdir=DIR read-only arch.-independent data root [PREFIX/share]
--datadir=DIR read-only architecture-independent data [DATAROOTDIR]
--infodir=DIR info documentation [DATAROOTDIR/info]
--localedir=DIR locale-dependent data [DATAROOTDIR/locale]
--mandir=DIR man documentation [DATAROOTDIR/man]
--docdir=DIR documentation root [DATAROOTDIR/doc/subversion]
--htmldir=DIR html documentation [DOCDIR]
--dvidir=DIR dvi documentation [DOCDIR]
--pdfdir=DIR pdf documentation [DOCDIR]
--psdir=DIR ps documentation [DOCDIR]
_ACEOF
cat <<\_ACEOF
System types:
--build=BUILD configure for building on BUILD [guessed]
--host=HOST cross-compile to build programs to run on HOST [BUILD]
--target=TARGET configure for building compilers for TARGET [HOST]
_ACEOF
fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of subversion 1.8.10:";;
+ short | recursive ) echo "Configuration of subversion 1.8.14:";;
esac
cat <<\_ACEOF
Optional Features:
--disable-option-checking ignore unrecognized --enable/--with options
--disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
--enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --enable-broken-httpd-auth
+ Allow building against httpd 2.4 with broken auth
--enable-sqlite-compatibility-version=X.Y.Z
Allow binary to run against SQLite as old as ARG
--enable-shared[=PKGS] build shared libraries [default=yes]
--enable-static[=PKGS] build static libraries [default=yes]
--enable-fast-install[=PKGS]
optimize for fast installation [default=yes]
--disable-libtool-lock avoid locking (might break parallel builds)
--enable-experimental-libtool
Use APR's libtool
--enable-static Build static libraries
--enable-shared Build shared libraries
--enable-all-static Build completely static (standalone) binaries.
--enable-local-library-preloading
Enable preloading of locally built libraries in
locally built executables. This may be necessary for
testing prior to installation on some platforms. It
does not work on some platforms (Darwin, OpenBSD,
...).
--enable-bdb6 Allow building against BDB 6+. See
--with-berkeley-db for specifying the location of
the Berkeley DB installation. Using BDB 6 will fail
if this option is not used.
--disable-keychain Disable use of Mac OS KeyChain for auth credentials
--enable-ev2-impl Use Ev2 implementations, where available
[EXPERIMENTAL]
--disable-nls Disable gettext functionality
--disable-plaintext-password-storage
Disable on-disk caching of plaintext passwords and
passphrases. (Leaving this functionality enabled
will not force Subversion to store passwords in
plaintext, but does permit users to explicitly allow
that behavior via runtime configuration.)
--enable-debug Turn on debugging
--enable-optimize Turn on optimizations
--enable-disallowing-of-undefined-references
Use -Wl,--no-undefined flag during linking of some
libraries to disallow undefined references
--enable-maintainer-mode
Turn on debugging and very strict compile-time
warnings
--disable-full-version-match
Disable the full version match rules when checking
Subversion library compatibility.
--enable-mod-activation Enable mod_dav_svn in httpd.conf
--enable-gcov Turn on gcov coverage testing (GCC only).
--enable-gprof Produce gprof profiling data in 'gmon.out' (GCC
only).
--enable-runtime-module-search
Turn on dynamic loading of RA/FS libraries including
third-party FS libraries
--enable-javahl Enable compilation of Java high-level bindings
(requires C++)
Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
--without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
--with-apr=PATH prefix for installed APR, path to APR build tree,
or the full path to apr-config
--with-apr-util=PATH prefix for installed APU, path to APU build tree,
or the full path to apu-config
--with-serf=PREFIX Serf HTTP client library (enabled by default if
found)
--with-apr_memcache=PREFIX
Standalone apr_memcache client library
--with-apxs[=FILE] Build shared Apache modules. FILE is the optional
pathname to the Apache apxs tool; defaults to
"apxs".
--with-apache-libexecdir[=PATH]
Install Apache modules to Apache's configured
modules directory instead of LIBEXECDIR; if PATH is
given, install to PATH.
--with-sqlite=PREFIX Use installed SQLite library or amalgamation file.
--with-pic[=PKGS] try to use only PIC/non-PIC objects [default=use
both]
--with-gnu-ld assume the C compiler uses GNU ld [default=no]
- --with-sysroot=DIR Search for dependent libraries within DIR
- (or the compiler's sysroot if not specified).
+ --with-sysroot[=DIR] Search for dependent libraries within DIR (or the
+ compiler's sysroot if not specified).
--with-trang=PATH Specify the command to run the trang schema
converter
--with-doxygen=PATH Specify the command to run doxygen
--with-expat=INCLUDES:LIB_SEARCH_DIRS:LIBS
Specify location of Expat
--with-berkeley-db[=HEADER:INCLUDES:LIB_SEARCH_DIRS:LIBS]
The Subversion Berkeley DB based filesystem library
requires Berkeley DB $db_version or $db_alt_version.
If you specify `--without-berkeley-db', that library
will not be built. If you omit the argument of this
option completely, the configure script will use
Berkeley DB used by APR-UTIL.
--with-sasl=PATH Compile with libsasl2 in PATH
--without-gpg-agent Disable support for GPG-Agent
--with-gnome-keyring Enable use of GNOME Keyring for auth credentials
(enabled by default if found)
--with-libmagic=PREFIX libmagic filetype detection library
--with-kwallet[=PATH] Enable use of KWallet (KDE 4) for auth credentials
--with-openssl This option does NOT affect the Subversion build
process in any way. It tells an integrated Serf HTTP
client library build process where to locate the
OpenSSL library when (and only when) building Serf
as an integrated part of the Subversion build
process. When linking to a previously installed
version of Serf instead, you do not need to use this
option.
--with-editor=PATH Specify a default editor for the subversion client.
--with-zlib=PREFIX zlib compression library
--with-jdk=PATH Try to use 'PATH/include' to find the JNI headers.
If PATH is not specified, look for a Java
Development Kit at JAVA_HOME.
--with-jikes=PATH Specify the path to a jikes binary to use it as your
Java compiler. The default is to look for jikes
(PATH optional). This behavior can be switched off
by supplying 'no'.
--with-swig=PATH Try to use 'PATH/bin/swig' to build the swig
bindings. If PATH is not specified, look for a
'swig' binary in your PATH.
--with-ruby-sitedir=SITEDIR
install Ruby bindings in SITEDIR (default is same as
ruby's one)
--with-ruby-test-verbose=LEVEL
how to use output level for Ruby bindings tests
(default is normal)
--with-ctypesgen=PATH Specify the path to ctypesgen. This can either be
the full path to a ctypesgen installation, the full
path to a ctypesgen source tree or the full path to
ctypesgen.py.
--with-junit=PATH Specify a path to the junit JAR file.
Some influential environment variables:
CC C compiler command
CFLAGS C compiler flags
LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
nonstandard directory <lib dir>
LIBS libraries to pass to the linker, e.g. -l<library>
CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
you have headers in a nonstandard directory <include dir>
CXX C++ compiler command
CXXFLAGS C++ compiler flags
CPP C preprocessor
CXXCPP C++ preprocessor
Use these variables to override the choices made by `configure' or to help
it to find libraries and programs with nonstandard names/locations.
Report bugs to <http://subversion.apache.org/>.
_ACEOF
ac_status=$?
fi
if test "$ac_init_help" = "recursive"; then
# If there are subdirs, report their specific --help.
for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
test -d "$ac_dir" ||
{ cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
continue
ac_builddir=.
case "$ac_dir" in
.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
*)
ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
# A ".." for each directory in $ac_dir_suffix.
ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
case $ac_top_builddir_sub in
"") ac_top_builddir_sub=. ac_top_build_prefix= ;;
*) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
esac ;;
esac
ac_abs_top_builddir=$ac_pwd
ac_abs_builddir=$ac_pwd$ac_dir_suffix
# for backward compatibility:
ac_top_builddir=$ac_top_build_prefix
case $srcdir in
.) # We are building in place.
ac_srcdir=.
ac_top_srcdir=$ac_top_builddir_sub
ac_abs_top_srcdir=$ac_pwd ;;
[\\/]* | ?:[\\/]* ) # Absolute name.
ac_srcdir=$srcdir$ac_dir_suffix;
ac_top_srcdir=$srcdir
ac_abs_top_srcdir=$srcdir ;;
*) # Relative name.
ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
ac_top_srcdir=$ac_top_build_prefix$srcdir
ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
esac
ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
cd "$ac_dir" || { ac_status=$?; continue; }
# Check for guested configure.
if test -f "$ac_srcdir/configure.gnu"; then
echo &&
$SHELL "$ac_srcdir/configure.gnu" --help=recursive
elif test -f "$ac_srcdir/configure"; then
echo &&
$SHELL "$ac_srcdir/configure" --help=recursive
else
$as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
fi || ac_status=$?
cd "$ac_pwd" || { ac_status=$?; break; }
done
fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-subversion configure 1.8.10
+subversion configure 1.8.14
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
This configure script is free software; the Free Software Foundation
gives unlimited permission to copy, distribute and modify it.
_ACEOF
exit
fi
## ------------------------ ##
## Autoconf initialization. ##
## ------------------------ ##
# ac_fn_c_try_compile LINENO
# --------------------------
# Try to compile conftest.$ac_ext, and return whether this succeeded.
ac_fn_c_try_compile ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
rm -f conftest.$ac_objext
if { { ac_try="$ac_compile"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
(eval "$ac_compile") 2>conftest.err
ac_status=$?
if test -s conftest.err; then
grep -v '^ *+' conftest.err >conftest.er1
cat conftest.er1 >&5
mv -f conftest.er1 conftest.err
fi
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } && {
test -z "$ac_c_werror_flag" ||
test ! -s conftest.err
} && test -s conftest.$ac_objext; then :
ac_retval=0
else
$as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=1
fi
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
as_fn_set_status $ac_retval
} # ac_fn_c_try_compile
# ac_fn_c_try_link LINENO
# -----------------------
# Try to link conftest.$ac_ext, and return whether this succeeded.
ac_fn_c_try_link ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
rm -f conftest.$ac_objext conftest$ac_exeext
if { { ac_try="$ac_link"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
(eval "$ac_link") 2>conftest.err
ac_status=$?
if test -s conftest.err; then
grep -v '^ *+' conftest.err >conftest.er1
cat conftest.er1 >&5
mv -f conftest.er1 conftest.err
fi
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } && {
test -z "$ac_c_werror_flag" ||
test ! -s conftest.err
} && test -s conftest$ac_exeext && {
test "$cross_compiling" = yes ||
test -x conftest$ac_exeext
}; then :
ac_retval=0
else
$as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=1
fi
# Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
# created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
# interfere with the next link command; also delete a directory that is
# left behind by Apple's compiler. We do this before executing the actions.
rm -rf conftest.dSYM conftest_ipa8_conftest.oo
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
as_fn_set_status $ac_retval
} # ac_fn_c_try_link
# ac_fn_cxx_try_compile LINENO
# ----------------------------
# Try to compile conftest.$ac_ext, and return whether this succeeded.
ac_fn_cxx_try_compile ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
rm -f conftest.$ac_objext
if { { ac_try="$ac_compile"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
(eval "$ac_compile") 2>conftest.err
ac_status=$?
if test -s conftest.err; then
grep -v '^ *+' conftest.err >conftest.er1
cat conftest.er1 >&5
mv -f conftest.er1 conftest.err
fi
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } && {
test -z "$ac_cxx_werror_flag" ||
test ! -s conftest.err
} && test -s conftest.$ac_objext; then :
ac_retval=0
else
$as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=1
fi
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
as_fn_set_status $ac_retval
} # ac_fn_cxx_try_compile
# ac_fn_cxx_try_link LINENO
# -------------------------
# Try to link conftest.$ac_ext, and return whether this succeeded.
ac_fn_cxx_try_link ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
rm -f conftest.$ac_objext conftest$ac_exeext
if { { ac_try="$ac_link"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
(eval "$ac_link") 2>conftest.err
ac_status=$?
if test -s conftest.err; then
grep -v '^ *+' conftest.err >conftest.er1
cat conftest.er1 >&5
mv -f conftest.er1 conftest.err
fi
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } && {
test -z "$ac_cxx_werror_flag" ||
test ! -s conftest.err
} && test -s conftest$ac_exeext && {
test "$cross_compiling" = yes ||
test -x conftest$ac_exeext
}; then :
ac_retval=0
else
$as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=1
fi
# Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
# created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
# interfere with the next link command; also delete a directory that is
# left behind by Apple's compiler. We do this before executing the actions.
rm -rf conftest.dSYM conftest_ipa8_conftest.oo
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
as_fn_set_status $ac_retval
} # ac_fn_cxx_try_link
# ac_fn_c_try_cpp LINENO
# ----------------------
# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
ac_fn_c_try_cpp ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
if { { ac_try="$ac_cpp conftest.$ac_ext"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
(eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
ac_status=$?
if test -s conftest.err; then
grep -v '^ *+' conftest.err >conftest.er1
cat conftest.er1 >&5
mv -f conftest.er1 conftest.err
fi
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } > conftest.i && {
test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
test ! -s conftest.err
}; then :
ac_retval=0
else
$as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=1
fi
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
as_fn_set_status $ac_retval
} # ac_fn_c_try_cpp
# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES
# -------------------------------------------------------
# Tests whether HEADER exists, giving a warning if it cannot be compiled using
# the include files in INCLUDES and setting the cache variable VAR
# accordingly.
ac_fn_c_check_header_mongrel ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
if eval \${$3+:} false; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
$as_echo_n "checking for $2... " >&6; }
if eval \${$3+:} false; then :
$as_echo_n "(cached) " >&6
fi
eval ac_res=\$$3
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
else
# Is the header compilable?
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5
$as_echo_n "checking $2 usability... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
#include <$2>
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ac_header_compiler=yes
else
ac_header_compiler=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5
$as_echo "$ac_header_compiler" >&6; }
# Is the header present?
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5
$as_echo_n "checking $2 presence... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <$2>
_ACEOF
if ac_fn_c_try_cpp "$LINENO"; then :
ac_header_preproc=yes
else
ac_header_preproc=no
fi
rm -f conftest.err conftest.i conftest.$ac_ext
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
$as_echo "$ac_header_preproc" >&6; }
# So? What about this header?
case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #((
yes:no: )
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5
$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
;;
no:yes:* )
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5
$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5
$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5
$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5
$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
( $as_echo "## -------------------------------------------- ##
## Report this to http://subversion.apache.org/ ##
## -------------------------------------------- ##"
) | sed "s/^/$as_me: WARNING: /" >&2
;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
$as_echo_n "checking for $2... " >&6; }
if eval \${$3+:} false; then :
$as_echo_n "(cached) " >&6
else
eval "$3=\$ac_header_compiler"
fi
eval ac_res=\$$3
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
fi
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_header_mongrel
# ac_fn_c_try_run LINENO
# ----------------------
# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes
# that executables *can* be run.
ac_fn_c_try_run ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
if { { ac_try="$ac_link"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
(eval "$ac_link") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
{ { case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
(eval "$ac_try") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; }; then :
ac_retval=0
else
$as_echo "$as_me: program exited with status $ac_status" >&5
$as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=$ac_status
fi
rm -rf conftest.dSYM conftest_ipa8_conftest.oo
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
as_fn_set_status $ac_retval
} # ac_fn_c_try_run
# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
# -------------------------------------------------------
# Tests whether HEADER exists and can be compiled using the include files in
# INCLUDES, setting the cache variable VAR accordingly.
ac_fn_c_check_header_compile ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
$as_echo_n "checking for $2... " >&6; }
if eval \${$3+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
#include <$2>
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
eval "$3=yes"
else
eval "$3=no"
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
eval ac_res=\$$3
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_header_compile
# ac_fn_c_check_func LINENO FUNC VAR
# ----------------------------------
# Tests whether FUNC exists, setting the cache variable VAR accordingly
ac_fn_c_check_func ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
$as_echo_n "checking for $2... " >&6; }
if eval \${$3+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
For example, HP-UX 11i <limits.h> declares gettimeofday. */
#define $2 innocuous_$2
/* System header to define __stub macros and hopefully few prototypes,
which can conflict with char $2 (); below.
Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
<limits.h> exists even on freestanding compilers. */
#ifdef __STDC__
# include <limits.h>
#else
# include <assert.h>
#endif
#undef $2
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char $2 ();
/* The GNU C library defines this for functions which it implements
to always fail with ENOSYS. Some functions are actually named
something starting with __ and the normal name is an alias. */
#if defined __stub_$2 || defined __stub___$2
choke me
#endif
int
main ()
{
return $2 ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
eval "$3=yes"
else
eval "$3=no"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
fi
eval ac_res=\$$3
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_func
# ac_fn_cxx_try_cpp LINENO
# ------------------------
# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
ac_fn_cxx_try_cpp ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
if { { ac_try="$ac_cpp conftest.$ac_ext"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
(eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
ac_status=$?
if test -s conftest.err; then
grep -v '^ *+' conftest.err >conftest.er1
cat conftest.er1 >&5
mv -f conftest.er1 conftest.err
fi
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } > conftest.i && {
test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
test ! -s conftest.err
}; then :
ac_retval=0
else
$as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=1
fi
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
as_fn_set_status $ac_retval
} # ac_fn_cxx_try_cpp
# ac_fn_c_check_type LINENO TYPE VAR INCLUDES
# -------------------------------------------
# Tests whether TYPE exists after having included INCLUDES, setting cache
# variable VAR accordingly.
ac_fn_c_check_type ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
$as_echo_n "checking for $2... " >&6; }
if eval \${$3+:} false; then :
$as_echo_n "(cached) " >&6
else
eval "$3=no"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
int
main ()
{
if (sizeof ($2))
return 0;
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
int
main ()
{
if (sizeof (($2)))
return 0;
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
else
eval "$3=yes"
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
eval ac_res=\$$3
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_type
cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by subversion $as_me 1.8.10, which was
+It was created by subversion $as_me 1.8.14, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
_ACEOF
exec 5>>config.log
{
cat <<_ASUNAME
## --------- ##
## Platform. ##
## --------- ##
hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
uname -m = `(uname -m) 2>/dev/null || echo unknown`
uname -r = `(uname -r) 2>/dev/null || echo unknown`
uname -s = `(uname -s) 2>/dev/null || echo unknown`
uname -v = `(uname -v) 2>/dev/null || echo unknown`
/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown`
/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
_ASUNAME
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
$as_echo "PATH: $as_dir"
done
IFS=$as_save_IFS
} >&5
cat >&5 <<_ACEOF
## ----------- ##
## Core tests. ##
## ----------- ##
_ACEOF
# Keep a trace of the command line.
# Strip out --no-create and --no-recursion so they do not pile up.
# Strip out --silent because we don't want to record it for future runs.
# Also quote any args containing shell meta-characters.
# Make two passes to allow for proper duplicate-argument suppression.
ac_configure_args=
ac_configure_args0=
ac_configure_args1=
ac_must_keep_next=false
for ac_pass in 1 2
do
for ac_arg
do
case $ac_arg in
-no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
-q | -quiet | --quiet | --quie | --qui | --qu | --q \
| -silent | --silent | --silen | --sile | --sil)
continue ;;
*\'*)
ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
esac
case $ac_pass in
1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
2)
as_fn_append ac_configure_args1 " '$ac_arg'"
if test $ac_must_keep_next = true; then
ac_must_keep_next=false # Got value, back to normal.
else
case $ac_arg in
*=* | --config-cache | -C | -disable-* | --disable-* \
| -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
| -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
| -with-* | --with-* | -without-* | --without-* | --x)
case "$ac_configure_args0 " in
"$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
esac
;;
-* ) ac_must_keep_next=true ;;
esac
fi
as_fn_append ac_configure_args " '$ac_arg'"
;;
esac
done
done
{ ac_configure_args0=; unset ac_configure_args0;}
{ ac_configure_args1=; unset ac_configure_args1;}
# When interrupted or exit'd, cleanup temporary files, and complete
# config.log. We remove comments because anyway the quotes in there
# would cause problems or look ugly.
# WARNING: Use '\'' to represent an apostrophe within the trap.
# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
trap 'exit_status=$?
# Save into config.log some information that might help in debugging.
{
echo
$as_echo "## ---------------- ##
## Cache variables. ##
## ---------------- ##"
echo
# The following way of writing the cache mishandles newlines in values,
(
for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
eval ac_val=\$$ac_var
case $ac_val in #(
*${as_nl}*)
case $ac_var in #(
*_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
esac
case $ac_var in #(
_ | IFS | as_nl) ;; #(
BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
*) { eval $ac_var=; unset $ac_var;} ;;
esac ;;
esac
done
(set) 2>&1 |
case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
*${as_nl}ac_space=\ *)
sed -n \
"s/'\''/'\''\\\\'\'''\''/g;
s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
;; #(
*)
sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
;;
esac |
sort
)
echo
$as_echo "## ----------------- ##
## Output variables. ##
## ----------------- ##"
echo
for ac_var in $ac_subst_vars
do
eval ac_val=\$$ac_var
case $ac_val in
*\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
esac
$as_echo "$ac_var='\''$ac_val'\''"
done | sort
echo
if test -n "$ac_subst_files"; then
$as_echo "## ------------------- ##
## File substitutions. ##
## ------------------- ##"
echo
for ac_var in $ac_subst_files
do
eval ac_val=\$$ac_var
case $ac_val in
*\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
esac
$as_echo "$ac_var='\''$ac_val'\''"
done | sort
echo
fi
if test -s confdefs.h; then
$as_echo "## ----------- ##
## confdefs.h. ##
## ----------- ##"
echo
cat confdefs.h
echo
fi
test "$ac_signal" != 0 &&
$as_echo "$as_me: caught signal $ac_signal"
$as_echo "$as_me: exit $exit_status"
} >&5
rm -f core *.core core.conftest.* &&
rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
exit $exit_status
' 0
for ac_signal in 1 2 13 15; do
trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
done
ac_signal=0
# confdefs.h avoids OS command line length limits that DEFS can exceed.
rm -f -r conftest* confdefs.h
$as_echo "/* confdefs.h */" > confdefs.h
# Predefined preprocessor variables.
cat >>confdefs.h <<_ACEOF
#define PACKAGE_NAME "$PACKAGE_NAME"
_ACEOF
cat >>confdefs.h <<_ACEOF
#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
_ACEOF
cat >>confdefs.h <<_ACEOF
#define PACKAGE_VERSION "$PACKAGE_VERSION"
_ACEOF
cat >>confdefs.h <<_ACEOF
#define PACKAGE_STRING "$PACKAGE_STRING"
_ACEOF
cat >>confdefs.h <<_ACEOF
#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
_ACEOF
cat >>confdefs.h <<_ACEOF
#define PACKAGE_URL "$PACKAGE_URL"
_ACEOF
# Let the site file select an alternate cache file if it wants to.
# Prefer an explicitly selected file to automatically selected ones.
ac_site_file1=NONE
ac_site_file2=NONE
if test -n "$CONFIG_SITE"; then
# We do not want a PATH search for config.site.
case $CONFIG_SITE in #((
-*) ac_site_file1=./$CONFIG_SITE;;
*/*) ac_site_file1=$CONFIG_SITE;;
*) ac_site_file1=./$CONFIG_SITE;;
esac
elif test "x$prefix" != xNONE; then
ac_site_file1=$prefix/share/config.site
ac_site_file2=$prefix/etc/config.site
else
ac_site_file1=$ac_default_prefix/share/config.site
ac_site_file2=$ac_default_prefix/etc/config.site
fi
for ac_site_file in "$ac_site_file1" "$ac_site_file2"
do
test "x$ac_site_file" = xNONE && continue
if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
$as_echo "$as_me: loading site script $ac_site_file" >&6;}
sed 's/^/| /' "$ac_site_file" >&5
. "$ac_site_file" \
|| { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "failed to load site script $ac_site_file
See \`config.log' for more details" "$LINENO" 5; }
fi
done
if test -r "$cache_file"; then
# Some versions of bash will fail to source /dev/null (special files
# actually), so we avoid doing that. DJGPP emulates it as a regular file.
if test /dev/null != "$cache_file" && test -f "$cache_file"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
$as_echo "$as_me: loading cache $cache_file" >&6;}
case $cache_file in
[\\/]* | ?:[\\/]* ) . "$cache_file";;
*) . "./$cache_file";;
esac
fi
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
$as_echo "$as_me: creating cache $cache_file" >&6;}
>$cache_file
fi
# Check that the precious variables saved in the cache have kept the same
# value.
ac_cache_corrupted=false
for ac_var in $ac_precious_vars; do
eval ac_old_set=\$ac_cv_env_${ac_var}_set
eval ac_new_set=\$ac_env_${ac_var}_set
eval ac_old_val=\$ac_cv_env_${ac_var}_value
eval ac_new_val=\$ac_env_${ac_var}_value
case $ac_old_set,$ac_new_set in
set,)
{ $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
ac_cache_corrupted=: ;;
,set)
{ $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
ac_cache_corrupted=: ;;
,);;
*)
if test "x$ac_old_val" != "x$ac_new_val"; then
# differences in whitespace do not lead to failure.
ac_old_val_w=`echo x $ac_old_val`
ac_new_val_w=`echo x $ac_new_val`
if test "$ac_old_val_w" != "$ac_new_val_w"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
ac_cache_corrupted=:
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
eval $ac_var=\$ac_old_val
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5
$as_echo "$as_me: former value: \`$ac_old_val'" >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5
$as_echo "$as_me: current value: \`$ac_new_val'" >&2;}
fi;;
esac
# Pass precious variables to config.status.
if test "$ac_new_set" = set; then
case $ac_new_val in
*\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
*) ac_arg=$ac_var=$ac_new_val ;;
esac
case " $ac_configure_args " in
*" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
*) as_fn_append ac_configure_args " '$ac_arg'" ;;
esac
fi
done
if $ac_cache_corrupted; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
fi
## -------------------- ##
## Main body of script. ##
## -------------------- ##
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
ac_aux_dir=
for ac_dir in build "$srcdir"/build; do
if test -f "$ac_dir/install-sh"; then
ac_aux_dir=$ac_dir
ac_install_sh="$ac_aux_dir/install-sh -c"
break
elif test -f "$ac_dir/install.sh"; then
ac_aux_dir=$ac_dir
ac_install_sh="$ac_aux_dir/install.sh -c"
break
elif test -f "$ac_dir/shtool"; then
ac_aux_dir=$ac_dir
ac_install_sh="$ac_aux_dir/shtool install -c"
break
fi
done
if test -z "$ac_aux_dir"; then
as_fn_error $? "cannot find install-sh, install.sh, or shtool in build \"$srcdir\"/build" "$LINENO" 5
fi
# These three variables are undocumented and unsupported,
# and are intended to be withdrawn in a future Autoconf release.
# They can cause serious problems if a builder's source tree is in a directory
# whose full name contains unusual characters.
ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var.
ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var.
ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
-{ $as_echo "$as_me:${as_lineno-$LINENO}: Configuring Subversion 1.8.10" >&5
-$as_echo "$as_me: Configuring Subversion 1.8.10" >&6;}
+{ $as_echo "$as_me:${as_lineno-$LINENO}: Configuring Subversion 1.8.14" >&5
+$as_echo "$as_me: Configuring Subversion 1.8.14" >&6;}
abs_srcdir="`cd $srcdir && pwd`"
abs_builddir="`pwd`"
if test "$abs_srcdir" = "$abs_builddir"; then
canonicalized_srcdir=""
else
canonicalized_srcdir="$srcdir/"
fi
SWIG_LDFLAGS="$LDFLAGS"
# Generate config.nice early (before the arguments are munged)
{ $as_echo "$as_me:${as_lineno-$LINENO}: creating config.nice" >&5
$as_echo "$as_me: creating config.nice" >&6;}
# This little dance satisfies Cygwin, which cannot overwrite in-use files.
if test -f "config.nice"; then
mv "config.nice" "config.nice.old"
fi
cat >"config.nice" <<EOF
#! /bin/sh
#
# Created by configure
'$0' $ac_configure_args "\$@"
EOF
chmod +x "config.nice"
rm -f "config.nice.old"
# ==== Check for programs ====================================================
# Look for a C compiler (before anything can set CFLAGS)
CMAINTAINERFLAGS="$CUSERFLAGS"
CUSERFLAGS="$CFLAGS"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
set dummy ${ac_tool_prefix}gcc; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$CC"; then
ac_cv_prog_CC="$CC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_CC="${ac_tool_prefix}gcc"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
$as_echo "$CC" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$ac_cv_prog_CC"; then
ac_ct_CC=$CC
# Extract the first word of "gcc", so it can be a program name with args.
set dummy gcc; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_CC"; then
ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_CC="gcc"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_CC=$ac_cv_prog_ac_ct_CC
if test -n "$ac_ct_CC"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
$as_echo "$ac_ct_CC" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "x$ac_ct_CC" = x; then
CC=""
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
CC=$ac_ct_CC
fi
else
CC="$ac_cv_prog_CC"
fi
if test -z "$CC"; then
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
set dummy ${ac_tool_prefix}cc; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$CC"; then
ac_cv_prog_CC="$CC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_CC="${ac_tool_prefix}cc"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
$as_echo "$CC" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
fi
if test -z "$CC"; then
# Extract the first word of "cc", so it can be a program name with args.
set dummy cc; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$CC"; then
ac_cv_prog_CC="$CC" # Let the user override the test.
else
ac_prog_rejected=no
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
ac_prog_rejected=yes
continue
fi
ac_cv_prog_CC="cc"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
if test $ac_prog_rejected = yes; then
# We found a bogon in the path, so make sure we never use it.
set dummy $ac_cv_prog_CC
shift
if test $# != 0; then
# We chose a different compiler from the bogus one.
# However, it has the same basename, so the bogon will be chosen
# first if we set CC to just the basename; use the full file name.
shift
ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
fi
fi
fi
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
$as_echo "$CC" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$CC"; then
if test -n "$ac_tool_prefix"; then
for ac_prog in cl.exe
do
# Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
set dummy $ac_tool_prefix$ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$CC"; then
ac_cv_prog_CC="$CC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
$as_echo "$CC" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
test -n "$CC" && break
done
fi
if test -z "$CC"; then
ac_ct_CC=$CC
for ac_prog in cl.exe
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_CC"; then
ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_CC="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_CC=$ac_cv_prog_ac_ct_CC
if test -n "$ac_ct_CC"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
$as_echo "$ac_ct_CC" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
test -n "$ac_ct_CC" && break
done
if test "x$ac_ct_CC" = x; then
CC=""
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
CC=$ac_ct_CC
fi
fi
fi
test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "no acceptable C compiler found in \$PATH
See \`config.log' for more details" "$LINENO" 5; }
# Provide some information about the compiler.
$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
set X $ac_compile
ac_compiler=$2
for ac_option in --version -v -V -qversion; do
{ { ac_try="$ac_compiler $ac_option >&5"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
(eval "$ac_compiler $ac_option >&5") 2>conftest.err
ac_status=$?
if test -s conftest.err; then
sed '10a\
... rest of stderr output deleted ...
10q' conftest.err >conftest.er1
cat conftest.er1 >&5
fi
rm -f conftest.er1 conftest.err
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }
done
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
ac_clean_files_save=$ac_clean_files
ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
# Try to create an executable without -o first, disregard a.out.
# It will help us diagnose broken compilers, and finding out an intuition
# of exeext.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
$as_echo_n "checking whether the C compiler works... " >&6; }
ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
# The possible output files:
ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
ac_rmfiles=
for ac_file in $ac_files
do
case $ac_file in
*.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
* ) ac_rmfiles="$ac_rmfiles $ac_file";;
esac
done
rm -f $ac_rmfiles
if { { ac_try="$ac_link_default"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
(eval "$ac_link_default") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then :
# Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
# in a Makefile. We should not override ac_cv_exeext if it was cached,
# so that the user can short-circuit this test for compilers unknown to
# Autoconf.
for ac_file in $ac_files ''
do
test -f "$ac_file" || continue
case $ac_file in
*.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
;;
[ab].out )
# We found the default executable, but exeext='' is most
# certainly right.
break;;
*.* )
if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
then :; else
ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
fi
# We set ac_cv_exeext here because the later test for it is not
# safe: cross compilers may not add the suffix if given an `-o'
# argument, so we may need to know it at that point already.
# Even if this section looks crufty: it has the advantage of
# actually working.
break;;
* )
break;;
esac
done
test "$ac_cv_exeext" = no && ac_cv_exeext=
else
ac_file=''
fi
if test -z "$ac_file"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
$as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error 77 "C compiler cannot create executables
See \`config.log' for more details" "$LINENO" 5; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
$as_echo_n "checking for C compiler default output file name... " >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
$as_echo "$ac_file" >&6; }
ac_exeext=$ac_cv_exeext
rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
ac_clean_files=$ac_clean_files_save
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
$as_echo_n "checking for suffix of executables... " >&6; }
if { { ac_try="$ac_link"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
(eval "$ac_link") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then :
# If both `conftest.exe' and `conftest' are `present' (well, observable)
# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
# work properly (i.e., refer to `conftest.exe'), while it won't with
# `rm'.
for ac_file in conftest.exe conftest conftest.*; do
test -f "$ac_file" || continue
case $ac_file in
*.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
*.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
break;;
* ) break;;
esac
done
else
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "cannot compute suffix of executables: cannot compile and link
See \`config.log' for more details" "$LINENO" 5; }
fi
rm -f conftest conftest$ac_cv_exeext
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
$as_echo "$ac_cv_exeext" >&6; }
rm -f conftest.$ac_ext
EXEEXT=$ac_cv_exeext
ac_exeext=$EXEEXT
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdio.h>
int
main ()
{
FILE *f = fopen ("conftest.out", "w");
return ferror (f) || fclose (f) != 0;
;
return 0;
}
_ACEOF
ac_clean_files="$ac_clean_files conftest.out"
# Check that the compiler produces executables we can run. If not, either
# the compiler is broken, or we cross compile.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
$as_echo_n "checking whether we are cross compiling... " >&6; }
if test "$cross_compiling" != yes; then
{ { ac_try="$ac_link"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
(eval "$ac_link") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }
if { ac_try='./conftest$ac_cv_exeext'
{ { case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
(eval "$ac_try") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; }; then
cross_compiling=no
else
if test "$cross_compiling" = maybe; then
cross_compiling=yes
else
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "cannot run C compiled programs.
If you meant to cross compile, use \`--host'.
See \`config.log' for more details" "$LINENO" 5; }
fi
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
$as_echo "$cross_compiling" >&6; }
rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
ac_clean_files=$ac_clean_files_save
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
$as_echo_n "checking for suffix of object files... " >&6; }
if ${ac_cv_objext+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
rm -f conftest.o conftest.obj
if { { ac_try="$ac_compile"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
(eval "$ac_compile") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then :
for ac_file in conftest.o conftest.obj conftest.*; do
test -f "$ac_file" || continue;
case $ac_file in
*.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
*) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
break;;
esac
done
else
$as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "cannot compute suffix of object files: cannot compile
See \`config.log' for more details" "$LINENO" 5; }
fi
rm -f conftest.$ac_cv_objext conftest.$ac_ext
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
$as_echo "$ac_cv_objext" >&6; }
OBJEXT=$ac_cv_objext
ac_objext=$OBJEXT
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
if ${ac_cv_c_compiler_gnu+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
#ifndef __GNUC__
choke me
#endif
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ac_compiler_gnu=yes
else
ac_compiler_gnu=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
ac_cv_c_compiler_gnu=$ac_compiler_gnu
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
$as_echo "$ac_cv_c_compiler_gnu" >&6; }
if test $ac_compiler_gnu = yes; then
GCC=yes
else
GCC=
fi
ac_test_CFLAGS=${CFLAGS+set}
ac_save_CFLAGS=$CFLAGS
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
$as_echo_n "checking whether $CC accepts -g... " >&6; }
if ${ac_cv_prog_cc_g+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_save_c_werror_flag=$ac_c_werror_flag
ac_c_werror_flag=yes
ac_cv_prog_cc_g=no
CFLAGS="-g"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ac_cv_prog_cc_g=yes
else
CFLAGS=""
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
else
ac_c_werror_flag=$ac_save_c_werror_flag
CFLAGS="-g"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ac_cv_prog_cc_g=yes
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
ac_c_werror_flag=$ac_save_c_werror_flag
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
$as_echo "$ac_cv_prog_cc_g" >&6; }
if test "$ac_test_CFLAGS" = set; then
CFLAGS=$ac_save_CFLAGS
elif test $ac_cv_prog_cc_g = yes; then
if test "$GCC" = yes; then
CFLAGS="-g -O2"
else
CFLAGS="-g"
fi
else
if test "$GCC" = yes; then
CFLAGS="-O2"
else
CFLAGS=
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
if ${ac_cv_prog_cc_c89+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_prog_cc_c89=no
ac_save_CC=$CC
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdarg.h>
#include <stdio.h>
struct stat;
/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
struct buf { int x; };
FILE * (*rcsopen) (struct buf *, struct stat *, int);
static char *e (p, i)
char **p;
int i;
{
return p[i];
}
static char *f (char * (*g) (char **, int), char **p, ...)
{
char *s;
va_list v;
va_start (v,p);
s = g (p, va_arg (v,int));
va_end (v);
return s;
}
/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
function prototypes and stuff, but not '\xHH' hex character constants.
These don't provoke an error unfortunately, instead are silently treated
as 'x'. The following induces an error, until -std is added to get
proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
array size at least. It's necessary to write '\x00'==0 to get something
that's true only with -std. */
int osf4_cc_array ['\x00' == 0 ? 1 : -1];
/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
inside strings and character constants. */
#define FOO(x) 'x'
int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
int test (int i, double x);
struct s1 {int (*f) (int a);};
struct s2 {int (*f) (double a);};
int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
int argc;
char **argv;
int
main ()
{
return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
;
return 0;
}
_ACEOF
for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
-Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
do
CC="$ac_save_CC $ac_arg"
if ac_fn_c_try_compile "$LINENO"; then :
ac_cv_prog_cc_c89=$ac_arg
fi
rm -f core conftest.err conftest.$ac_objext
test "x$ac_cv_prog_cc_c89" != "xno" && break
done
rm -f conftest.$ac_ext
CC=$ac_save_CC
fi
# AC_CACHE_VAL
case "x$ac_cv_prog_cc_c89" in
x)
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
$as_echo "none needed" >&6; } ;;
xno)
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
$as_echo "unsupported" >&6; } ;;
*)
CC="$CC $ac_cv_prog_cc_c89"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
esac
if test "x$ac_cv_prog_cc_c89" != xno; then :
fi
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
CFLAGS_KEEP="$CFLAGS"
CFLAGS=""
_svn_xxflags__save="$CFLAGS"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC accepts -std=c90" >&5
$as_echo_n "checking if $CC accepts -std=c90... " >&6; }
CFLAGS="-std=c90 $CFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(void){return 0;}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CFLAGS="$_svn_xxflags__save"
_svn_xxflags__save="$CFLAGS"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC accepts -std=c89" >&5
$as_echo_n "checking if $CC accepts -std=c89... " >&6; }
CFLAGS="-std=c89 $CFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(void){return 0;}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CFLAGS="$_svn_xxflags__save"
_svn_xxflags__save="$CFLAGS"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC accepts -ansi" >&5
$as_echo_n "checking if $CC accepts -ansi... " >&6; }
CFLAGS="-ansi $CFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(void){return 0;}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
CMODEFLAGS="$CFLAGS"
CFLAGS="$CFLAGS_KEEP"
_svn_xxflags__save="$CFLAGS"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC accepts -Werror=unknown-warning-option" >&5
$as_echo_n "checking if $CC accepts -Werror=unknown-warning-option... " >&6; }
CFLAGS="-Werror=unknown-warning-option $CFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(void){return 0;}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
# Look for a C++ compiler (before anything can set CXXFLAGS)
CXXMAINTAINERFLAGS="$CXXUSERFLAGS"
CXXUSERFLAGS="$CXXFLAGS"
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
if test -z "$CXX"; then
if test -n "$CCC"; then
CXX=$CCC
else
if test -n "$ac_tool_prefix"; then
for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC
do
# Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
set dummy $ac_tool_prefix$ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_CXX+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$CXX"; then
ac_cv_prog_CXX="$CXX" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
CXX=$ac_cv_prog_CXX
if test -n "$CXX"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5
$as_echo "$CXX" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
test -n "$CXX" && break
done
fi
if test -z "$CXX"; then
ac_ct_CXX=$CXX
for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_CXX+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_CXX"; then
ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_CXX="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
if test -n "$ac_ct_CXX"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5
$as_echo "$ac_ct_CXX" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
test -n "$ac_ct_CXX" && break
done
if test "x$ac_ct_CXX" = x; then
CXX="g++"
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
CXX=$ac_ct_CXX
fi
fi
fi
fi
# Provide some information about the compiler.
$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5
set X $ac_compile
ac_compiler=$2
for ac_option in --version -v -V -qversion; do
{ { ac_try="$ac_compiler $ac_option >&5"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
(eval "$ac_compiler $ac_option >&5") 2>conftest.err
ac_status=$?
if test -s conftest.err; then
sed '10a\
... rest of stderr output deleted ...
10q' conftest.err >conftest.er1
cat conftest.er1 >&5
fi
rm -f conftest.er1 conftest.err
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }
done
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5
$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; }
if ${ac_cv_cxx_compiler_gnu+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
#ifndef __GNUC__
choke me
#endif
;
return 0;
}
_ACEOF
if ac_fn_cxx_try_compile "$LINENO"; then :
ac_compiler_gnu=yes
else
ac_compiler_gnu=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
ac_cv_cxx_compiler_gnu=$ac_compiler_gnu
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5
$as_echo "$ac_cv_cxx_compiler_gnu" >&6; }
if test $ac_compiler_gnu = yes; then
GXX=yes
else
GXX=
fi
ac_test_CXXFLAGS=${CXXFLAGS+set}
ac_save_CXXFLAGS=$CXXFLAGS
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5
$as_echo_n "checking whether $CXX accepts -g... " >&6; }
if ${ac_cv_prog_cxx_g+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_save_cxx_werror_flag=$ac_cxx_werror_flag
ac_cxx_werror_flag=yes
ac_cv_prog_cxx_g=no
CXXFLAGS="-g"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_cxx_try_compile "$LINENO"; then :
ac_cv_prog_cxx_g=yes
else
CXXFLAGS=""
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_cxx_try_compile "$LINENO"; then :
else
ac_cxx_werror_flag=$ac_save_cxx_werror_flag
CXXFLAGS="-g"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_cxx_try_compile "$LINENO"; then :
ac_cv_prog_cxx_g=yes
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
ac_cxx_werror_flag=$ac_save_cxx_werror_flag
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5
$as_echo "$ac_cv_prog_cxx_g" >&6; }
if test "$ac_test_CXXFLAGS" = set; then
CXXFLAGS=$ac_save_CXXFLAGS
elif test $ac_cv_prog_cxx_g = yes; then
if test "$GXX" = yes; then
CXXFLAGS="-g -O2"
else
CXXFLAGS="-g"
fi
else
if test "$GXX" = yes; then
CXXFLAGS="-O2"
else
CXXFLAGS=
fi
fi
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
CXXFLAGS_KEEP="$CXXFLAGS"
CXXFLAGS=""
_svn_xxflags__save="$CXXFLAGS"
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CXX accepts -std=c++98" >&5
$as_echo_n "checking if $CXX accepts -std=c++98... " >&6; }
CXXFLAGS="-std=c++98 $CXXFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(){}
_ACEOF
if ac_fn_cxx_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CXXFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
CXXMODEFLAGS="$CXXFLAGS"
CXXFLAGS="$CXXFLAGS_KEEP"
_svn_xxflags__save="$CXXFLAGS"
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CXX accepts -Werror=unknown-warning-option" >&5
$as_echo_n "checking if $CXX accepts -Werror=unknown-warning-option... " >&6; }
CXXFLAGS="-Werror=unknown-warning-option $CXXFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(){}
_ACEOF
if ac_fn_cxx_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CXXFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
# Look for a C pre-processor
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
$as_echo_n "checking how to run the C preprocessor... " >&6; }
# On Suns, sometimes $CPP names a directory.
if test -n "$CPP" && test -d "$CPP"; then
CPP=
fi
if test -z "$CPP"; then
if ${ac_cv_prog_CPP+:} false; then :
$as_echo_n "(cached) " >&6
else
# Double quotes because CPP needs to be expanded
for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
do
ac_preproc_ok=false
for ac_c_preproc_warn_flag in '' yes
do
# Use a header file that comes with gcc, so configuring glibc
# with a fresh cross-compiler works.
# Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
# <limits.h> exists even on freestanding compilers.
# On the NeXT, cc -E runs the code through the compiler's parser,
# not just through cpp. "Syntax error" is here to catch this case.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#ifdef __STDC__
# include <limits.h>
#else
# include <assert.h>
#endif
Syntax error
_ACEOF
if ac_fn_c_try_cpp "$LINENO"; then :
else
# Broken: fails on valid input.
continue
fi
rm -f conftest.err conftest.i conftest.$ac_ext
# OK, works on sane cases. Now check whether nonexistent headers
# can be detected and how.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <ac_nonexistent.h>
_ACEOF
if ac_fn_c_try_cpp "$LINENO"; then :
# Broken: success on invalid input.
continue
else
# Passes both tests.
ac_preproc_ok=:
break
fi
rm -f conftest.err conftest.i conftest.$ac_ext
done
# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
rm -f conftest.i conftest.err conftest.$ac_ext
if $ac_preproc_ok; then :
break
fi
done
ac_cv_prog_CPP=$CPP
fi
CPP=$ac_cv_prog_CPP
else
ac_cv_prog_CPP=$CPP
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
$as_echo "$CPP" >&6; }
ac_preproc_ok=false
for ac_c_preproc_warn_flag in '' yes
do
# Use a header file that comes with gcc, so configuring glibc
# with a fresh cross-compiler works.
# Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
# <limits.h> exists even on freestanding compilers.
# On the NeXT, cc -E runs the code through the compiler's parser,
# not just through cpp. "Syntax error" is here to catch this case.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#ifdef __STDC__
# include <limits.h>
#else
# include <assert.h>
#endif
Syntax error
_ACEOF
if ac_fn_c_try_cpp "$LINENO"; then :
else
# Broken: fails on valid input.
continue
fi
rm -f conftest.err conftest.i conftest.$ac_ext
# OK, works on sane cases. Now check whether nonexistent headers
# can be detected and how.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <ac_nonexistent.h>
_ACEOF
if ac_fn_c_try_cpp "$LINENO"; then :
# Broken: success on invalid input.
continue
else
# Passes both tests.
ac_preproc_ok=:
break
fi
rm -f conftest.err conftest.i conftest.$ac_ext
done
# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
rm -f conftest.i conftest.err conftest.$ac_ext
if $ac_preproc_ok; then :
else
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
See \`config.log' for more details" "$LINENO" 5; }
fi
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
# Look for a good sed
# AC_PROG_SED was introduced in Autoconf 2.59b
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5
$as_echo_n "checking for a sed that does not truncate output... " >&6; }
if ${ac_cv_path_SED+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
for ac_i in 1 2 3 4 5 6 7; do
ac_script="$ac_script$as_nl$ac_script"
done
echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed
{ ac_script=; unset ac_script;}
if test -z "$SED"; then
ac_path_SED_found=false
# Loop through the user's path and test for each of PROGNAME-LIST
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_prog in sed gsed; do
for ac_exec_ext in '' $ac_executable_extensions; do
ac_path_SED="$as_dir/$ac_prog$ac_exec_ext"
as_fn_executable_p "$ac_path_SED" || continue
# Check for GNU ac_path_SED and select it if it is found.
# Check for GNU $ac_path_SED
case `"$ac_path_SED" --version 2>&1` in
*GNU*)
ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;;
*)
ac_count=0
$as_echo_n 0123456789 >"conftest.in"
while :
do
cat "conftest.in" "conftest.in" >"conftest.tmp"
mv "conftest.tmp" "conftest.in"
cp "conftest.in" "conftest.nl"
$as_echo '' >> "conftest.nl"
"$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break
diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
as_fn_arith $ac_count + 1 && ac_count=$as_val
if test $ac_count -gt ${ac_path_SED_max-0}; then
# Best one so far, save it but keep looking for a better one
ac_cv_path_SED="$ac_path_SED"
ac_path_SED_max=$ac_count
fi
# 10*(2^10) chars as input seems more than enough
test $ac_count -gt 10 && break
done
rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
esac
$ac_path_SED_found && break 3
done
done
done
IFS=$as_save_IFS
if test -z "$ac_cv_path_SED"; then
as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5
fi
else
ac_cv_path_SED=$SED
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5
$as_echo "$ac_cv_path_SED" >&6; }
SED="$ac_cv_path_SED"
rm -f conftest.sed
# Grab target_cpu, so we can use it in the Solaris pkginfo file
# Make sure we can run config.sub.
$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
$as_echo_n "checking build system type... " >&6; }
if ${ac_cv_build+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_build_alias=$build_alias
test "x$ac_build_alias" = x &&
ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
test "x$ac_build_alias" = x &&
as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5
ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
$as_echo "$ac_cv_build" >&6; }
case $ac_cv_build in
*-*-*) ;;
*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;;
esac
build=$ac_cv_build
ac_save_IFS=$IFS; IFS='-'
set x $ac_cv_build
shift
build_cpu=$1
build_vendor=$2
shift; shift
# Remember, the first character of IFS is used to create $*,
# except with old shells:
build_os=$*
IFS=$ac_save_IFS
case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
$as_echo_n "checking host system type... " >&6; }
if ${ac_cv_host+:} false; then :
$as_echo_n "(cached) " >&6
else
if test "x$host_alias" = x; then
ac_cv_host=$ac_cv_build
else
ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
$as_echo "$ac_cv_host" >&6; }
case $ac_cv_host in
*-*-*) ;;
*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;;
esac
host=$ac_cv_host
ac_save_IFS=$IFS; IFS='-'
set x $ac_cv_host
shift
host_cpu=$1
host_vendor=$2
shift; shift
# Remember, the first character of IFS is used to create $*,
# except with old shells:
host_os=$*
IFS=$ac_save_IFS
case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking target system type" >&5
$as_echo_n "checking target system type... " >&6; }
if ${ac_cv_target+:} false; then :
$as_echo_n "(cached) " >&6
else
if test "x$target_alias" = x; then
ac_cv_target=$ac_cv_host
else
ac_cv_target=`$SHELL "$ac_aux_dir/config.sub" $target_alias` ||
as_fn_error $? "$SHELL $ac_aux_dir/config.sub $target_alias failed" "$LINENO" 5
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_target" >&5
$as_echo "$ac_cv_target" >&6; }
case $ac_cv_target in
*-*-*) ;;
*) as_fn_error $? "invalid value of canonical target" "$LINENO" 5;;
esac
target=$ac_cv_target
ac_save_IFS=$IFS; IFS='-'
set x $ac_cv_target
shift
target_cpu=$1
target_vendor=$2
shift; shift
# Remember, the first character of IFS is used to create $*,
# except with old shells:
target_os=$*
IFS=$ac_save_IFS
case $target_os in *\ *) target_os=`echo "$target_os" | sed 's/ /-/g'`;; esac
# The aliases save the names the user supplied, while $host etc.
# will get canonicalized.
test -n "$target_alias" &&
test "$program_prefix$program_suffix$program_transform_name" = \
NONENONEs,x,x, &&
program_prefix=${target_alias}-
# Look for an extended grep
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
if ${ac_cv_path_GREP+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -z "$GREP"; then
ac_path_GREP_found=false
# Loop through the user's path and test for each of PROGNAME-LIST
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_prog in grep ggrep; do
for ac_exec_ext in '' $ac_executable_extensions; do
ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
as_fn_executable_p "$ac_path_GREP" || continue
# Check for GNU ac_path_GREP and select it if it is found.
# Check for GNU $ac_path_GREP
case `"$ac_path_GREP" --version 2>&1` in
*GNU*)
ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
*)
ac_count=0
$as_echo_n 0123456789 >"conftest.in"
while :
do
cat "conftest.in" "conftest.in" >"conftest.tmp"
mv "conftest.tmp" "conftest.in"
cp "conftest.in" "conftest.nl"
$as_echo 'GREP' >> "conftest.nl"
"$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
as_fn_arith $ac_count + 1 && ac_count=$as_val
if test $ac_count -gt ${ac_path_GREP_max-0}; then
# Best one so far, save it but keep looking for a better one
ac_cv_path_GREP="$ac_path_GREP"
ac_path_GREP_max=$ac_count
fi
# 10*(2^10) chars as input seems more than enough
test $ac_count -gt 10 && break
done
rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
esac
$ac_path_GREP_found && break 3
done
done
done
IFS=$as_save_IFS
if test -z "$ac_cv_path_GREP"; then
as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
fi
else
ac_cv_path_GREP=$GREP
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
$as_echo "$ac_cv_path_GREP" >&6; }
GREP="$ac_cv_path_GREP"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
$as_echo_n "checking for egrep... " >&6; }
if ${ac_cv_path_EGREP+:} false; then :
$as_echo_n "(cached) " >&6
else
if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
then ac_cv_path_EGREP="$GREP -E"
else
if test -z "$EGREP"; then
ac_path_EGREP_found=false
# Loop through the user's path and test for each of PROGNAME-LIST
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_prog in egrep; do
for ac_exec_ext in '' $ac_executable_extensions; do
ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
as_fn_executable_p "$ac_path_EGREP" || continue
# Check for GNU ac_path_EGREP and select it if it is found.
# Check for GNU $ac_path_EGREP
case `"$ac_path_EGREP" --version 2>&1` in
*GNU*)
ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
*)
ac_count=0
$as_echo_n 0123456789 >"conftest.in"
while :
do
cat "conftest.in" "conftest.in" >"conftest.tmp"
mv "conftest.tmp" "conftest.in"
cp "conftest.in" "conftest.nl"
$as_echo 'EGREP' >> "conftest.nl"
"$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
as_fn_arith $ac_count + 1 && ac_count=$as_val
if test $ac_count -gt ${ac_path_EGREP_max-0}; then
# Best one so far, save it but keep looking for a better one
ac_cv_path_EGREP="$ac_path_EGREP"
ac_path_EGREP_max=$ac_count
fi
# 10*(2^10) chars as input seems more than enough
test $ac_count -gt 10 && break
done
rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
esac
$ac_path_EGREP_found && break 3
done
done
done
IFS=$as_save_IFS
if test -z "$ac_cv_path_EGREP"; then
as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
fi
else
ac_cv_path_EGREP=$EGREP
fi
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
$as_echo "$ac_cv_path_EGREP" >&6; }
EGREP="$ac_cv_path_EGREP"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5
$as_echo_n "checking whether ln -s works... " >&6; }
LN_S=$as_ln_s
if test "$LN_S" = "ln -s"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5
$as_echo "no, using $LN_S" >&6; }
fi
# Find a good install program. We prefer a C program (faster),
# so one script is as good as another. But avoid the broken or
# incompatible versions:
# SysV /etc/install, /usr/sbin/install
# SunOS /usr/etc/install
# IRIX /sbin/install
# AIX /bin/install
# AmigaOS /C/install, which installs bootblocks on floppy discs
# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
# AFS /usr/afsws/bin/install, which mishandles nonexistent args
# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
# OS/2's system install, which has a completely different semantic
# ./install, which can be erroneously created by make from ./install.sh.
# Reject install programs that cannot install multiple files.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
$as_echo_n "checking for a BSD-compatible install... " >&6; }
if test -z "$INSTALL"; then
if ${ac_cv_path_install+:} false; then :
$as_echo_n "(cached) " >&6
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
# Account for people who put trailing slashes in PATH elements.
case $as_dir/ in #((
./ | .// | /[cC]/* | \
/etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \
/usr/ucb/* ) ;;
*)
# OSF1 and SCO ODT 3.0 have their own names for install.
# Don't use installbsd from OSF since it installs stuff as root
# by default.
for ac_prog in ginstall scoinst install; do
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
if test $ac_prog = install &&
grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
# AIX install. It has an incompatible calling convention.
:
elif test $ac_prog = install &&
grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
# program-specific install script used by HP pwplus--don't use.
:
else
rm -rf conftest.one conftest.two conftest.dir
echo one > conftest.one
echo two > conftest.two
mkdir conftest.dir
if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
test -s conftest.one && test -s conftest.two &&
test -s conftest.dir/conftest.one &&
test -s conftest.dir/conftest.two
then
ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
break 3
fi
fi
fi
done
done
;;
esac
done
IFS=$as_save_IFS
rm -rf conftest.one conftest.two conftest.dir
fi
if test "${ac_cv_path_install+set}" = set; then
INSTALL=$ac_cv_path_install
else
# As a last resort, use the slow shell script. Don't cache a
# value for INSTALL within a source directory, because that will
# break other packages using the cache if that directory is
# removed, or if the value is a relative name.
INSTALL=$ac_install_sh
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5
$as_echo "$INSTALL" >&6; }
# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
# It thinks the first close brace ends the variable substitution.
test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
# If $INSTALL is relative path to our fallback install-sh, then convert
# to an absolute path, as in some cases (e.g. Solaris VPATH build), libtool
# may try to use it from a changed working directory.
if test "$INSTALL" = "build/install-sh -c"; then
INSTALL="$abs_srcdir/$INSTALL"
fi
if test -z "$MKDIR"; then
MKDIR="$INSTALL -d"
fi
# ==== Libraries, for which we may have source to build ======================
APR_VER_REGEXES="0\.9\.[7-9] 0\.9\.[12][0-9] 1\. 2\."
APR_WANTED_REGEXES="$APR_VER_REGEXES"
{ $as_echo "$as_me:${as_lineno-$LINENO}: Apache Portable Runtime (APR) library configuration" >&5
$as_echo "$as_me: Apache Portable Runtime (APR) library configuration" >&6;}
apr_found="no"
if test "$ac_cv_emxos2" = "yes"; then
# Scripts don't pass test -x on OS/2
TEST_X="test -f"
else
TEST_X="test -x"
fi
acceptable_majors="1 0"
apr_temp_acceptable_apr_config=""
for apr_temp_major in $acceptable_majors
do
case $apr_temp_major in
0)
apr_temp_acceptable_apr_config="$apr_temp_acceptable_apr_config apr-config"
;;
*)
apr_temp_acceptable_apr_config="$apr_temp_acceptable_apr_config apr-$apr_temp_major-config"
;;
esac
done
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for APR" >&5
$as_echo_n "checking for APR... " >&6; }
# Check whether --with-apr was given.
if test "${with_apr+set}" = set; then :
withval=$with_apr;
if test "$withval" = "no" || test "$withval" = "yes"; then
as_fn_error $? "--with-apr requires a directory or file to be provided" "$LINENO" 5
fi
for apr_temp_apr_config_file in $apr_temp_acceptable_apr_config
do
for lookdir in "$withval/bin" "$withval"
do
if $TEST_X "$lookdir/$apr_temp_apr_config_file"; then
apr_found="yes"
apr_config="$lookdir/$apr_temp_apr_config_file"
break 2
fi
done
done
if test "$apr_found" != "yes" && $TEST_X "$withval" && $withval --help > /dev/null 2>&1 ; then
apr_found="yes"
apr_config="$withval"
fi
if test "$apr_found" != "yes"; then
as_fn_error $? "the --with-apr parameter is incorrect. It must specify an install prefix, a build directory, or an apr-config file." "$LINENO" 5
fi
else
if test -d """"; then
apr_temp_abs_srcdir="`cd "" && pwd`"
apr_found="reconfig"
apr_bundled_major="`sed -n '/#define.*APR_MAJOR_VERSION/s/^[^0-9]*\([0-9]*\).*$/\1/p' \"""/include/apr_version.h\"`"
case $apr_bundled_major in
"")
as_fn_error $? "failed to find major version of bundled APR" "$LINENO" 5
;;
0)
apr_temp_apr_config_file="apr-config"
;;
*)
apr_temp_apr_config_file="apr-$apr_bundled_major-config"
;;
esac
if test -n """"; then
apr_config="""/$apr_temp_apr_config_file"
else
apr_config="""/$apr_temp_apr_config_file"
fi
fi
if test "$apr_found" = "no" && test -n "1" && test "1" = "1"; then
for apr_temp_apr_config_file in $apr_temp_acceptable_apr_config
do
if $apr_temp_apr_config_file --help > /dev/null 2>&1 ; then
apr_found="yes"
apr_config="$apr_temp_apr_config_file"
break
else
for lookdir in /usr /usr/local /opt/apr /usr/local/apache2 ; do
if $TEST_X "$lookdir/bin/$apr_temp_apr_config_file"; then
apr_found="yes"
apr_config="$lookdir/bin/$apr_temp_apr_config_file"
break 2
fi
done
fi
done
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $apr_found" >&5
$as_echo "$apr_found" >&6; }
if test $apr_found = "no"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: APR not found" >&5
$as_echo "$as_me: WARNING: APR not found" >&2;}
echo "The Apache Portable Runtime (APR) library cannot be found."
echo "Please install APR on this system and configure Subversion"
echo "with the appropriate --with-apr option."
echo ""
echo "You probably need to do something similar with the Apache"
echo "Portable Runtime Utility (APRUTIL) library and then configure"
echo "Subversion with both the --with-apr and --with-apr-util options."
echo ""
as_fn_error $? "no suitable APR found" "$LINENO" 5
fi
if test $apr_found = "reconfig"; then
as_fn_error $? "Unexpected APR reconfig" "$LINENO" 5
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking APR version" >&5
$as_echo_n "checking APR version... " >&6; }
apr_version="`$apr_config --version`"
if test $? -ne 0; then
as_fn_error $? "apr-config --version failed" "$LINENO" 5
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $apr_version" >&5
$as_echo "$apr_version" >&6; }
APR_WANTED_REGEX_MATCH=0
for apr_wanted_regex in $APR_WANTED_REGEXES; do
if test `expr $apr_version : $apr_wanted_regex` -ne 0; then
APR_WANTED_REGEX_MATCH=1
break
fi
done
if test $APR_WANTED_REGEX_MATCH -eq 0; then
echo "wanted regexes are $APR_WANTED_REGEXES"
as_fn_error $? "invalid apr version found" "$LINENO" 5
fi
CPPFLAGS="$CPPFLAGS `$apr_config --cppflags`"
if test $? -ne 0; then
as_fn_error $? "apr-config --cppflags failed" "$LINENO" 5
fi
CFLAGS="$CFLAGS `$apr_config --cflags`"
if test $? -ne 0; then
as_fn_error $? "apr-config --cflags failed" "$LINENO" 5
fi
apr_ldflags="`$apr_config --ldflags`"
if test $? -ne 0; then
as_fn_error $? "apr-config --ldflags failed" "$LINENO" 5
fi
LDFLAGS="$LDFLAGS `
input_flags="$apr_ldflags"
output_flags=""
filtered_dirs="/lib /lib64 /usr/lib /usr/lib64"
for flag in $input_flags; do
filter="no"
for dir in $filtered_dirs; do
if test "$flag" = "-L$dir" || test "$flag" = "-L$dir/"; then
filter="yes"
break
fi
done
if test "$filter" = "no"; then
output_flags="$output_flags $flag"
fi
done
if test -n "$output_flags"; then
printf "%s" "${output_flags# }"
fi
`"
SVN_APR_INCLUDES="`$apr_config --includes`"
if test $? -ne 0; then
as_fn_error $? "apr-config --includes failed" "$LINENO" 5
fi
if test "$enable_all_static" = "yes"; then
SVN_APR_LIBS="`$apr_config --link-ld --libs`"
if test $? -ne 0; then
as_fn_error $? "apr-config --link-ld --libs failed" "$LINENO" 5
fi
else
SVN_APR_LIBS="`$apr_config --link-ld`"
if test $? -ne 0; then
as_fn_error $? "apr-config --link-ld failed" "$LINENO" 5
fi
fi
SVN_APR_LIBS="`
input_flags="$SVN_APR_LIBS"
output_flags=""
filtered_dirs="/lib /lib64 /usr/lib /usr/lib64"
for flag in $input_flags; do
filter="no"
for dir in $filtered_dirs; do
if test "$flag" = "-L$dir" || test "$flag" = "-L$dir/"; then
filter="yes"
break
fi
done
if test "$filter" = "no"; then
output_flags="$output_flags $flag"
fi
done
if test -n "$output_flags"; then
printf "%s" "${output_flags# }"
fi
`"
SVN_APR_SHLIB_PATH_VAR="`$apr_config --shlib-path-var`"
if test $? -ne 0; then
as_fn_error $? "apr-config --shlib-path-var failed" "$LINENO" 5
fi
SVN_APR_CONFIG="$apr_config"
if test `expr $apr_version : 2` -ne 0; then
svn_lib_ver=2
apu_config=$apr_config
SVN_APRUTIL_CONFIG="$apu_config"
else
svn_lib_ver=0
APU_VER_REGEXES="0\.9\.[7-9] 0\.9\.1[0-9] 1\."
APRUTIL_WANTED_REGEXES="$APU_VER_REGEXES"
{ $as_echo "$as_me:${as_lineno-$LINENO}: Apache Portable Runtime Utility (APRUTIL) library configuration" >&5
$as_echo "$as_me: Apache Portable Runtime Utility (APRUTIL) library configuration" >&6;}
apu_found="no"
if test "$ac_cv_emxos2" = "yes"; then
# Scripts don't pass test -x on OS/2
TEST_X="test -f"
else
TEST_X="test -x"
fi
acceptable_majors="1 0"
apu_temp_acceptable_apu_config=""
for apu_temp_major in $acceptable_majors
do
case $apu_temp_major in
0)
apu_temp_acceptable_apu_config="$apu_temp_acceptable_apu_config apu-config"
;;
*)
apu_temp_acceptable_apu_config="$apu_temp_acceptable_apu_config apu-$apu_temp_major-config"
;;
esac
done
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for APR-util" >&5
$as_echo_n "checking for APR-util... " >&6; }
# Check whether --with-apr-util was given.
if test "${with_apr_util+set}" = set; then :
withval=$with_apr_util;
if test "$withval" = "no" || test "$withval" = "yes"; then
as_fn_error $? "--with-apr-util requires a directory or file to be provided" "$LINENO" 5
fi
for apu_temp_apu_config_file in $apu_temp_acceptable_apu_config
do
for lookdir in "$withval/bin" "$withval"
do
if $TEST_X "$lookdir/$apu_temp_apu_config_file"; then
apu_found="yes"
apu_config="$lookdir/$apu_temp_apu_config_file"
break 2
fi
done
done
if test "$apu_found" != "yes" && $TEST_X "$withval" && $withval --help > /dev/null 2>&1 ; then
apu_found="yes"
apu_config="$withval"
fi
if test "$apu_found" != "yes"; then
as_fn_error $? "the --with-apr-util parameter is incorrect. It must specify an install prefix, a build directory, or an apu-config file." "$LINENO" 5
fi
else
if test -d """"; then
apu_temp_abs_srcdir="`cd "" && pwd`"
apu_found="reconfig"
apu_bundled_major="`sed -n '/#define.*APU_MAJOR_VERSION/s/^[^0-9]*\([0-9]*\).*$/\1/p' \"""/include/apu_version.h\"`"
case $apu_bundled_major in
"")
as_fn_error $? "failed to find major version of bundled APU" "$LINENO" 5
;;
0)
apu_temp_apu_config_file="apu-config"
;;
*)
apu_temp_apu_config_file="apu-$apu_bundled_major-config"
;;
esac
if test -n """"; then
apu_config="""/$apu_temp_apu_config_file"
else
apu_config="""/$apu_temp_apu_config_file"
fi
fi
if test "$apu_found" = "no" && test -n "1" && test "1" = "1"; then
for apu_temp_apu_config_file in $apu_temp_acceptable_apu_config
do
if $apu_temp_apu_config_file --help > /dev/null 2>&1 ; then
apu_found="yes"
apu_config="$apu_temp_apu_config_file"
break
else
for lookdir in /usr /usr/local /opt/apr /usr/local/apache2 ; do
if $TEST_X "$lookdir/bin/$apu_temp_apu_config_file"; then
apu_found="yes"
apu_config="$lookdir/bin/$apu_temp_apu_config_file"
break 2
fi
done
fi
done
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $apu_found" >&5
$as_echo "$apu_found" >&6; }
if test $apu_found = "no"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: APRUTIL not found" >&5
$as_echo "$as_me: WARNING: APRUTIL not found" >&2;}
echo "The Apache Portable Runtime Utility (APRUTIL) library cannot be found."
echo "Install APRUTIL on this system and configure Subversion with the"
echo " appropriate --with-apr-util option."
echo ""
as_fn_error $? "no suitable APRUTIL found" "$LINENO" 5
fi
if test $apu_found = "reconfig"; then
as_fn_error $? "Unexpected APRUTIL reconfig" "$LINENO" 5
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking APRUTIL version" >&5
$as_echo_n "checking APRUTIL version... " >&6; }
apu_version="`$apu_config --version`"
if test $? -ne 0; then
# This is a hack as suggested by Ben Collins-Sussman. It can be
# removed after apache 2.0.44 has been released. (The apu-config
# shipped in 2.0.43 contains a correct version number, but
# stupidly doesn't understand the --version switch.)
apu_version=`grep "APRUTIL_DOTTED_VERSION=" $(which $apu_config) | tr -d "APRUTIL_DOTTED_VERSION="| tr -d '"'`
#AC_MSG_ERROR([
# apu-config --version failed.
# Your apu-config doesn't support the --version switch, please upgrade
# to APR-UTIL more recent than 2002-Nov-05.])
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $apu_version" >&5
$as_echo "$apu_version" >&6; }
APU_WANTED_REGEX_MATCH=0
for apu_wanted_regex in $APRUTIL_WANTED_REGEXES; do
if test `expr $apu_version : $apu_wanted_regex` -ne 0; then
APU_WANTED_REGEX_MATCH=1
break
fi
done
if test $APU_WANTED_REGEX_MATCH -eq 0; then
echo "wanted regexes are $APRUTIL_WANTED_REGEXES"
as_fn_error $? "invalid APRUTIL version found" "$LINENO" 5
fi
apu_ldflags="`$apu_config --ldflags`"
if test $? -ne 0; then
as_fn_error $? "apu-config --ldflags failed" "$LINENO" 5
fi
LDFLAGS="$LDFLAGS `
input_flags="$apu_ldflags"
output_flags=""
filtered_dirs="/lib /lib64 /usr/lib /usr/lib64"
for flag in $input_flags; do
filter="no"
for dir in $filtered_dirs; do
if test "$flag" = "-L$dir" || test "$flag" = "-L$dir/"; then
filter="yes"
break
fi
done
if test "$filter" = "no"; then
output_flags="$output_flags $flag"
fi
done
if test -n "$output_flags"; then
printf "%s" "${output_flags# }"
fi
`"
SVN_APRUTIL_INCLUDES="`$apu_config --includes`"
if test $? -ne 0; then
as_fn_error $? "apu-config --includes failed" "$LINENO" 5
fi
if test "$enable_all_static" = "yes"; then
SVN_APRUTIL_LIBS="`$apu_config --link-ld --libs`"
if test $? -ne 0; then
as_fn_error $? "apu-config --link-ld --libs failed" "$LINENO" 5
fi
else
SVN_APRUTIL_LIBS="`$apu_config --link-ld`"
if test $? -ne 0; then
as_fn_error $? "apu-config --link-ld failed" "$LINENO" 5
fi
fi
SVN_APRUTIL_LIBS="`
input_flags="$SVN_APRUTIL_LIBS"
output_flags=""
filtered_dirs="/lib /lib64 /usr/lib /usr/lib64"
for flag in $input_flags; do
filter="no"
for dir in $filtered_dirs; do
if test "$flag" = "-L$dir" || test "$flag" = "-L$dir/"; then
filter="yes"
break
fi
done
if test "$filter" = "no"; then
output_flags="$output_flags $flag"
fi
done
if test -n "$output_flags"; then
printf "%s" "${output_flags# }"
fi
`"
SVN_APRUTIL_CONFIG="$apu_config"
SVN_HAVE_OLD_EXPAT="`$apu_config --old-expat`"
if test "$SVN_HAVE_OLD_EXPAT" = "yes"; then
$as_echo "#define SVN_HAVE_OLD_EXPAT 1" >>confdefs.h
fi
fi
SVN_LT_SOVERSION="-version-info $svn_lib_ver"
cat >>confdefs.h <<_ACEOF
#define SVN_SOVERSION $svn_lib_ver
_ACEOF
# Extract the first word of "pkg-config", so it can be a program name with args.
set dummy pkg-config; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_PKG_CONFIG+:} false; then :
$as_echo_n "(cached) " >&6
else
case $PKG_CONFIG in
[\\/]* | ?:[\\/]*)
ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
;;
esac
fi
PKG_CONFIG=$ac_cv_path_PKG_CONFIG
if test -n "$PKG_CONFIG"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
$as_echo "$PKG_CONFIG" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
$as_echo_n "checking for ANSI C header files... " >&6; }
if ${ac_cv_header_stdc+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <float.h>
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ac_cv_header_stdc=yes
else
ac_cv_header_stdc=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
if test $ac_cv_header_stdc = yes; then
# SunOS 4.x string.h does not declare mem*, contrary to ANSI.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <string.h>
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
$EGREP "memchr" >/dev/null 2>&1; then :
else
ac_cv_header_stdc=no
fi
rm -f conftest*
fi
if test $ac_cv_header_stdc = yes; then
# ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdlib.h>
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
$EGREP "free" >/dev/null 2>&1; then :
else
ac_cv_header_stdc=no
fi
rm -f conftest*
fi
if test $ac_cv_header_stdc = yes; then
# /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
if test "$cross_compiling" = yes; then :
:
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <ctype.h>
#include <stdlib.h>
#if ((' ' & 0x0FF) == 0x020)
# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
#else
# define ISLOWER(c) \
(('a' <= (c) && (c) <= 'i') \
|| ('j' <= (c) && (c) <= 'r') \
|| ('s' <= (c) && (c) <= 'z'))
# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
#endif
#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
int
main ()
{
int i;
for (i = 0; i < 256; i++)
if (XOR (islower (i), ISLOWER (i))
|| toupper (i) != TOUPPER (i))
return 2;
return 0;
}
_ACEOF
if ac_fn_c_try_run "$LINENO"; then :
else
ac_cv_header_stdc=no
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
$as_echo "$ac_cv_header_stdc" >&6; }
if test $ac_cv_header_stdc = yes; then
$as_echo "#define STDC_HEADERS 1" >>confdefs.h
fi
# On IRIX 5.3, sys/types and inttypes.h are conflicting.
for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
inttypes.h stdint.h unistd.h
do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
"
if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
_ACEOF
fi
done
serf_found=no
serf_required=no
serf_skip=no
serf_check_major="1"
serf_check_minor="2"
serf_check_patch="1"
serf_check_version="1.2.1"
# Check whether --with-serf was given.
if test "${with_serf+set}" = set; then :
withval=$with_serf;
if test "$withval" = "yes" ; then
serf_required=yes
elif test "$withval" = "no" ; then
serf_skip=yes
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: serf library configuration via prefix" >&5
$as_echo "$as_me: serf library configuration via prefix" >&6;}
serf_required=yes
serf_prefix=$withval
for serf_major in serf-2 serf-1; do
if ! test -d $serf_prefix/include/$serf_major; then continue; fi
save_cppflags="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $SVN_APR_INCLUDES $SVN_APRUTIL_INCLUDES -I$serf_prefix/include/$serf_major"
for ac_header in serf.h
do :
ac_fn_c_check_header_mongrel "$LINENO" "serf.h" "ac_cv_header_serf_h" "$ac_includes_default"
if test "x$ac_cv_header_serf_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_SERF_H 1
_ACEOF
save_ldflags="$LDFLAGS"
LDFLAGS="$LDFLAGS `
input_flags="-L$serf_prefix/lib"
output_flags=""
filtered_dirs="/lib /lib64 /usr/lib /usr/lib64"
for flag in $input_flags; do
filter="no"
for dir in $filtered_dirs; do
if test "$flag" = "-L$dir" || test "$flag" = "-L$dir/"; then
filter="yes"
break
fi
done
if test "$filter" = "no"; then
output_flags="$output_flags $flag"
fi
done
if test -n "$output_flags"; then
printf "%s" "${output_flags# }"
fi
`"
as_ac_Lib=`$as_echo "ac_cv_lib_$serf_major''_serf_context_create" | $as_tr_sh`
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for serf_context_create in -l$serf_major" >&5
$as_echo_n "checking for serf_context_create in -l$serf_major... " >&6; }
if eval \${$as_ac_Lib+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-l$serf_major $SVN_APRUTIL_LIBS $SVN_APR_LIBS -lz $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char serf_context_create ();
int
main ()
{
return serf_context_create ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
eval "$as_ac_Lib=yes"
else
eval "$as_ac_Lib=no"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
eval ac_res=\$$as_ac_Lib
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdlib.h>
#include "serf.h"
int
main ()
{
#if ! SERF_VERSION_AT_LEAST($serf_check_major, $serf_check_minor, $serf_check_patch)
#error Serf version too old: need $serf_check_version
#endif
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
serf_found=yes
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Serf version too old: need $serf_check_version" >&5
$as_echo "$as_me: WARNING: Serf version too old: need $serf_check_version" >&2;}
serf_found=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
LDFLAGS="$save_ldflags"
fi
done
CPPFLAGS="$save_cppflags"
test $serf_found = yes && break
done
if test $serf_found = "yes"; then
SVN_SERF_INCLUDES="-I$serf_prefix/include/$serf_major"
if test -e "$serf_prefix/lib/lib$serf_major.la"; then
SVN_SERF_LIBS="$serf_prefix/lib/lib$serf_major.la"
else
SVN_SERF_LIBS="-l$serf_major"
LDFLAGS="$LDFLAGS `
input_flags="-L$serf_prefix/lib"
output_flags=""
filtered_dirs="/lib /lib64 /usr/lib /usr/lib64"
for flag in $input_flags; do
filter="no"
for dir in $filtered_dirs; do
if test "$flag" = "-L$dir" || test "$flag" = "-L$dir/"; then
filter="yes"
break
fi
done
if test "$filter" = "no"; then
output_flags="$output_flags $flag"
fi
done
if test -n "$output_flags"; then
printf "%s" "${output_flags# }"
fi
`"
fi
fi
fi
fi
if test "$serf_skip" = "no" ; then
if test "$serf_found" = "no" ; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: serf library configuration via pkg-config" >&5
$as_echo "$as_me: serf library configuration via pkg-config" >&6;}
if test -n "$PKG_CONFIG"; then
for serf_major in serf-2 serf-1; do
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $serf_major library" >&5
$as_echo_n "checking for $serf_major library... " >&6; }
if $PKG_CONFIG $serf_major --exists; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking serf library version" >&5
$as_echo_n "checking serf library version... " >&6; }
SERF_VERSION=`$PKG_CONFIG $serf_major --modversion`
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $SERF_VERSION" >&5
$as_echo "$SERF_VERSION" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking serf version is suitable" >&5
$as_echo_n "checking serf version is suitable... " >&6; }
if $PKG_CONFIG $serf_major --atleast-version=$serf_check_version; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
serf_found=yes
SVN_SERF_INCLUDES=`$PKG_CONFIG $serf_major --cflags | $SED -e 's/-D[^ ]*//g'`
SVN_SERF_LIBS=`$PKG_CONFIG $serf_major --libs`
break
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Serf version too old: need $serf_check_version" >&5
$as_echo "$as_me: WARNING: Serf version too old: need $serf_check_version" >&2;}
fi
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
done
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking was serf enabled" >&5
$as_echo_n "checking was serf enabled... " >&6; }
if test "$serf_found" = "yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
echo ""
echo "An appropriate version of serf could not be found, so libsvn_ra_serf"
echo "will not be built. If you want to build libsvn_ra_serf, please"
echo "install serf $serf_check_version or newer."
echo ""
if test "$serf_required" = "yes"; then
as_fn_error $? "Serf was explicitly enabled but an appropriate version was not found." "$LINENO" 5
fi
fi
fi
svn_lib_serf=$serf_found
if test "$svn_lib_serf" = "yes"; then
$as_echo "#define SVN_HAVE_SERF 1" >>confdefs.h
fi
apr_memcache_found=no
# Check whether --with-apr_memcache was given.
if test "${with_apr_memcache+set}" = set; then :
withval=$with_apr_memcache;
if test "$withval" = "yes" ; then
as_fn_error $? "--with-apr_memcache requires an argument." "$LINENO" 5
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: looking for separate apr_memcache package" >&5
$as_echo "$as_me: looking for separate apr_memcache package" >&6;}
apr_memcache_prefix=$withval
save_cppflags="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $SVN_APR_INCLUDES $SVN_APRUTIL_INCLUDES -I$apr_memcache_prefix/include/apr_memcache-0"
ac_fn_c_check_header_mongrel "$LINENO" "apr_memcache.h" "ac_cv_header_apr_memcache_h" "$ac_includes_default"
if test "x$ac_cv_header_apr_memcache_h" = xyes; then :
save_ldflags="$LDFLAGS"
LDFLAGS="$LDFLAGS -L$apr_memcache_prefix/lib"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for apr_memcache_create in -lapr_memcache" >&5
$as_echo_n "checking for apr_memcache_create in -lapr_memcache... " >&6; }
if ${ac_cv_lib_apr_memcache_apr_memcache_create+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lapr_memcache $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char apr_memcache_create ();
int
main ()
{
return apr_memcache_create ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_apr_memcache_apr_memcache_create=yes
else
ac_cv_lib_apr_memcache_apr_memcache_create=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_apr_memcache_apr_memcache_create" >&5
$as_echo "$ac_cv_lib_apr_memcache_apr_memcache_create" >&6; }
if test "x$ac_cv_lib_apr_memcache_apr_memcache_create" = xyes; then :
apr_memcache_found="standalone"
fi
LDFLAGS="$save_ldflags"
fi
CPPFLAGS="$save_cppflags"
fi
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: looking for apr_memcache as part of apr-util" >&5
$as_echo "$as_me: looking for apr_memcache as part of apr-util" >&6;}
save_cppflags="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $SVN_APR_INCLUDES $SVN_APRUTIL_INCLUDES"
ac_fn_c_check_header_mongrel "$LINENO" "apr_memcache.h" "ac_cv_header_apr_memcache_h" "$ac_includes_default"
if test "x$ac_cv_header_apr_memcache_h" = xyes; then :
save_ldflags="$LDFLAGS"
LDFLAGS="$LDFLAGS $SVN_APRUTIL_LIBS"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for apr_memcache_create in -laprutil-1" >&5
$as_echo_n "checking for apr_memcache_create in -laprutil-1... " >&6; }
if ${ac_cv_lib_aprutil_1_apr_memcache_create+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-laprutil-1 $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char apr_memcache_create ();
int
main ()
{
return apr_memcache_create ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_aprutil_1_apr_memcache_create=yes
else
ac_cv_lib_aprutil_1_apr_memcache_create=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_aprutil_1_apr_memcache_create" >&5
$as_echo "$ac_cv_lib_aprutil_1_apr_memcache_create" >&6; }
if test "x$ac_cv_lib_aprutil_1_apr_memcache_create" = xyes; then :
apr_memcache_found="aprutil"
fi
LDFLAGS="$save_ldflags"
fi
CPPFLAGS="$save_cppflags"
fi
if test $apr_memcache_found = "standalone"; then
SVN_APR_MEMCACHE_INCLUDES="-I$apr_memcache_prefix/include/apr_memcache-0"
SVN_APR_MEMCACHE_LIBS="$apr_memcache_prefix/lib/libapr_memcache.la"
svn_lib_apr_memcache=yes
elif test $apr_memcache_found = "aprutil"; then
SVN_APR_MEMCACHE_INCLUDES=""
SVN_APR_MEMCACHE_LIBS=""
svn_lib_apr_memcache=yes
elif test $apr_memcache_found = "reconfig"; then
svn_lib_apr_memcache=yes
else
svn_lib_apr_memcache=no
fi
if test "$svn_lib_apr_memcache" = "yes"; then
$as_echo "#define SVN_HAVE_MEMCACHE 1" >>confdefs.h
fi
HTTPD_WANTED_MMN="20020903"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Apache module support via DSO through APXS" >&5
$as_echo_n "checking for Apache module support via DSO through APXS... " >&6; }
# Check whether --with-apxs was given.
if test "${with_apxs+set}" = set; then :
withval=$with_apxs;
if test "$withval" = "yes"; then
APXS=apxs
else
APXS="$withval"
fi
APXS_EXPLICIT=1
fi
if test -z "$APXS"; then
for i in /usr/sbin /usr/local/apache/bin /usr/local/apache2/bin /usr/bin ; do
if test -f "$i/apxs2"; then
APXS="$i/apxs2"
break
fi
if test -f "$i/apxs"; then
APXS="$i/apxs"
break
fi
done
fi
if test -n "$APXS" && test "$APXS" != "no"; then
APXS_INCLUDE="`$APXS -q INCLUDEDIR`"
if test -r $APXS_INCLUDE/mod_dav.h; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: found at $APXS" >&5
$as_echo "found at $APXS" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking httpd version" >&5
$as_echo_n "checking httpd version... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include "$APXS_INCLUDE/ap_mmn.h"
#if AP_MODULE_MAGIC_AT_LEAST($HTTPD_WANTED_MMN,0)
VERSION_OKAY
#endif
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
$EGREP "VERSION_OKAY" >/dev/null 2>&1; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: recent enough" >&5
$as_echo "recent enough" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: apache too old: mmn must be at least $HTTPD_WANTED_MMN" >&5
$as_echo "apache too old: mmn must be at least $HTTPD_WANTED_MMN" >&6; }
if test "$APXS_EXPLICIT" != ""; then
as_fn_error $? "Apache APXS build explicitly requested, but apache version is too old" "$LINENO" 5
fi
APXS=""
fi
rm -f conftest*
elif test "$APXS_EXPLICIT" != ""; then
as_fn_error $? "no - APXS refers to an old version of Apache
Unable to locate $APXS_INCLUDE/mod_dav.h" "$LINENO" 5
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no - Unable to locate $APXS_INCLUDE/mod_dav.h" >&5
$as_echo "no - Unable to locate $APXS_INCLUDE/mod_dav.h" >&6; }
APXS=""
fi
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test -n "$APXS" && test "$APXS" != "no"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether Apache version is compatible with APR version" >&5
$as_echo_n "checking whether Apache version is compatible with APR version... " >&6; }
apr_major_version="${apr_version%%.*}"
case "$apr_major_version" in
0)
apache_minor_version_wanted_regex="0"
;;
1)
apache_minor_version_wanted_regex="[1-5]"
;;
2)
apache_minor_version_wanted_regex="[3-5]"
;;
*)
as_fn_error $? "unknown APR version" "$LINENO" 5
;;
esac
old_CPPFLAGS="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $SVN_APR_INCLUDES"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include "$APXS_INCLUDE/ap_release.h"
apache_minor_version=AP_SERVER_MINORVERSION
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
$EGREP "apache_minor_version= *\"$apache_minor_version_wanted_regex\"" >/dev/null 2>&1; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
as_fn_error $? "Apache version incompatible with APR version" "$LINENO" 5
fi
rm -f conftest*
CPPFLAGS="$old_CPPFLAGS"
fi
# check for some busted versions of mod_dav
# in particular 2.2.25, 2.4.5, and 2.4.6 had the following bugs which are
# troublesome for Subversion:
# PR 55304: https://issues.apache.org/bugzilla/show_bug.cgi?id=55304
# PR 55306: https://issues.apache.org/bugzilla/show_bug.cgi?id=55306
# PR 55397: https://issues.apache.org/bugzilla/show_bug.cgi?id=55397
if test -n "$APXS" && test "$APXS" != "no"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking mod_dav version" >&5
$as_echo_n "checking mod_dav version... " >&6; }
old_CPPFLAGS="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $SVN_APR_INCLUDES"
blacklisted_versions_regex="\"2\" \"\.\" (\"2\" \"\.\" \"25\"|\"4\" \"\.\" \"[56]\")"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include "$APXS_INCLUDE/ap_release.h"
apache_version=AP_SERVER_BASEREVISION
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
$EGREP "apache_version= *$blacklisted_versions_regex" >/dev/null 2>&1; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: broken" >&5
$as_echo "broken" >&6; }
as_fn_error $? "Apache httpd version includes a broken mod_dav; use a newer version of httpd" "$LINENO" 5
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: acceptable" >&5
$as_echo "acceptable" >&6; }
fi
rm -f conftest*
CPPFLAGS="$old_CPPFLAGS"
fi
# Check whether --with-apache-libexecdir was given.
if test "${with_apache_libexecdir+set}" = set; then :
withval=$with_apache_libexecdir; APACHE_LIBEXECDIR="$withval"
else
APACHE_LIBEXECDIR='no'
fi
INSTALL_APACHE_MODS=false
if test -n "$APXS" && test "$APXS" != "no"; then
APXS_CC="`$APXS -q CC`"
APACHE_INCLUDES="$APACHE_INCLUDES -I$APXS_INCLUDE"
if test "$APACHE_LIBEXECDIR" = 'no'; then
APACHE_LIBEXECDIR="$libexecdir"
elif test "$APACHE_LIBEXECDIR" = 'yes'; then
APACHE_LIBEXECDIR="`$APXS -q libexecdir`"
fi
BUILD_APACHE_RULE=apache-mod
INSTALL_APACHE_RULE=install-mods-shared
INSTALL_APACHE_MODS=true
+ HTTPD="`$APXS -q sbindir`/`$APXS -q PROGNAME`"
+ if ! test -e $HTTPD ; then
+ HTTPD="`$APXS -q bindir`/`$APXS -q PROGNAME`"
+ fi
+ HTTPD_VERSION="`$HTTPD -v | $SED -e 's@^.*/\([0-9.]*\)\(.*$\)@\1@ ; 1q'`"
+ # Check whether --enable-broken-httpd-auth was given.
+if test "${enable_broken_httpd_auth+set}" = set; then :
+ enableval=$enable_broken_httpd_auth; broken_httpd_auth=$enableval
+else
+ broken_httpd_auth=no
+fi
+ if test "$enable_broken_httpd_auth" = "yes"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Building with broken httpd auth" >&5
+$as_echo "$as_me: Building with broken httpd auth" >&6;}
+
+$as_echo "#define SVN_ALLOW_BROKEN_HTTPD_AUTH 1" >>confdefs.h
+
+ fi
+
case $host in
*-*-cygwin*)
APACHE_LDFLAGS="-shrext .so"
;;
esac
elif test x"$APXS" != x"no"; then
echo "=================================================================="
echo "WARNING: skipping the build of mod_dav_svn"
echo " try using --with-apxs"
echo "=================================================================="
fi
+
# there aren't any flags that interest us ...
#if test -n "$APXS" && test "$APXS" != "no"; then
# CFLAGS="$CFLAGS `$APXS -q CFLAGS CFLAGS_SHLIB`"
#fi
if test -n "$APXS_CC" && test "$APXS_CC" != "$CC" ; then
echo "=================================================================="
echo "WARNING: You have chosen to compile Subversion with a different"
echo " compiler than the one used to compile Apache."
echo ""
echo " Current compiler: $CC"
echo " Apache's compiler: $APXS_CC"
echo ""
echo "This could cause some problems."
echo "=================================================================="
fi
SQLITE_MINIMUM_VER="3.7.12"
SQLITE_RECOMMENDED_VER="3.7.15.1"
SQLITE_URL="http://www.sqlite.org/sqlite-amalgamation-$(printf %d%02d%02d%02d $(echo ${SQLITE_RECOMMENDED_VER} | sed -e 's/\./ /g')).zip"
SQLITE_MINIMUM_VER="${SQLITE_MINIMUM_VER}"
SQLITE_RECOMMENDED_VER="${SQLITE_RECOMMENDED_VER}"
SQLITE_URL="${SQLITE_URL}"
SQLITE_PKGNAME="sqlite3"
version_string="$SQLITE_MINIMUM_VER"
major=`expr $version_string : '\([0-9]*\)'`
minor=`expr $version_string : '[0-9]*\.\([0-9]*\)'`
micro=`expr $version_string : '[0-9]*\.[0-9]*\.\([0-9]*\)'`
if test -z "$micro"; then
micro=0
fi
sqlite_min_ver_num=`expr $major \* 1000000 \
\+ $minor \* 1000 \
\+ $micro`
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking sqlite library" >&5
$as_echo "$as_me: checking sqlite library" >&6;}
# Check whether --with-sqlite was given.
if test "${with_sqlite+set}" = set; then :
withval=$with_sqlite;
if test "$withval" = "yes" ; then
as_fn_error $? "--with-sqlite requires an argument." "$LINENO" 5
else
sqlite_dir="$withval"
fi
if test -d $sqlite_dir; then
if test -z "$sqlite_dir"; then
sqlite_dir=""
sqlite_include="sqlite3.h"
else
sqlite_dir="$sqlite_dir"
sqlite_include="$sqlite_dir/include/sqlite3.h"
fi
save_CPPFLAGS="$CPPFLAGS"
save_LDFLAGS="$LDFLAGS"
if test ! -z "$sqlite_dir"; then
CPPFLAGS="$CPPFLAGS -I$sqlite_dir/include"
LDFLAGS="$LDFLAGS -L$sqlite_dir/lib"
fi
ac_fn_c_check_header_mongrel "$LINENO" "sqlite3.h" "ac_cv_header_sqlite3_h" "$ac_includes_default"
if test "x$ac_cv_header_sqlite3_h" = xyes; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking sqlite library version (via header)" >&5
$as_echo_n "checking sqlite library version (via header)... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include "$sqlite_include"
#if SQLITE_VERSION_NUMBER >= $sqlite_min_ver_num
SQLITE_VERSION_OKAY
#endif
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
$EGREP "SQLITE_VERSION_OKAY" >/dev/null 2>&1; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: okay" >&5
$as_echo "okay" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sqlite3_close in -lsqlite3" >&5
$as_echo_n "checking for sqlite3_close in -lsqlite3... " >&6; }
if ${ac_cv_lib_sqlite3_sqlite3_close+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lsqlite3 $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char sqlite3_close ();
int
main ()
{
return sqlite3_close ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_sqlite3_sqlite3_close=yes
else
ac_cv_lib_sqlite3_sqlite3_close=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sqlite3_sqlite3_close" >&5
$as_echo "$ac_cv_lib_sqlite3_sqlite3_close" >&6; }
if test "x$ac_cv_lib_sqlite3_sqlite3_close" = xyes; then :
svn_lib_sqlite="yes"
if test -z "$sqlite_dir" -o ! -d "$sqlite_dir"; then
SVN_SQLITE_LIBS="-lsqlite3"
else
SVN_SQLITE_INCLUDES="-I$sqlite_dir/include"
SVN_SQLITE_LIBS="`
input_flags="-L$sqlite_dir/lib -lsqlite3"
output_flags=""
filtered_dirs="/lib /lib64 /usr/lib /usr/lib64"
for flag in $input_flags; do
filter="no"
for dir in $filtered_dirs; do
if test "$flag" = "-L$dir" || test "$flag" = "-L$dir/"; then
filter="yes"
break
fi
done
if test "$filter" = "no"; then
output_flags="$output_flags $flag"
fi
done
if test -n "$output_flags"; then
printf "%s" "${output_flags# }"
fi
`"
fi
fi
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported SQLite version" >&5
$as_echo "unsupported SQLite version" >&6; }
fi
rm -f conftest*
fi
CPPFLAGS="$save_CPPFLAGS"
LDFLAGS="$save_LDFLAGS"
else
sqlite_amalg="$sqlite_dir"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking sqlite amalgamation" >&5
$as_echo_n "checking sqlite amalgamation... " >&6; }
if test ! -e $sqlite_amalg; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking sqlite amalgamation file version" >&5
$as_echo_n "checking sqlite amalgamation file version... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include "$sqlite_amalg"
#if SQLITE_VERSION_NUMBER >= $sqlite_min_ver_num
SQLITE_VERSION_OKAY
#endif
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
$EGREP "SQLITE_VERSION_OKAY" >/dev/null 2>&1; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: amalgamation found and is okay" >&5
$as_echo "amalgamation found and is okay" >&6; }
case $host_os in
beos* | mingw* | pw32* | cegcc* | cygwin*)
svn_sqlite_dso_ldflags=
;;
darwin*)
# if libdl is installed we need to link against it
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
$as_echo_n "checking for dlopen in -ldl... " >&6; }
if ${ac_cv_lib_dl_dlopen+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-ldl $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char dlopen ();
int
main ()
{
return dlopen ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_dl_dlopen=yes
else
ac_cv_lib_dl_dlopen=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
lt_cv_dlopen="dlopen" svn_sqlite_dso_ldflags="-ldl"
else
svn_sqlite_dso_ldflags=
fi
;;
*)
ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load"
if test "x$ac_cv_func_shl_load" = xyes; then :
svn_sqlite_dso_ldflags=
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5
$as_echo_n "checking for shl_load in -ldld... " >&6; }
if ${ac_cv_lib_dld_shl_load+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-ldld $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char shl_load ();
int
main ()
{
return shl_load ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_dld_shl_load=yes
else
ac_cv_lib_dld_shl_load=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5
$as_echo "$ac_cv_lib_dld_shl_load" >&6; }
if test "x$ac_cv_lib_dld_shl_load" = xyes; then :
svn_sqlite_dso_ldflags="-ldld"
else
ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen"
if test "x$ac_cv_func_dlopen" = xyes; then :
svn_sqlite_dso_ldflags=
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
$as_echo_n "checking for dlopen in -ldl... " >&6; }
if ${ac_cv_lib_dl_dlopen+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-ldl $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char dlopen ();
int
main ()
{
return dlopen ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_dl_dlopen=yes
else
ac_cv_lib_dl_dlopen=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
svn_sqlite_dso_ldflags="-ldl"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5
$as_echo_n "checking for dlopen in -lsvld... " >&6; }
if ${ac_cv_lib_svld_dlopen+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lsvld $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char dlopen ();
int
main ()
{
return dlopen ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_svld_dlopen=yes
else
ac_cv_lib_svld_dlopen=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5
$as_echo "$ac_cv_lib_svld_dlopen" >&6; }
if test "x$ac_cv_lib_svld_dlopen" = xyes; then :
svn_sqlite_dso_ldflags="-lsvld"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5
$as_echo_n "checking for dld_link in -ldld... " >&6; }
if ${ac_cv_lib_dld_dld_link+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-ldld $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char dld_link ();
int
main ()
{
return dld_link ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_dld_dld_link=yes
else
ac_cv_lib_dld_dld_link=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5
$as_echo "$ac_cv_lib_dld_dld_link" >&6; }
if test "x$ac_cv_lib_dld_dld_link" = xyes; then :
svn_sqlite_dso_ldflags="-ldld"
fi
fi
fi
fi
fi
fi
;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking additional libraries for sqlite" >&5
$as_echo_n "checking additional libraries for sqlite... " >&6; }
if test -n "$svn_sqlite_dso_ldflags"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${svn_sqlite_dso_ldflags}" >&5
$as_echo "${svn_sqlite_dso_ldflags}" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5
$as_echo "none" >&6; }
fi
$as_echo "#define SVN_SQLITE_INLINE 1" >>confdefs.h
SVN_SQLITE_INCLUDES="-I`dirname $sqlite_amalg`"
if test -n "$svn_sqlite_dso_ldflags"; then
SVN_SQLITE_LIBS="$svn_sqlite_dso_ldflags -lpthread"
else
SVN_SQLITE_LIBS="-lpthread"
fi
svn_lib_sqlite="yes"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported amalgamation SQLite version" >&5
$as_echo "unsupported amalgamation SQLite version" >&6; }
fi
rm -f conftest*
fi
fi
if test -z "$svn_lib_sqlite"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: no suitable sqlite found in $sqlite_dir" >&5
$as_echo "$as_me: WARNING: no suitable sqlite found in $sqlite_dir" >&2;}
echo ""
echo "An appropriate version of sqlite could not be found. We recommmend"
echo "${SQLITE_RECOMMENDED_VER}, but require at least ${SQLITE_MINIMUM_VER}."
echo "Please either install a newer sqlite on this system"
echo ""
echo "or"
echo ""
echo "get the sqlite ${SQLITE_RECOMMENDED_VER} amalgamation from:"
echo " ${SQLITE_URL}"
echo "unpack the archive using unzip and rename the resulting"
echo "directory to:"
echo "$abs_srcdir/sqlite-amalgamation"
if test x"$abs_srcdir" != x"$abs_builddir"; then
echo "or to:"
echo "$abs_builddir/sqlite-amalgamation"
fi
echo ""
as_fn_error $? "Subversion requires SQLite" "$LINENO" 5
fi
else
sqlite_amalg="$abs_srcdir/sqlite-amalgamation/sqlite3.c"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking sqlite amalgamation" >&5
$as_echo_n "checking sqlite amalgamation... " >&6; }
if test ! -e $sqlite_amalg; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking sqlite amalgamation file version" >&5
$as_echo_n "checking sqlite amalgamation file version... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include "$sqlite_amalg"
#if SQLITE_VERSION_NUMBER >= $sqlite_min_ver_num
SQLITE_VERSION_OKAY
#endif
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
$EGREP "SQLITE_VERSION_OKAY" >/dev/null 2>&1; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: amalgamation found and is okay" >&5
$as_echo "amalgamation found and is okay" >&6; }
case $host_os in
beos* | mingw* | pw32* | cegcc* | cygwin*)
svn_sqlite_dso_ldflags=
;;
darwin*)
# if libdl is installed we need to link against it
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
$as_echo_n "checking for dlopen in -ldl... " >&6; }
if ${ac_cv_lib_dl_dlopen+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-ldl $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char dlopen ();
int
main ()
{
return dlopen ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_dl_dlopen=yes
else
ac_cv_lib_dl_dlopen=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
lt_cv_dlopen="dlopen" svn_sqlite_dso_ldflags="-ldl"
else
svn_sqlite_dso_ldflags=
fi
;;
*)
ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load"
if test "x$ac_cv_func_shl_load" = xyes; then :
svn_sqlite_dso_ldflags=
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5
$as_echo_n "checking for shl_load in -ldld... " >&6; }
if ${ac_cv_lib_dld_shl_load+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-ldld $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char shl_load ();
int
main ()
{
return shl_load ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_dld_shl_load=yes
else
ac_cv_lib_dld_shl_load=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5
$as_echo "$ac_cv_lib_dld_shl_load" >&6; }
if test "x$ac_cv_lib_dld_shl_load" = xyes; then :
svn_sqlite_dso_ldflags="-ldld"
else
ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen"
if test "x$ac_cv_func_dlopen" = xyes; then :
svn_sqlite_dso_ldflags=
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
$as_echo_n "checking for dlopen in -ldl... " >&6; }
if ${ac_cv_lib_dl_dlopen+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-ldl $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char dlopen ();
int
main ()
{
return dlopen ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_dl_dlopen=yes
else
ac_cv_lib_dl_dlopen=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
svn_sqlite_dso_ldflags="-ldl"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5
$as_echo_n "checking for dlopen in -lsvld... " >&6; }
if ${ac_cv_lib_svld_dlopen+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lsvld $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char dlopen ();
int
main ()
{
return dlopen ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_svld_dlopen=yes
else
ac_cv_lib_svld_dlopen=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5
$as_echo "$ac_cv_lib_svld_dlopen" >&6; }
if test "x$ac_cv_lib_svld_dlopen" = xyes; then :
svn_sqlite_dso_ldflags="-lsvld"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5
$as_echo_n "checking for dld_link in -ldld... " >&6; }
if ${ac_cv_lib_dld_dld_link+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-ldld $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char dld_link ();
int
main ()
{
return dld_link ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_dld_dld_link=yes
else
ac_cv_lib_dld_dld_link=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5
$as_echo "$ac_cv_lib_dld_dld_link" >&6; }
if test "x$ac_cv_lib_dld_dld_link" = xyes; then :
svn_sqlite_dso_ldflags="-ldld"
fi
fi
fi
fi
fi
fi
;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking additional libraries for sqlite" >&5
$as_echo_n "checking additional libraries for sqlite... " >&6; }
if test -n "$svn_sqlite_dso_ldflags"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${svn_sqlite_dso_ldflags}" >&5
$as_echo "${svn_sqlite_dso_ldflags}" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5
$as_echo "none" >&6; }
fi
$as_echo "#define SVN_SQLITE_INLINE 1" >>confdefs.h
SVN_SQLITE_INCLUDES="-I`dirname $sqlite_amalg`"
if test -n "$svn_sqlite_dso_ldflags"; then
SVN_SQLITE_LIBS="$svn_sqlite_dso_ldflags -lpthread"
else
SVN_SQLITE_LIBS="-lpthread"
fi
svn_lib_sqlite="yes"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported amalgamation SQLite version" >&5
$as_echo "unsupported amalgamation SQLite version" >&6; }
fi
rm -f conftest*
fi
if test -z "$svn_lib_sqlite"; then
sqlite_amalg="$abs_builddir/sqlite-amalgamation/sqlite3.c"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking sqlite amalgamation" >&5
$as_echo_n "checking sqlite amalgamation... " >&6; }
if test ! -e $sqlite_amalg; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking sqlite amalgamation file version" >&5
$as_echo_n "checking sqlite amalgamation file version... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include "$sqlite_amalg"
#if SQLITE_VERSION_NUMBER >= $sqlite_min_ver_num
SQLITE_VERSION_OKAY
#endif
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
$EGREP "SQLITE_VERSION_OKAY" >/dev/null 2>&1; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: amalgamation found and is okay" >&5
$as_echo "amalgamation found and is okay" >&6; }
case $host_os in
beos* | mingw* | pw32* | cegcc* | cygwin*)
svn_sqlite_dso_ldflags=
;;
darwin*)
# if libdl is installed we need to link against it
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
$as_echo_n "checking for dlopen in -ldl... " >&6; }
if ${ac_cv_lib_dl_dlopen+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-ldl $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char dlopen ();
int
main ()
{
return dlopen ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_dl_dlopen=yes
else
ac_cv_lib_dl_dlopen=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
lt_cv_dlopen="dlopen" svn_sqlite_dso_ldflags="-ldl"
else
svn_sqlite_dso_ldflags=
fi
;;
*)
ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load"
if test "x$ac_cv_func_shl_load" = xyes; then :
svn_sqlite_dso_ldflags=
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5
$as_echo_n "checking for shl_load in -ldld... " >&6; }
if ${ac_cv_lib_dld_shl_load+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-ldld $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char shl_load ();
int
main ()
{
return shl_load ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_dld_shl_load=yes
else
ac_cv_lib_dld_shl_load=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5
$as_echo "$ac_cv_lib_dld_shl_load" >&6; }
if test "x$ac_cv_lib_dld_shl_load" = xyes; then :
svn_sqlite_dso_ldflags="-ldld"
else
ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen"
if test "x$ac_cv_func_dlopen" = xyes; then :
svn_sqlite_dso_ldflags=
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
$as_echo_n "checking for dlopen in -ldl... " >&6; }
if ${ac_cv_lib_dl_dlopen+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-ldl $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char dlopen ();
int
main ()
{
return dlopen ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_dl_dlopen=yes
else
ac_cv_lib_dl_dlopen=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
svn_sqlite_dso_ldflags="-ldl"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5
$as_echo_n "checking for dlopen in -lsvld... " >&6; }
if ${ac_cv_lib_svld_dlopen+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lsvld $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char dlopen ();
int
main ()
{
return dlopen ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_svld_dlopen=yes
else
ac_cv_lib_svld_dlopen=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5
$as_echo "$ac_cv_lib_svld_dlopen" >&6; }
if test "x$ac_cv_lib_svld_dlopen" = xyes; then :
svn_sqlite_dso_ldflags="-lsvld"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5
$as_echo_n "checking for dld_link in -ldld... " >&6; }
if ${ac_cv_lib_dld_dld_link+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-ldld $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char dld_link ();
int
main ()
{
return dld_link ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_dld_dld_link=yes
else
ac_cv_lib_dld_dld_link=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5
$as_echo "$ac_cv_lib_dld_dld_link" >&6; }
if test "x$ac_cv_lib_dld_dld_link" = xyes; then :
svn_sqlite_dso_ldflags="-ldld"
fi
fi
fi
fi
fi
fi
;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking additional libraries for sqlite" >&5
$as_echo_n "checking additional libraries for sqlite... " >&6; }
if test -n "$svn_sqlite_dso_ldflags"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${svn_sqlite_dso_ldflags}" >&5
$as_echo "${svn_sqlite_dso_ldflags}" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5
$as_echo "none" >&6; }
fi
$as_echo "#define SVN_SQLITE_INLINE 1" >>confdefs.h
SVN_SQLITE_INCLUDES="-I`dirname $sqlite_amalg`"
if test -n "$svn_sqlite_dso_ldflags"; then
SVN_SQLITE_LIBS="$svn_sqlite_dso_ldflags -lpthread"
else
SVN_SQLITE_LIBS="-lpthread"
fi
svn_lib_sqlite="yes"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported amalgamation SQLite version" >&5
$as_echo "unsupported amalgamation SQLite version" >&6; }
fi
rm -f conftest*
fi
fi
if test -z "$svn_lib_sqlite"; then
if test -z ""; then
sqlite_dir=""
sqlite_include="sqlite3.h"
else
sqlite_dir=""
sqlite_include="/include/sqlite3.h"
fi
save_CPPFLAGS="$CPPFLAGS"
save_LDFLAGS="$LDFLAGS"
if test ! -z ""; then
CPPFLAGS="$CPPFLAGS -I$sqlite_dir/include"
LDFLAGS="$LDFLAGS -L$sqlite_dir/lib"
fi
ac_fn_c_check_header_mongrel "$LINENO" "sqlite3.h" "ac_cv_header_sqlite3_h" "$ac_includes_default"
if test "x$ac_cv_header_sqlite3_h" = xyes; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking sqlite library version (via header)" >&5
$as_echo_n "checking sqlite library version (via header)... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include "$sqlite_include"
#if SQLITE_VERSION_NUMBER >= $sqlite_min_ver_num
SQLITE_VERSION_OKAY
#endif
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
$EGREP "SQLITE_VERSION_OKAY" >/dev/null 2>&1; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: okay" >&5
$as_echo "okay" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sqlite3_close in -lsqlite3" >&5
$as_echo_n "checking for sqlite3_close in -lsqlite3... " >&6; }
if ${ac_cv_lib_sqlite3_sqlite3_close+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lsqlite3 $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char sqlite3_close ();
int
main ()
{
return sqlite3_close ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_sqlite3_sqlite3_close=yes
else
ac_cv_lib_sqlite3_sqlite3_close=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sqlite3_sqlite3_close" >&5
$as_echo "$ac_cv_lib_sqlite3_sqlite3_close" >&6; }
if test "x$ac_cv_lib_sqlite3_sqlite3_close" = xyes; then :
svn_lib_sqlite="yes"
if test -z "$sqlite_dir" -o ! -d "$sqlite_dir"; then
SVN_SQLITE_LIBS="-lsqlite3"
else
SVN_SQLITE_INCLUDES="-I$sqlite_dir/include"
SVN_SQLITE_LIBS="`
input_flags="-L$sqlite_dir/lib -lsqlite3"
output_flags=""
filtered_dirs="/lib /lib64 /usr/lib /usr/lib64"
for flag in $input_flags; do
filter="no"
for dir in $filtered_dirs; do
if test "$flag" = "-L$dir" || test "$flag" = "-L$dir/"; then
filter="yes"
break
fi
done
if test "$filter" = "no"; then
output_flags="$output_flags $flag"
fi
done
if test -n "$output_flags"; then
printf "%s" "${output_flags# }"
fi
`"
fi
fi
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported SQLite version" >&5
$as_echo "unsupported SQLite version" >&6; }
fi
rm -f conftest*
fi
CPPFLAGS="$save_CPPFLAGS"
LDFLAGS="$save_LDFLAGS"
fi
if test -z "$svn_lib_sqlite"; then
if test -n "$PKG_CONFIG"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking sqlite library version (via pkg-config)" >&5
$as_echo_n "checking sqlite library version (via pkg-config)... " >&6; }
sqlite_version=`$PKG_CONFIG $SQLITE_PKGNAME --modversion --silence-errors`
if test -n "$sqlite_version"; then
version_string="$sqlite_version"
major=`expr $version_string : '\([0-9]*\)'`
minor=`expr $version_string : '[0-9]*\.\([0-9]*\)'`
micro=`expr $version_string : '[0-9]*\.[0-9]*\.\([0-9]*\)'`
if test -z "$micro"; then
micro=0
fi
sqlite_ver_num=`expr $major \* 1000000 \
\+ $minor \* 1000 \
\+ $micro`
if test "$sqlite_ver_num" -ge "$sqlite_min_ver_num"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $sqlite_version" >&5
$as_echo "$sqlite_version" >&6; }
svn_lib_sqlite="yes"
SVN_SQLITE_INCLUDES="`$PKG_CONFIG $SQLITE_PKGNAME --cflags`"
SVN_SQLITE_LIBS="`$PKG_CONFIG $SQLITE_PKGNAME --libs`"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: none or unsupported $sqlite_version" >&5
$as_echo "none or unsupported $sqlite_version" >&6; }
fi
fi
fi
if test -z "$svn_lib_sqlite"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$svn_lib_sqlite"; then
echo ""
echo "An appropriate version of sqlite could not be found. We recommmend"
echo "${SQLITE_RECOMMENDED_VER}, but require at least ${SQLITE_MINIMUM_VER}."
echo "Please either install a newer sqlite on this system"
echo ""
echo "or"
echo ""
echo "get the sqlite ${SQLITE_RECOMMENDED_VER} amalgamation from:"
echo " ${SQLITE_URL}"
echo "unpack the archive using unzip and rename the resulting"
echo "directory to:"
echo "$abs_srcdir/sqlite-amalgamation"
if test x"$abs_srcdir" != x"$abs_builddir"; then
echo "or to:"
echo "$abs_builddir/sqlite-amalgamation"
fi
echo ""
as_fn_error $? "Subversion requires SQLite" "$LINENO" 5
fi
fi
# Check whether --enable-sqlite-compatibility-version was given.
if test "${enable_sqlite_compatibility_version+set}" = set; then :
enableval=$enable_sqlite_compatibility_version; sqlite_compat_ver=$enableval
else
sqlite_compat_ver=no
fi
if test -n "$sqlite_compat_ver" && test "$sqlite_compat_ver" != no; then
version_string="$sqlite_compat_ver"
major=`expr $version_string : '\([0-9]*\)'`
minor=`expr $version_string : '[0-9]*\.\([0-9]*\)'`
micro=`expr $version_string : '[0-9]*\.[0-9]*\.\([0-9]*\)'`
if test -z "$micro"; then
micro=0
fi
sqlite_compat_ver_num=`expr $major \* 1000000 \
\+ $minor \* 1000 \
\+ $micro`
CFLAGS="-DSVN_SQLITE_MIN_VERSION='\"$sqlite_compat_ver\"' $CFLAGS"
CFLAGS="-DSVN_SQLITE_MIN_VERSION_NUMBER=$sqlite_compat_ver_num $CFLAGS"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler provides atomic builtins" >&5
$as_echo_n "checking whether the compiler provides atomic builtins... " >&6; }
if ${svn_cv_atomic_builtins+:} false; then :
$as_echo_n "(cached) " >&6
else
if test "$cross_compiling" = yes; then :
svn_cv_atomic_builtins=no
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main()
{
unsigned long long val = 1010, tmp, *mem = &val;
if (__sync_fetch_and_add(&val, 1010) != 1010 || val != 2020)
return 1;
tmp = val;
if (__sync_fetch_and_sub(mem, 1010) != tmp || val != 1010)
return 1;
if (__sync_sub_and_fetch(&val, 1010) != 0 || val != 0)
return 1;
tmp = 3030;
if (__sync_val_compare_and_swap(mem, 0, tmp) != 0 || val != tmp)
return 1;
if (__sync_lock_test_and_set(&val, 4040) != 3030)
return 1;
mem = &tmp;
if (__sync_val_compare_and_swap(&mem, &tmp, &val) != &tmp)
return 1;
__sync_synchronize();
if (mem != &val)
return 1;
return 0;
}
_ACEOF
if ac_fn_c_try_run "$LINENO"; then :
svn_cv_atomic_builtins=yes
else
svn_cv_atomic_builtins=no
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $svn_cv_atomic_builtins" >&5
$as_echo "$svn_cv_atomic_builtins" >&6; }
if test "$svn_cv_atomic_builtins" = "yes"; then
$as_echo "#define SVN_HAS_ATOMIC_BUILTINS 1" >>confdefs.h
fi
if test "${bindir}" = '${exec_prefix}/bin'; then
if test "${exec_prefix}" = "NONE"; then
if test "${prefix}" = "NONE"; then
SVN_BINDIR="${ac_default_prefix}/bin"
else
SVN_BINDIR="${prefix}/bin"
fi
else
SVN_BINDIR="${exec_prefix}/bin"
fi
else
SVN_BINDIR="${bindir}"
fi
SVN_BINDIR="`eval echo ${SVN_BINDIR}`"
cat >>confdefs.h <<_ACEOF
#define SVN_BINDIR "${SVN_BINDIR}"
_ACEOF
localedir='${datadir}/locale'
if test "${datadir}" = '${prefix}/share' && test "${prefix}" = "NONE"; then
exp_localedir='${ac_default_prefix}/share/locale'
else
exp_localedir=$localedir
fi
svn_last=
svn_cur=""${exp_localedir}""
while test "x${svn_cur}" != "x${svn_last}";
do
svn_last="${svn_cur}"
svn_cur=`eval "echo ${svn_cur}"`
done
svn_localedir="${svn_cur}"
cat >>confdefs.h <<_ACEOF
#define SVN_LOCALE_DIR "${svn_localedir}"
_ACEOF
{ $as_echo "$as_me:${as_lineno-$LINENO}: configuring libtool now" >&5
$as_echo "$as_me: configuring libtool now" >&6;}
case `pwd` in
*\ * | *\ *)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5
$as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;;
esac
-macro_version='2.4.2'
-macro_revision='1.3337'
+macro_version='2.4.2.458.26-92994'
+macro_revision='2.4.3'
-ltmain="$ac_aux_dir/ltmain.sh"
+ltmain=$ac_aux_dir/ltmain.sh
# Backslashify metacharacters that are still active within
# double-quoted strings.
sed_quote_subst='s/\(["`$\\]\)/\\\1/g'
# Same as above, but do not quote variable references.
double_quote_subst='s/\(["`\\]\)/\\\1/g'
# Sed substitution to delay expansion of an escaped shell variable in a
# double_quote_subst'ed string.
delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
# Sed substitution to delay expansion of an escaped single quote.
delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
# Sed substitution to avoid accidental globbing in evaled expressions
no_glob_subst='s/\*/\\\*/g'
ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO
ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5
$as_echo_n "checking how to print strings... " >&6; }
# Test print first, because it will be a builtin if present.
if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \
test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then
ECHO='print -r --'
elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then
ECHO='printf %s\n'
else
# Use this function as a fallback that always works.
func_fallback_echo ()
{
eval 'cat <<_LTECHO_EOF
$1
_LTECHO_EOF'
}
ECHO='func_fallback_echo'
fi
# func_echo_all arg...
# Invoke $ECHO with all args, space-separated.
func_echo_all ()
{
$ECHO ""
}
-case "$ECHO" in
+case $ECHO in
printf*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: printf" >&5
$as_echo "printf" >&6; } ;;
print*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: print -r" >&5
$as_echo "print -r" >&6; } ;;
*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: cat" >&5
$as_echo "cat" >&6; } ;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5
$as_echo_n "checking for a sed that does not truncate output... " >&6; }
if ${ac_cv_path_SED+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
for ac_i in 1 2 3 4 5 6 7; do
ac_script="$ac_script$as_nl$ac_script"
done
echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed
{ ac_script=; unset ac_script;}
if test -z "$SED"; then
ac_path_SED_found=false
# Loop through the user's path and test for each of PROGNAME-LIST
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_prog in sed gsed; do
for ac_exec_ext in '' $ac_executable_extensions; do
ac_path_SED="$as_dir/$ac_prog$ac_exec_ext"
as_fn_executable_p "$ac_path_SED" || continue
# Check for GNU ac_path_SED and select it if it is found.
# Check for GNU $ac_path_SED
case `"$ac_path_SED" --version 2>&1` in
*GNU*)
ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;;
*)
ac_count=0
$as_echo_n 0123456789 >"conftest.in"
while :
do
cat "conftest.in" "conftest.in" >"conftest.tmp"
mv "conftest.tmp" "conftest.in"
cp "conftest.in" "conftest.nl"
$as_echo '' >> "conftest.nl"
"$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break
diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
as_fn_arith $ac_count + 1 && ac_count=$as_val
if test $ac_count -gt ${ac_path_SED_max-0}; then
# Best one so far, save it but keep looking for a better one
ac_cv_path_SED="$ac_path_SED"
ac_path_SED_max=$ac_count
fi
# 10*(2^10) chars as input seems more than enough
test $ac_count -gt 10 && break
done
rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
esac
$ac_path_SED_found && break 3
done
done
done
IFS=$as_save_IFS
if test -z "$ac_cv_path_SED"; then
as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5
fi
else
ac_cv_path_SED=$SED
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5
$as_echo "$ac_cv_path_SED" >&6; }
SED="$ac_cv_path_SED"
rm -f conftest.sed
test -z "$SED" && SED=sed
Xsed="$SED -e 1s/^X//"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5
$as_echo_n "checking for fgrep... " >&6; }
if ${ac_cv_path_FGREP+:} false; then :
$as_echo_n "(cached) " >&6
else
if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1
then ac_cv_path_FGREP="$GREP -F"
else
if test -z "$FGREP"; then
ac_path_FGREP_found=false
# Loop through the user's path and test for each of PROGNAME-LIST
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_prog in fgrep; do
for ac_exec_ext in '' $ac_executable_extensions; do
ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext"
as_fn_executable_p "$ac_path_FGREP" || continue
# Check for GNU ac_path_FGREP and select it if it is found.
# Check for GNU $ac_path_FGREP
case `"$ac_path_FGREP" --version 2>&1` in
*GNU*)
ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;;
*)
ac_count=0
$as_echo_n 0123456789 >"conftest.in"
while :
do
cat "conftest.in" "conftest.in" >"conftest.tmp"
mv "conftest.tmp" "conftest.in"
cp "conftest.in" "conftest.nl"
$as_echo 'FGREP' >> "conftest.nl"
"$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break
diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
as_fn_arith $ac_count + 1 && ac_count=$as_val
if test $ac_count -gt ${ac_path_FGREP_max-0}; then
# Best one so far, save it but keep looking for a better one
ac_cv_path_FGREP="$ac_path_FGREP"
ac_path_FGREP_max=$ac_count
fi
# 10*(2^10) chars as input seems more than enough
test $ac_count -gt 10 && break
done
rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
esac
$ac_path_FGREP_found && break 3
done
done
done
IFS=$as_save_IFS
if test -z "$ac_cv_path_FGREP"; then
as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
fi
else
ac_cv_path_FGREP=$FGREP
fi
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5
$as_echo "$ac_cv_path_FGREP" >&6; }
FGREP="$ac_cv_path_FGREP"
test -z "$GREP" && GREP=grep
# Check whether --with-gnu-ld was given.
if test "${with_gnu_ld+set}" = set; then :
- withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes
+ withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes
else
with_gnu_ld=no
fi
ac_prog=ld
-if test "$GCC" = yes; then
+if test yes = "$GCC"; then
# Check if gcc -print-prog-name=ld gives a path.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5
$as_echo_n "checking for ld used by $CC... " >&6; }
case $host in
*-*-mingw*)
- # gcc leaves a trailing carriage return which upsets mingw
+ # gcc leaves a trailing carriage return, which upsets mingw
ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
*)
ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
esac
case $ac_prog in
# Accept absolute paths.
[\\/]* | ?:[\\/]*)
re_direlt='/[^/][^/]*/\.\./'
# Canonicalize the pathname of ld
ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
done
- test -z "$LD" && LD="$ac_prog"
+ test -z "$LD" && LD=$ac_prog
;;
"")
# If it fails, then pretend we aren't using GCC.
ac_prog=ld
;;
*)
# If it is relative, then search for the first ld in PATH.
with_gnu_ld=unknown
;;
esac
-elif test "$with_gnu_ld" = yes; then
+elif test yes = "$with_gnu_ld"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5
$as_echo_n "checking for GNU ld... " >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5
$as_echo_n "checking for non-GNU ld... " >&6; }
fi
if ${lt_cv_path_LD+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -z "$LD"; then
- lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
for ac_dir in $PATH; do
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
test -z "$ac_dir" && ac_dir=.
if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
- lt_cv_path_LD="$ac_dir/$ac_prog"
+ lt_cv_path_LD=$ac_dir/$ac_prog
# Check to see if the program is GNU ld. I'd rather use --version,
# but apparently some variants of GNU ld only accept -v.
# Break only if it was the GNU/non-GNU ld that we prefer.
case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
*GNU* | *'with BFD'*)
- test "$with_gnu_ld" != no && break
+ test no != "$with_gnu_ld" && break
;;
*)
- test "$with_gnu_ld" != yes && break
+ test yes != "$with_gnu_ld" && break
;;
esac
fi
done
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
else
- lt_cv_path_LD="$LD" # Let the user override the test with a path.
+ lt_cv_path_LD=$LD # Let the user override the test with a path.
fi
fi
-LD="$lt_cv_path_LD"
+LD=$lt_cv_path_LD
if test -n "$LD"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5
$as_echo "$LD" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5
$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; }
if ${lt_cv_prog_gnu_ld+:} false; then :
$as_echo_n "(cached) " >&6
else
# I'd rather use --version here, but apparently some GNU lds only accept -v.
case `$LD -v 2>&1 </dev/null` in
*GNU* | *'with BFD'*)
lt_cv_prog_gnu_ld=yes
;;
*)
lt_cv_prog_gnu_ld=no
;;
esac
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_gnu_ld" >&5
$as_echo "$lt_cv_prog_gnu_ld" >&6; }
with_gnu_ld=$lt_cv_prog_gnu_ld
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5
$as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; }
if ${lt_cv_path_NM+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$NM"; then
# Let the user override the test.
- lt_cv_path_NM="$NM"
+ lt_cv_path_NM=$NM
else
- lt_nm_to_check="${ac_tool_prefix}nm"
+ lt_nm_to_check=${ac_tool_prefix}nm
if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
lt_nm_to_check="$lt_nm_to_check nm"
fi
for lt_tmp_nm in $lt_nm_to_check; do
- lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
test -z "$ac_dir" && ac_dir=.
- tmp_nm="$ac_dir/$lt_tmp_nm"
- if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then
+ tmp_nm=$ac_dir/$lt_tmp_nm
+ if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then
# Check to see if the nm accepts a BSD-compat flag.
- # Adding the `sed 1q' prevents false positives on HP-UX, which says:
+ # Adding the 'sed 1q' prevents false positives on HP-UX, which says:
# nm: unknown option "B" ignored
# Tru64's nm complains that /dev/null is an invalid object file
- case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in
- */dev/null* | *'Invalid file or object type'*)
+ # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty
+ case $build_os in
+ mingw*) lt_bad_file=conftest.nm/nofile ;;
+ *) lt_bad_file=/dev/null ;;
+ esac
+ case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in
+ *$lt_bad_file* | *'Invalid file or object type'*)
lt_cv_path_NM="$tmp_nm -B"
- break
+ break 2
;;
*)
case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
*/dev/null*)
lt_cv_path_NM="$tmp_nm -p"
- break
+ break 2
;;
*)
lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
continue # so that we can try to find one that supports BSD flags
;;
esac
;;
esac
fi
done
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
done
: ${lt_cv_path_NM=no}
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5
$as_echo "$lt_cv_path_NM" >&6; }
-if test "$lt_cv_path_NM" != "no"; then
- NM="$lt_cv_path_NM"
+if test no != "$lt_cv_path_NM"; then
+ NM=$lt_cv_path_NM
else
# Didn't find any BSD compatible name lister, look for dumpbin.
if test -n "$DUMPBIN"; then :
# Let the user override the test.
else
if test -n "$ac_tool_prefix"; then
for ac_prog in dumpbin "link -dump"
do
# Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
set dummy $ac_tool_prefix$ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_DUMPBIN+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$DUMPBIN"; then
ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
DUMPBIN=$ac_cv_prog_DUMPBIN
if test -n "$DUMPBIN"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5
$as_echo "$DUMPBIN" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
test -n "$DUMPBIN" && break
done
fi
if test -z "$DUMPBIN"; then
ac_ct_DUMPBIN=$DUMPBIN
for ac_prog in dumpbin "link -dump"
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_DUMPBIN"; then
ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_DUMPBIN="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN
if test -n "$ac_ct_DUMPBIN"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5
$as_echo "$ac_ct_DUMPBIN" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
test -n "$ac_ct_DUMPBIN" && break
done
if test "x$ac_ct_DUMPBIN" = x; then
DUMPBIN=":"
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
DUMPBIN=$ac_ct_DUMPBIN
fi
fi
- case `$DUMPBIN -symbols /dev/null 2>&1 | sed '1q'` in
+ case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in
*COFF*)
- DUMPBIN="$DUMPBIN -symbols"
+ DUMPBIN="$DUMPBIN -symbols -headers"
;;
*)
DUMPBIN=:
;;
esac
fi
- if test "$DUMPBIN" != ":"; then
- NM="$DUMPBIN"
+ if test : != "$DUMPBIN"; then
+ NM=$DUMPBIN
fi
fi
test -z "$NM" && NM=nm
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5
$as_echo_n "checking the name lister ($NM) interface... " >&6; }
if ${lt_cv_nm_interface+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_nm_interface="BSD nm"
echo "int some_variable = 0;" > conftest.$ac_ext
(eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5)
(eval "$ac_compile" 2>conftest.err)
cat conftest.err >&5
(eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
(eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
cat conftest.err >&5
(eval echo "\"\$as_me:$LINENO: output\"" >&5)
cat conftest.out >&5
if $GREP 'External.*some_variable' conftest.out > /dev/null; then
lt_cv_nm_interface="MS dumpbin"
fi
rm -f conftest*
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5
$as_echo "$lt_cv_nm_interface" >&6; }
# find the maximum length of command line arguments
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5
$as_echo_n "checking the maximum length of command line arguments... " >&6; }
if ${lt_cv_sys_max_cmd_len+:} false; then :
$as_echo_n "(cached) " >&6
else
i=0
- teststring="ABCD"
+ teststring=ABCD
case $build_os in
msdosdjgpp*)
# On DJGPP, this test can blow up pretty badly due to problems in libc
# (any single argument exceeding 2000 bytes causes a buffer overrun
# during glob expansion). Even if it were fixed, the result of this
# check would be larger than it should be.
lt_cv_sys_max_cmd_len=12288; # 12K is about right
;;
gnu*)
# Under GNU Hurd, this test is not required because there is
# no limit to the length of command line arguments.
# Libtool will interpret -1 as no limit whatsoever
lt_cv_sys_max_cmd_len=-1;
;;
cygwin* | mingw* | cegcc*)
# On Win9x/ME, this test blows up -- it succeeds, but takes
# about 5 minutes as the teststring grows exponentially.
# Worse, since 9x/ME are not pre-emptively multitasking,
# you end up with a "frozen" computer, even though with patience
# the test eventually succeeds (with a max line length of 256k).
# Instead, let's just punt: use the minimum linelength reported by
# all of the supported platforms: 8192 (on NT/2K/XP).
lt_cv_sys_max_cmd_len=8192;
;;
mint*)
# On MiNT this can take a long time and run out of memory.
lt_cv_sys_max_cmd_len=8192;
;;
amigaos*)
# On AmigaOS with pdksh, this test takes hours, literally.
# So we just punt and use a minimum line length of 8192.
lt_cv_sys_max_cmd_len=8192;
;;
- netbsd* | freebsd* | openbsd* | darwin* | dragonfly*)
+ bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*)
# This has been around since 386BSD, at least. Likely further.
if test -x /sbin/sysctl; then
lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
elif test -x /usr/sbin/sysctl; then
lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
else
lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs
fi
# And add a safety zone
lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
;;
interix*)
# We know the value 262144 and hardcode it with a safety zone (like BSD)
lt_cv_sys_max_cmd_len=196608
;;
os2*)
# The test takes a long time on OS/2.
lt_cv_sys_max_cmd_len=8192
;;
osf*)
# Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
# due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
# nice to cause kernel panics so lets avoid the loop below.
# First set a reasonable default.
lt_cv_sys_max_cmd_len=16384
#
if test -x /sbin/sysconfig; then
case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
*1*) lt_cv_sys_max_cmd_len=-1 ;;
esac
fi
;;
sco3.2v5*)
lt_cv_sys_max_cmd_len=102400
;;
sysv5* | sco5v6* | sysv4.2uw2*)
kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
if test -n "$kargmax"; then
lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'`
else
lt_cv_sys_max_cmd_len=32768
fi
;;
*)
lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
- if test -n "$lt_cv_sys_max_cmd_len"; then
+ if test -n "$lt_cv_sys_max_cmd_len" && \
+ test undefined != "$lt_cv_sys_max_cmd_len"; then
lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
else
# Make teststring a little bigger before we do anything with it.
# a 1K string should be a reasonable start.
- for i in 1 2 3 4 5 6 7 8 ; do
+ for i in 1 2 3 4 5 6 7 8; do
teststring=$teststring$teststring
done
SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
# If test is not a shell built-in, we'll probably end up computing a
# maximum length that is only half of the actual maximum length, but
# we can't tell.
- while { test "X"`env echo "$teststring$teststring" 2>/dev/null` \
+ while { test X`env echo "$teststring$teststring" 2>/dev/null` \
= "X$teststring$teststring"; } >/dev/null 2>&1 &&
- test $i != 17 # 1/2 MB should be enough
+ test 17 != "$i" # 1/2 MB should be enough
do
i=`expr $i + 1`
teststring=$teststring$teststring
done
# Only check the string length outside the loop.
lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
teststring=
# Add a significant safety factor because C++ compilers can tack on
# massive amounts of additional arguments before passing them to the
# linker. It appears as though 1/2 is a usable value.
lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
fi
;;
esac
fi
-if test -n $lt_cv_sys_max_cmd_len ; then
+if test -n "$lt_cv_sys_max_cmd_len"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5
$as_echo "$lt_cv_sys_max_cmd_len" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5
$as_echo "none" >&6; }
fi
max_cmd_len=$lt_cv_sys_max_cmd_len
: ${CP="cp -f"}
: ${MV="mv -f"}
: ${RM="rm -f"}
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands some XSI constructs" >&5
-$as_echo_n "checking whether the shell understands some XSI constructs... " >&6; }
-# Try some XSI features
-xsi_shell=no
-( _lt_dummy="a/b/c"
- test "${_lt_dummy##*/},${_lt_dummy%/*},${_lt_dummy#??}"${_lt_dummy%"$_lt_dummy"}, \
- = c,a/b,b/c, \
- && eval 'test $(( 1 + 1 )) -eq 2 \
- && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \
- && xsi_shell=yes
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $xsi_shell" >&5
-$as_echo "$xsi_shell" >&6; }
-
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands \"+=\"" >&5
-$as_echo_n "checking whether the shell understands \"+=\"... " >&6; }
-lt_shell_append=no
-( foo=bar; set foo baz; eval "$1+=\$2" && test "$foo" = barbaz ) \
- >/dev/null 2>&1 \
- && lt_shell_append=yes
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_shell_append" >&5
-$as_echo "$lt_shell_append" >&6; }
-
-
if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
lt_unset=unset
else
lt_unset=false
fi
# test EBCDIC or ASCII
case `echo X|tr X '\101'` in
A) # ASCII based system
# \n is not interpreted correctly by Solaris 8 /usr/ucb/tr
lt_SP2NL='tr \040 \012'
lt_NL2SP='tr \015\012 \040\040'
;;
*) # EBCDIC based system
lt_SP2NL='tr \100 \n'
lt_NL2SP='tr \r\n \100\100'
;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5
$as_echo_n "checking how to convert $build file names to $host format... " >&6; }
if ${lt_cv_to_host_file_cmd+:} false; then :
$as_echo_n "(cached) " >&6
else
case $host in
*-*-mingw* )
case $build in
*-*-mingw* ) # actually msys
lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32
;;
*-*-cygwin* )
lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32
;;
* ) # otherwise, assume *nix
lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32
;;
esac
;;
*-*-cygwin* )
case $build in
*-*-mingw* ) # actually msys
lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin
;;
*-*-cygwin* )
lt_cv_to_host_file_cmd=func_convert_file_noop
;;
* ) # otherwise, assume *nix
lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin
;;
esac
;;
* ) # unhandled hosts (and "normal" native builds)
lt_cv_to_host_file_cmd=func_convert_file_noop
;;
esac
fi
to_host_file_cmd=$lt_cv_to_host_file_cmd
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd" >&5
$as_echo "$lt_cv_to_host_file_cmd" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5
$as_echo_n "checking how to convert $build file names to toolchain format... " >&6; }
if ${lt_cv_to_tool_file_cmd+:} false; then :
$as_echo_n "(cached) " >&6
else
#assume ordinary cross tools, or native build.
lt_cv_to_tool_file_cmd=func_convert_file_noop
case $host in
*-*-mingw* )
case $build in
*-*-mingw* ) # actually msys
lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32
;;
esac
;;
esac
fi
to_tool_file_cmd=$lt_cv_to_tool_file_cmd
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd" >&5
$as_echo "$lt_cv_to_tool_file_cmd" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5
$as_echo_n "checking for $LD option to reload object files... " >&6; }
if ${lt_cv_ld_reload_flag+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_ld_reload_flag='-r'
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5
$as_echo "$lt_cv_ld_reload_flag" >&6; }
reload_flag=$lt_cv_ld_reload_flag
case $reload_flag in
"" | " "*) ;;
*) reload_flag=" $reload_flag" ;;
esac
reload_cmds='$LD$reload_flag -o $output$reload_objs'
case $host_os in
cygwin* | mingw* | pw32* | cegcc*)
- if test "$GCC" != yes; then
+ if test yes != "$GCC"; then
reload_cmds=false
fi
;;
darwin*)
- if test "$GCC" = yes; then
- reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs'
+ if test yes = "$GCC"; then
+ reload_cmds='$LTCC $LTCFLAGS -nostdlib $wl-r -o $output$reload_objs'
else
reload_cmds='$LD$reload_flag -o $output$reload_objs'
fi
;;
esac
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args.
set dummy ${ac_tool_prefix}objdump; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_OBJDUMP+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$OBJDUMP"; then
ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
OBJDUMP=$ac_cv_prog_OBJDUMP
if test -n "$OBJDUMP"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5
$as_echo "$OBJDUMP" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$ac_cv_prog_OBJDUMP"; then
ac_ct_OBJDUMP=$OBJDUMP
# Extract the first word of "objdump", so it can be a program name with args.
set dummy objdump; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_OBJDUMP"; then
ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_OBJDUMP="objdump"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP
if test -n "$ac_ct_OBJDUMP"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5
$as_echo "$ac_ct_OBJDUMP" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "x$ac_ct_OBJDUMP" = x; then
OBJDUMP="false"
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
OBJDUMP=$ac_ct_OBJDUMP
fi
else
OBJDUMP="$ac_cv_prog_OBJDUMP"
fi
test -z "$OBJDUMP" && OBJDUMP=objdump
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5
$as_echo_n "checking how to recognize dependent libraries... " >&6; }
if ${lt_cv_deplibs_check_method+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_file_magic_cmd='$MAGIC_CMD'
lt_cv_file_magic_test_file=
lt_cv_deplibs_check_method='unknown'
# Need to set the preceding variable on all platforms that support
# interlibrary dependencies.
# 'none' -- dependencies not supported.
-# `unknown' -- same as none, but documents that we really don't know.
+# 'unknown' -- same as none, but documents that we really don't know.
# 'pass_all' -- all dependencies passed with no checks.
# 'test_compile' -- check by making test program.
# 'file_magic [[regex]]' -- check by looking for files in library path
-# which responds to the $file_magic_cmd with a given extended regex.
-# If you have `file' or equivalent on your system and you're not sure
-# whether `pass_all' will *always* work, you probably want this one.
+# that responds to the $file_magic_cmd with a given extended regex.
+# If you have 'file' or equivalent on your system and you're not sure
+# whether 'pass_all' will *always* work, you probably want this one.
case $host_os in
aix[4-9]*)
lt_cv_deplibs_check_method=pass_all
;;
beos*)
lt_cv_deplibs_check_method=pass_all
;;
bsdi[45]*)
lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)'
lt_cv_file_magic_cmd='/usr/bin/file -L'
lt_cv_file_magic_test_file=/shlib/libc.so
;;
cygwin*)
# func_win32_libid is a shell function defined in ltmain.sh
lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
lt_cv_file_magic_cmd='func_win32_libid'
;;
mingw* | pw32*)
# Base MSYS/MinGW do not provide the 'file' command needed by
# func_win32_libid shell function, so use a weaker test based on 'objdump',
# unless we find 'file', for example because we are cross-compiling.
- # func_win32_libid assumes BSD nm, so disallow it if using MS dumpbin.
- if ( test "$lt_cv_nm_interface" = "BSD nm" && file / ) >/dev/null 2>&1; then
+ if ( file / ) >/dev/null 2>&1; then
lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
lt_cv_file_magic_cmd='func_win32_libid'
else
# Keep this pattern in sync with the one in func_win32_libid.
lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)'
lt_cv_file_magic_cmd='$OBJDUMP -f'
fi
;;
cegcc*)
# use the weaker test based on 'objdump'. See mingw*.
lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?'
lt_cv_file_magic_cmd='$OBJDUMP -f'
;;
darwin* | rhapsody*)
lt_cv_deplibs_check_method=pass_all
;;
freebsd* | dragonfly*)
if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
case $host_cpu in
i*86 )
# Not sure whether the presence of OpenBSD here was a mistake.
# Let's accept both of them until this is cleared up.
lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library'
lt_cv_file_magic_cmd=/usr/bin/file
lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
;;
esac
else
lt_cv_deplibs_check_method=pass_all
fi
;;
-gnu*)
- lt_cv_deplibs_check_method=pass_all
- ;;
-
haiku*)
lt_cv_deplibs_check_method=pass_all
;;
hpux10.20* | hpux11*)
lt_cv_file_magic_cmd=/usr/bin/file
case $host_cpu in
ia64*)
lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64'
lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
;;
hppa*64*)
lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'
lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
;;
*)
lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\.[0-9]) shared library'
lt_cv_file_magic_test_file=/usr/lib/libc.sl
;;
esac
;;
interix[3-9]*)
# PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here
lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$'
;;
irix5* | irix6* | nonstopux*)
case $LD in
*-32|*"-32 ") libmagic=32-bit;;
*-n32|*"-n32 ") libmagic=N32;;
*-64|*"-64 ") libmagic=64-bit;;
*) libmagic=never-match;;
esac
lt_cv_deplibs_check_method=pass_all
;;
# This must be glibc/ELF.
-linux* | k*bsd*-gnu | kopensolaris*-gnu)
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
lt_cv_deplibs_check_method=pass_all
;;
netbsd*)
if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
else
lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$'
fi
;;
newos6*)
lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)'
lt_cv_file_magic_cmd=/usr/bin/file
lt_cv_file_magic_test_file=/usr/lib/libnls.so
;;
*nto* | *qnx*)
lt_cv_deplibs_check_method=pass_all
;;
-openbsd*)
- if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+openbsd* | bitrig*)
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$'
else
lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
fi
;;
osf3* | osf4* | osf5*)
lt_cv_deplibs_check_method=pass_all
;;
rdos*)
lt_cv_deplibs_check_method=pass_all
;;
solaris*)
lt_cv_deplibs_check_method=pass_all
;;
sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
lt_cv_deplibs_check_method=pass_all
;;
sysv4 | sysv4.3*)
case $host_vendor in
motorola)
lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]'
lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
;;
ncr)
lt_cv_deplibs_check_method=pass_all
;;
sequent)
lt_cv_file_magic_cmd='/bin/file'
lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )'
;;
sni)
lt_cv_file_magic_cmd='/bin/file'
lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib"
lt_cv_file_magic_test_file=/lib/libc.so
;;
siemens)
lt_cv_deplibs_check_method=pass_all
;;
pc)
lt_cv_deplibs_check_method=pass_all
;;
esac
;;
tpf*)
lt_cv_deplibs_check_method=pass_all
;;
esac
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5
$as_echo "$lt_cv_deplibs_check_method" >&6; }
file_magic_glob=
want_nocaseglob=no
if test "$build" = "$host"; then
case $host_os in
mingw* | pw32*)
if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then
want_nocaseglob=yes
else
file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[\1]\/[\1]\/g;/g"`
fi
;;
esac
fi
file_magic_cmd=$lt_cv_file_magic_cmd
deplibs_check_method=$lt_cv_deplibs_check_method
test -z "$deplibs_check_method" && deplibs_check_method=unknown
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args.
set dummy ${ac_tool_prefix}dlltool; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_DLLTOOL+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$DLLTOOL"; then
ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
DLLTOOL=$ac_cv_prog_DLLTOOL
if test -n "$DLLTOOL"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5
$as_echo "$DLLTOOL" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$ac_cv_prog_DLLTOOL"; then
ac_ct_DLLTOOL=$DLLTOOL
# Extract the first word of "dlltool", so it can be a program name with args.
set dummy dlltool; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_DLLTOOL"; then
ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_DLLTOOL="dlltool"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL
if test -n "$ac_ct_DLLTOOL"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5
$as_echo "$ac_ct_DLLTOOL" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "x$ac_ct_DLLTOOL" = x; then
DLLTOOL="false"
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
DLLTOOL=$ac_ct_DLLTOOL
fi
else
DLLTOOL="$ac_cv_prog_DLLTOOL"
fi
test -z "$DLLTOOL" && DLLTOOL=dlltool
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5
$as_echo_n "checking how to associate runtime and link libraries... " >&6; }
if ${lt_cv_sharedlib_from_linklib_cmd+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_sharedlib_from_linklib_cmd='unknown'
case $host_os in
cygwin* | mingw* | pw32* | cegcc*)
- # two different shell functions defined in ltmain.sh
- # decide which to use based on capabilities of $DLLTOOL
+ # two different shell functions defined in ltmain.sh;
+ # decide which one to use based on capabilities of $DLLTOOL
case `$DLLTOOL --help 2>&1` in
*--identify-strict*)
lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib
;;
*)
lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback
;;
esac
;;
*)
# fallback: assume linklib IS sharedlib
- lt_cv_sharedlib_from_linklib_cmd="$ECHO"
+ lt_cv_sharedlib_from_linklib_cmd=$ECHO
;;
esac
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5
$as_echo "$lt_cv_sharedlib_from_linklib_cmd" >&6; }
sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd
test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO
if test -n "$ac_tool_prefix"; then
for ac_prog in ar
do
# Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
set dummy $ac_tool_prefix$ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_AR+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$AR"; then
ac_cv_prog_AR="$AR" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_AR="$ac_tool_prefix$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
AR=$ac_cv_prog_AR
if test -n "$AR"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
$as_echo "$AR" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
test -n "$AR" && break
done
fi
if test -z "$AR"; then
ac_ct_AR=$AR
for ac_prog in ar
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_AR+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_AR"; then
ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_AR="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_AR=$ac_cv_prog_ac_ct_AR
if test -n "$ac_ct_AR"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5
$as_echo "$ac_ct_AR" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
test -n "$ac_ct_AR" && break
done
if test "x$ac_ct_AR" = x; then
AR="false"
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
AR=$ac_ct_AR
fi
fi
: ${AR=ar}
: ${AR_FLAGS=cru}
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5
$as_echo_n "checking for archiver @FILE support... " >&6; }
if ${lt_cv_ar_at_file+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_ar_at_file=no
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
echo conftest.$ac_objext > conftest.lst
lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5'
{ { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5
(eval $lt_ar_try) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }
- if test "$ac_status" -eq 0; then
+ if test 0 -eq "$ac_status"; then
# Ensure the archiver fails upon bogus file names.
rm -f conftest.$ac_objext libconftest.a
{ { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5
(eval $lt_ar_try) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }
- if test "$ac_status" -ne 0; then
+ if test 0 -ne "$ac_status"; then
lt_cv_ar_at_file=@
fi
fi
rm -f conftest.* libconftest.a
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5
$as_echo "$lt_cv_ar_at_file" >&6; }
-if test "x$lt_cv_ar_at_file" = xno; then
+if test no = "$lt_cv_ar_at_file"; then
archiver_list_spec=
else
archiver_list_spec=$lt_cv_ar_at_file
fi
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
set dummy ${ac_tool_prefix}strip; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_STRIP+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$STRIP"; then
ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_STRIP="${ac_tool_prefix}strip"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
STRIP=$ac_cv_prog_STRIP
if test -n "$STRIP"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
$as_echo "$STRIP" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$ac_cv_prog_STRIP"; then
ac_ct_STRIP=$STRIP
# Extract the first word of "strip", so it can be a program name with args.
set dummy strip; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_STRIP+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_STRIP"; then
ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_STRIP="strip"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
if test -n "$ac_ct_STRIP"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
$as_echo "$ac_ct_STRIP" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "x$ac_ct_STRIP" = x; then
STRIP=":"
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
STRIP=$ac_ct_STRIP
fi
else
STRIP="$ac_cv_prog_STRIP"
fi
test -z "$STRIP" && STRIP=:
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
set dummy ${ac_tool_prefix}ranlib; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_RANLIB+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$RANLIB"; then
ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
RANLIB=$ac_cv_prog_RANLIB
if test -n "$RANLIB"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5
$as_echo "$RANLIB" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$ac_cv_prog_RANLIB"; then
ac_ct_RANLIB=$RANLIB
# Extract the first word of "ranlib", so it can be a program name with args.
set dummy ranlib; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_RANLIB+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_RANLIB"; then
ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_RANLIB="ranlib"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
if test -n "$ac_ct_RANLIB"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5
$as_echo "$ac_ct_RANLIB" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "x$ac_ct_RANLIB" = x; then
RANLIB=":"
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
RANLIB=$ac_ct_RANLIB
fi
else
RANLIB="$ac_cv_prog_RANLIB"
fi
test -z "$RANLIB" && RANLIB=:
# Determine commands to create old-style static archives.
old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs'
old_postinstall_cmds='chmod 644 $oldlib'
old_postuninstall_cmds=
if test -n "$RANLIB"; then
case $host_os in
- openbsd*)
+ bitrig* | openbsd*)
old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib"
;;
*)
old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib"
;;
esac
old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib"
fi
case $host_os in
darwin*)
lock_old_archive_extraction=yes ;;
*)
lock_old_archive_extraction=no ;;
esac
for ac_prog in gawk mawk nawk awk
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_AWK+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$AWK"; then
ac_cv_prog_AWK="$AWK" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_AWK="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
AWK=$ac_cv_prog_AWK
if test -n "$AWK"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
$as_echo "$AWK" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
test -n "$AWK" && break
done
# If no C compiler was specified, use CC.
LTCC=${LTCC-"$CC"}
# If no C compiler flags were specified, use CFLAGS.
LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
# Allow CC to be a program name with arguments.
compiler=$CC
# Check for command to grab the raw symbol name followed by C symbol from nm.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5
$as_echo_n "checking command to parse $NM output from $compiler object... " >&6; }
if ${lt_cv_sys_global_symbol_pipe+:} false; then :
$as_echo_n "(cached) " >&6
else
# These are sane defaults that work on at least a few old systems.
# [They come from Ultrix. What could be older than Ultrix?!! ;)]
# Character class describing NM global symbol codes.
symcode='[BCDEGRST]'
# Regexp to match symbols that can be accessed directly from C.
sympat='\([_A-Za-z][_A-Za-z0-9]*\)'
# Define system-specific variables.
case $host_os in
aix*)
symcode='[BCDT]'
;;
cygwin* | mingw* | pw32* | cegcc*)
symcode='[ABCDGISTW]'
;;
hpux*)
- if test "$host_cpu" = ia64; then
+ if test ia64 = "$host_cpu"; then
symcode='[ABCDEGRST]'
fi
;;
irix* | nonstopux*)
symcode='[BCDEGRST]'
;;
osf*)
symcode='[BCDEGQRST]'
;;
solaris*)
symcode='[BDRT]'
;;
sco3.2v5*)
symcode='[DT]'
;;
sysv4.2uw2*)
symcode='[DT]'
;;
sysv5* | sco5v6* | unixware* | OpenUNIX*)
symcode='[ABDT]'
;;
sysv4)
symcode='[DFNSTU]'
;;
esac
# If we're using GNU nm, then use its standard symbol codes.
case `$NM -V 2>&1` in
*GNU* | *'with BFD'*)
symcode='[ABCDGIRSTW]' ;;
esac
+if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ # Gets list of data symbols to import.
+ lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'"
+ # Adjust the below global symbol transforms to fixup imported variables.
+ lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'"
+ lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'"
+ lt_c_name_lib_hook="\
+ -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\
+ -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'"
+else
+ # Disable hooks by default.
+ lt_cv_sys_global_symbol_to_import=
+ lt_cdecl_hook=
+ lt_c_name_hook=
+ lt_c_name_lib_hook=
+fi
+
# Transform an extracted symbol line into a proper C declaration.
# Some systems (esp. on ia64) link data and code symbols differently,
# so use this general approach.
-lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'"
+lt_cv_sys_global_symbol_to_cdecl="sed -n"\
+$lt_cdecl_hook\
+" -e 's/^T .* \(.*\)$/extern int \1();/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'"
# Transform an extracted symbol line into symbol name and symbol address
-lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\)[ ]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"\2\", (void *) \&\2},/p'"
-lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([^ ]*\)[ ]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \(lib[^ ]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"lib\2\", (void *) \&\2},/p'"
+lt_cv_sys_global_symbol_to_c_name_address="sed -n"\
+$lt_c_name_hook\
+" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'"
+# Transform an extracted symbol line into symbol name with lib prefix and
+# symbol address.
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\
+$lt_c_name_lib_hook\
+" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\
+" -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'"
+
# Handle CRLF in mingw tool chain
opt_cr=
case $build_os in
mingw*)
opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp
;;
esac
# Try without a prefix underscore, then with it.
for ac_symprfx in "" "_"; do
# Transform symcode, sympat, and symprfx into a raw symbol and a C symbol.
symxfrm="\\1 $ac_symprfx\\2 \\2"
# Write the raw and C identifiers.
if test "$lt_cv_nm_interface" = "MS dumpbin"; then
- # Fake it for dumpbin and say T for any non-static function
- # and D for any global variable.
+ # Fake it for dumpbin and say T for any non-static function,
+ # D for any global variable and I for any imported variable.
# Also find C++ and __fastcall symbols from MSVC++,
# which start with @ or ?.
lt_cv_sys_global_symbol_pipe="$AWK '"\
" {last_section=section; section=\$ 3};"\
" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\
" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
+" /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\
+" /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\
+" /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\
" \$ 0!~/External *\|/{next};"\
" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
" {if(hide[section]) next};"\
-" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\
-" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\
-" s[1]~/^[@?]/{print s[1], s[1]; next};"\
-" s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\
+" {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\
+" {split(\$ 0,a,/\||\r/); split(a[2],s)};"\
+" s[1]~/^[@?]/{print f,s[1],s[1]; next};"\
+" s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\
" ' prfx=^$ac_symprfx"
else
lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
fi
lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'"
# Check to see that the pipe works correctly.
pipe_works=no
rm -f conftest*
cat > conftest.$ac_ext <<_LT_EOF
#ifdef __cplusplus
extern "C" {
#endif
char nm_test_var;
void nm_test_func(void);
void nm_test_func(void){}
#ifdef __cplusplus
}
#endif
int main(){nm_test_var='a';nm_test_func();return(0);}
_LT_EOF
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
# Now try to grab the symbols.
nlist=conftest.nm
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist\""; } >&5
(eval $NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } && test -s "$nlist"; then
# Try sorting and uniquifying the output.
if sort "$nlist" | uniq > "$nlist"T; then
mv -f "$nlist"T "$nlist"
else
rm -f "$nlist"T
fi
# Make sure that we snagged all the symbols we need.
if $GREP ' nm_test_var$' "$nlist" >/dev/null; then
if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
cat <<_LT_EOF > conftest.$ac_ext
/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */
-#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE)
-/* DATA imports from DLLs on WIN32 con't be const, because runtime
+#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE
+/* DATA imports from DLLs on WIN32 can't be const, because runtime
relocations are performed -- see ld's documentation on pseudo-relocs. */
# define LT_DLSYM_CONST
-#elif defined(__osf__)
+#elif defined __osf__
/* This system does not cope well with relocations in const data. */
# define LT_DLSYM_CONST
#else
# define LT_DLSYM_CONST const
#endif
#ifdef __cplusplus
extern "C" {
#endif
_LT_EOF
# Now generate the symbol file.
eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext'
cat <<_LT_EOF >> conftest.$ac_ext
/* The mapping between symbol names and symbols. */
LT_DLSYM_CONST struct {
const char *name;
void *address;
}
lt__PROGRAM__LTX_preloaded_symbols[] =
{
{ "@PROGRAM@", (void *) 0 },
_LT_EOF
- $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
+ $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
cat <<\_LT_EOF >> conftest.$ac_ext
{0, (void *) 0}
};
/* This works around a problem in FreeBSD linker */
#ifdef FREEBSD_WORKAROUND
static const void *lt_preloaded_setup() {
return lt__PROGRAM__LTX_preloaded_symbols;
}
#endif
#ifdef __cplusplus
}
#endif
_LT_EOF
# Now try linking the two files.
mv conftest.$ac_objext conftstm.$ac_objext
lt_globsym_save_LIBS=$LIBS
lt_globsym_save_CFLAGS=$CFLAGS
- LIBS="conftstm.$ac_objext"
+ LIBS=conftstm.$ac_objext
CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag"
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
(eval $ac_link) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; } && test -s conftest${ac_exeext}; then
+ test $ac_status = 0; } && test -s conftest$ac_exeext; then
pipe_works=yes
fi
LIBS=$lt_globsym_save_LIBS
CFLAGS=$lt_globsym_save_CFLAGS
else
echo "cannot find nm_test_func in $nlist" >&5
fi
else
echo "cannot find nm_test_var in $nlist" >&5
fi
else
echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5
fi
else
echo "$progname: failed program was:" >&5
cat conftest.$ac_ext >&5
fi
rm -rf conftest* conftst*
# Do not use the global_symbol_pipe unless it works.
- if test "$pipe_works" = yes; then
+ if test yes = "$pipe_works"; then
break
else
lt_cv_sys_global_symbol_pipe=
fi
done
fi
if test -z "$lt_cv_sys_global_symbol_pipe"; then
lt_cv_sys_global_symbol_to_cdecl=
fi
if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5
$as_echo "failed" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5
$as_echo "ok" >&6; }
fi
# Response file support.
if test "$lt_cv_nm_interface" = "MS dumpbin"; then
nm_file_list_spec='@'
elif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then
nm_file_list_spec='@'
fi
+
+
+
+
+
+
+
+
+
+
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5
$as_echo_n "checking for sysroot... " >&6; }
# Check whether --with-sysroot was given.
if test "${with_sysroot+set}" = set; then :
withval=$with_sysroot;
else
with_sysroot=no
fi
lt_sysroot=
-case ${with_sysroot} in #(
+case $with_sysroot in #(
yes)
- if test "$GCC" = yes; then
+ if test yes = "$GCC"; then
lt_sysroot=`$CC --print-sysroot 2>/dev/null`
fi
;; #(
/*)
lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"`
;; #(
no|'')
;; #(
*)
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${with_sysroot}" >&5
-$as_echo "${with_sysroot}" >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_sysroot" >&5
+$as_echo "$with_sysroot" >&6; }
as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5
;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}" >&5
$as_echo "${lt_sysroot:-no}" >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a working dd" >&5
+$as_echo_n "checking for a working dd... " >&6; }
+if ${ac_cv_path_lt_DD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ printf 0123456789abcdef0123456789abcdef >conftest.i
+cat conftest.i conftest.i >conftest2.i
+: ${lt_DD:=$DD}
+if test -z "$lt_DD"; then
+ ac_path_lt_DD_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in dd; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_lt_DD="$as_dir/$ac_prog$ac_exec_ext"
+ as_fn_executable_p "$ac_path_lt_DD" || continue
+if "$ac_path_lt_DD" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; then
+ cmp -s conftest.i conftest.out \
+ && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=:
+fi
+ $ac_path_lt_DD_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_lt_DD"; then
+ :
+ fi
+else
+ ac_cv_path_lt_DD=$lt_DD
+fi
+
+rm -f conftest.i conftest2.i conftest.out
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_lt_DD" >&5
+$as_echo "$ac_cv_path_lt_DD" >&6; }
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to truncate binary pipes" >&5
+$as_echo_n "checking how to truncate binary pipes... " >&6; }
+if ${lt_cv_truncate_bin+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ printf 0123456789abcdef0123456789abcdef >conftest.i
+cat conftest.i conftest.i >conftest2.i
+lt_cv_truncate_bin=
+if "$ac_cv_path_lt_DD" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; then
+ cmp -s conftest.i conftest.out \
+ && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1"
+fi
+rm -f conftest.i conftest2.i conftest.out
+test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q"
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_truncate_bin" >&5
+$as_echo "$lt_cv_truncate_bin" >&6; }
+
+
+
+
+
+
# Check whether --enable-libtool-lock was given.
if test "${enable_libtool_lock+set}" = set; then :
enableval=$enable_libtool_lock;
fi
-test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
+test no = "$enable_libtool_lock" || enable_libtool_lock=yes
# Some flags need to be propagated to the compiler or linker for good
# libtool support.
case $host in
ia64-*-hpux*)
- # Find out which ABI we are using.
+ # Find out what ABI is being produced by ac_compile, and set mode
+ # options accordingly.
echo 'int i;' > conftest.$ac_ext
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
case `/usr/bin/file conftest.$ac_objext` in
*ELF-32*)
- HPUX_IA64_MODE="32"
+ HPUX_IA64_MODE=32
;;
*ELF-64*)
- HPUX_IA64_MODE="64"
+ HPUX_IA64_MODE=64
;;
esac
fi
rm -rf conftest*
;;
*-*-irix6*)
- # Find out which ABI we are using.
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly.
echo '#line '$LINENO' "configure"' > conftest.$ac_ext
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
- if test "$lt_cv_prog_gnu_ld" = yes; then
+ if test yes = "$lt_cv_prog_gnu_ld"; then
case `/usr/bin/file conftest.$ac_objext` in
*32-bit*)
LD="${LD-ld} -melf32bsmip"
;;
*N32*)
LD="${LD-ld} -melf32bmipn32"
;;
*64-bit*)
LD="${LD-ld} -melf64bmip"
;;
esac
else
case `/usr/bin/file conftest.$ac_objext` in
*32-bit*)
LD="${LD-ld} -32"
;;
*N32*)
LD="${LD-ld} -n32"
;;
*64-bit*)
LD="${LD-ld} -64"
;;
esac
fi
fi
rm -rf conftest*
;;
-x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \
+mips64*-*linux*)
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly.
+ echo '#line '$LINENO' "configure"' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ emul=elf
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ emul="${emul}32"
+ ;;
+ *64-bit*)
+ emul="${emul}64"
+ ;;
+ esac
+ case `/usr/bin/file conftest.$ac_objext` in
+ *MSB*)
+ emul="${emul}btsmip"
+ ;;
+ *LSB*)
+ emul="${emul}ltsmip"
+ ;;
+ esac
+ case `/usr/bin/file conftest.$ac_objext` in
+ *N32*)
+ emul="${emul}n32"
+ ;;
+ esac
+ LD="${LD-ld} -m $emul"
+ fi
+ rm -rf conftest*
+ ;;
+
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \
s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
- # Find out which ABI we are using.
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly. Note that the listed cases only cover the
+ # situations where additional linker options are needed (such as when
+ # doing 32-bit compilation for a host where ld defaults to 64-bit, or
+ # vice versa); the common cases where no linker options are needed do
+ # not appear in the list.
echo 'int i;' > conftest.$ac_ext
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
case `/usr/bin/file conftest.o` in
*32-bit*)
case $host in
x86_64-*kfreebsd*-gnu)
LD="${LD-ld} -m elf_i386_fbsd"
;;
x86_64-*linux*)
- LD="${LD-ld} -m elf_i386"
+ case `/usr/bin/file conftest.o` in
+ *x86-64*)
+ LD="${LD-ld} -m elf32_x86_64"
+ ;;
+ *)
+ LD="${LD-ld} -m elf_i386"
+ ;;
+ esac
;;
- ppc64-*linux*|powerpc64-*linux*)
+ powerpc64le-*linux*)
+ LD="${LD-ld} -m elf32lppclinux"
+ ;;
+ powerpc64-*linux*)
LD="${LD-ld} -m elf32ppclinux"
;;
s390x-*linux*)
LD="${LD-ld} -m elf_s390"
;;
sparc64-*linux*)
LD="${LD-ld} -m elf32_sparc"
;;
esac
;;
*64-bit*)
case $host in
x86_64-*kfreebsd*-gnu)
LD="${LD-ld} -m elf_x86_64_fbsd"
;;
x86_64-*linux*)
LD="${LD-ld} -m elf_x86_64"
;;
- ppc*-*linux*|powerpc*-*linux*)
+ powerpcle-*linux*)
+ LD="${LD-ld} -m elf64lppc"
+ ;;
+ powerpc-*linux*)
LD="${LD-ld} -m elf64ppc"
;;
s390*-*linux*|s390*-*tpf*)
LD="${LD-ld} -m elf64_s390"
;;
sparc*-*linux*)
LD="${LD-ld} -m elf64_sparc"
;;
esac
;;
esac
fi
rm -rf conftest*
;;
*-*-sco3.2v5*)
# On SCO OpenServer 5, we need -belf to get full-featured binaries.
- SAVE_CFLAGS="$CFLAGS"
+ SAVE_CFLAGS=$CFLAGS
CFLAGS="$CFLAGS -belf"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5
$as_echo_n "checking whether the C compiler needs -belf... " >&6; }
if ${lt_cv_cc_needs_belf+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
lt_cv_cc_needs_belf=yes
else
lt_cv_cc_needs_belf=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5
$as_echo "$lt_cv_cc_needs_belf" >&6; }
- if test x"$lt_cv_cc_needs_belf" != x"yes"; then
+ if test yes != "$lt_cv_cc_needs_belf"; then
# this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
- CFLAGS="$SAVE_CFLAGS"
+ CFLAGS=$SAVE_CFLAGS
fi
;;
*-*solaris*)
- # Find out which ABI we are using.
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly.
echo 'int i;' > conftest.$ac_ext
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
case `/usr/bin/file conftest.o` in
*64-bit*)
case $lt_cv_prog_gnu_ld in
yes*)
case $host in
- i?86-*-solaris*)
+ i?86-*-solaris*|x86_64-*-solaris*)
LD="${LD-ld} -m elf_x86_64"
;;
sparc*-*-solaris*)
LD="${LD-ld} -m elf64_sparc"
;;
esac
# GNU ld 2.21 introduced _sol2 emulations. Use them if available.
if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then
- LD="${LD-ld}_sol2"
+ LD=${LD-ld}_sol2
fi
;;
*)
if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
LD="${LD-ld} -64"
fi
;;
esac
;;
esac
fi
rm -rf conftest*
;;
esac
-need_locks="$enable_libtool_lock"
+need_locks=$enable_libtool_lock
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args.
set dummy ${ac_tool_prefix}mt; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_MANIFEST_TOOL+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$MANIFEST_TOOL"; then
ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL
if test -n "$MANIFEST_TOOL"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL" >&5
$as_echo "$MANIFEST_TOOL" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$ac_cv_prog_MANIFEST_TOOL"; then
ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL
# Extract the first word of "mt", so it can be a program name with args.
set dummy mt; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_MANIFEST_TOOL+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_MANIFEST_TOOL"; then
ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_MANIFEST_TOOL="mt"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL
if test -n "$ac_ct_MANIFEST_TOOL"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL" >&5
$as_echo "$ac_ct_MANIFEST_TOOL" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "x$ac_ct_MANIFEST_TOOL" = x; then
MANIFEST_TOOL=":"
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL
fi
else
MANIFEST_TOOL="$ac_cv_prog_MANIFEST_TOOL"
fi
test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5
$as_echo_n "checking if $MANIFEST_TOOL is a manifest tool... " >&6; }
if ${lt_cv_path_mainfest_tool+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_path_mainfest_tool=no
echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5
$MANIFEST_TOOL '-?' 2>conftest.err > conftest.out
cat conftest.err >&5
if $GREP 'Manifest Tool' conftest.out > /dev/null; then
lt_cv_path_mainfest_tool=yes
fi
rm -f conftest*
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5
$as_echo "$lt_cv_path_mainfest_tool" >&6; }
-if test "x$lt_cv_path_mainfest_tool" != xyes; then
+if test yes != "$lt_cv_path_mainfest_tool"; then
MANIFEST_TOOL=:
fi
case $host_os in
rhapsody* | darwin*)
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args.
set dummy ${ac_tool_prefix}dsymutil; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_DSYMUTIL+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$DSYMUTIL"; then
ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
DSYMUTIL=$ac_cv_prog_DSYMUTIL
if test -n "$DSYMUTIL"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5
$as_echo "$DSYMUTIL" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$ac_cv_prog_DSYMUTIL"; then
ac_ct_DSYMUTIL=$DSYMUTIL
# Extract the first word of "dsymutil", so it can be a program name with args.
set dummy dsymutil; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_DSYMUTIL"; then
ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_DSYMUTIL="dsymutil"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL
if test -n "$ac_ct_DSYMUTIL"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5
$as_echo "$ac_ct_DSYMUTIL" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "x$ac_ct_DSYMUTIL" = x; then
DSYMUTIL=":"
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
DSYMUTIL=$ac_ct_DSYMUTIL
fi
else
DSYMUTIL="$ac_cv_prog_DSYMUTIL"
fi
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args.
set dummy ${ac_tool_prefix}nmedit; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_NMEDIT+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$NMEDIT"; then
ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
NMEDIT=$ac_cv_prog_NMEDIT
if test -n "$NMEDIT"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5
$as_echo "$NMEDIT" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$ac_cv_prog_NMEDIT"; then
ac_ct_NMEDIT=$NMEDIT
# Extract the first word of "nmedit", so it can be a program name with args.
set dummy nmedit; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_NMEDIT"; then
ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_NMEDIT="nmedit"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT
if test -n "$ac_ct_NMEDIT"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5
$as_echo "$ac_ct_NMEDIT" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "x$ac_ct_NMEDIT" = x; then
NMEDIT=":"
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
NMEDIT=$ac_ct_NMEDIT
fi
else
NMEDIT="$ac_cv_prog_NMEDIT"
fi
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args.
set dummy ${ac_tool_prefix}lipo; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_LIPO+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$LIPO"; then
ac_cv_prog_LIPO="$LIPO" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_LIPO="${ac_tool_prefix}lipo"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
LIPO=$ac_cv_prog_LIPO
if test -n "$LIPO"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5
$as_echo "$LIPO" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$ac_cv_prog_LIPO"; then
ac_ct_LIPO=$LIPO
# Extract the first word of "lipo", so it can be a program name with args.
set dummy lipo; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_LIPO+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_LIPO"; then
ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_LIPO="lipo"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO
if test -n "$ac_ct_LIPO"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5
$as_echo "$ac_ct_LIPO" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "x$ac_ct_LIPO" = x; then
LIPO=":"
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
LIPO=$ac_ct_LIPO
fi
else
LIPO="$ac_cv_prog_LIPO"
fi
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args.
set dummy ${ac_tool_prefix}otool; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_OTOOL+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$OTOOL"; then
ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_OTOOL="${ac_tool_prefix}otool"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
OTOOL=$ac_cv_prog_OTOOL
if test -n "$OTOOL"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5
$as_echo "$OTOOL" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$ac_cv_prog_OTOOL"; then
ac_ct_OTOOL=$OTOOL
# Extract the first word of "otool", so it can be a program name with args.
set dummy otool; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_OTOOL+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_OTOOL"; then
ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_OTOOL="otool"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL
if test -n "$ac_ct_OTOOL"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5
$as_echo "$ac_ct_OTOOL" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "x$ac_ct_OTOOL" = x; then
OTOOL=":"
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
OTOOL=$ac_ct_OTOOL
fi
else
OTOOL="$ac_cv_prog_OTOOL"
fi
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args.
set dummy ${ac_tool_prefix}otool64; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_OTOOL64+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$OTOOL64"; then
ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
OTOOL64=$ac_cv_prog_OTOOL64
if test -n "$OTOOL64"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5
$as_echo "$OTOOL64" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$ac_cv_prog_OTOOL64"; then
ac_ct_OTOOL64=$OTOOL64
# Extract the first word of "otool64", so it can be a program name with args.
set dummy otool64; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_OTOOL64"; then
ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_OTOOL64="otool64"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64
if test -n "$ac_ct_OTOOL64"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5
$as_echo "$ac_ct_OTOOL64" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "x$ac_ct_OTOOL64" = x; then
OTOOL64=":"
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
OTOOL64=$ac_ct_OTOOL64
fi
else
OTOOL64="$ac_cv_prog_OTOOL64"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5
$as_echo_n "checking for -single_module linker flag... " >&6; }
if ${lt_cv_apple_cc_single_mod+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_apple_cc_single_mod=no
- if test -z "${LT_MULTI_MODULE}"; then
+ if test -z "$LT_MULTI_MODULE"; then
# By default we will add the -single_module flag. You can override
# by either setting the environment variable LT_MULTI_MODULE
# non-empty at configure time, or by adding -multi_module to the
# link flags.
rm -rf libconftest.dylib*
echo "int foo(void){return 1;}" > conftest.c
echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
-dynamiclib -Wl,-single_module conftest.c" >&5
$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
-dynamiclib -Wl,-single_module conftest.c 2>conftest.err
_lt_result=$?
# If there is a non-empty error log, and "single_module"
# appears in it, assume the flag caused a linker warning
if test -s conftest.err && $GREP single_module conftest.err; then
cat conftest.err >&5
# Otherwise, if the output was created with a 0 exit code from
# the compiler, it worked.
- elif test -f libconftest.dylib && test $_lt_result -eq 0; then
+ elif test -f libconftest.dylib && test 0 = "$_lt_result"; then
lt_cv_apple_cc_single_mod=yes
else
cat conftest.err >&5
fi
rm -rf libconftest.dylib*
rm -f conftest.*
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5
$as_echo "$lt_cv_apple_cc_single_mod" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5
$as_echo_n "checking for -exported_symbols_list linker flag... " >&6; }
if ${lt_cv_ld_exported_symbols_list+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_ld_exported_symbols_list=no
save_LDFLAGS=$LDFLAGS
echo "_main" > conftest.sym
LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
lt_cv_ld_exported_symbols_list=yes
else
lt_cv_ld_exported_symbols_list=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
- LDFLAGS="$save_LDFLAGS"
+ LDFLAGS=$save_LDFLAGS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5
$as_echo "$lt_cv_ld_exported_symbols_list" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5
$as_echo_n "checking for -force_load linker flag... " >&6; }
if ${lt_cv_ld_force_load+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_ld_force_load=no
cat > conftest.c << _LT_EOF
int forced_loaded() { return 2;}
_LT_EOF
echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5
$LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5
echo "$AR cru libconftest.a conftest.o" >&5
$AR cru libconftest.a conftest.o 2>&5
echo "$RANLIB libconftest.a" >&5
$RANLIB libconftest.a 2>&5
cat > conftest.c << _LT_EOF
int main() { return 0;}
_LT_EOF
echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5
$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err
_lt_result=$?
if test -s conftest.err && $GREP force_load conftest.err; then
cat conftest.err >&5
- elif test -f conftest && test $_lt_result -eq 0 && $GREP forced_load conftest >/dev/null 2>&1 ; then
+ elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then
lt_cv_ld_force_load=yes
else
cat conftest.err >&5
fi
rm -f conftest.err libconftest.a conftest conftest.c
rm -rf conftest.dSYM
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5
$as_echo "$lt_cv_ld_force_load" >&6; }
case $host_os in
rhapsody* | darwin1.[012])
- _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;;
+ _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;;
darwin1.*)
- _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+ _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
darwin*) # darwin 5.x on
# if running on 10.5 or later, the deployment target defaults
# to the OS version, if on x86, and 10.4, the deployment
# target defaults to 10.4. Don't you love it?
case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
10.0,*86*-darwin8*|10.0,*-darwin[91]*)
- _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
- 10.[012]*)
- _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+ _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
+ 10.[012][,.]*)
+ _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
10.*)
- _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+ _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
esac
;;
esac
- if test "$lt_cv_apple_cc_single_mod" = "yes"; then
+ if test yes = "$lt_cv_apple_cc_single_mod"; then
_lt_dar_single_mod='$single_module'
fi
- if test "$lt_cv_ld_exported_symbols_list" = "yes"; then
- _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym'
+ if test yes = "$lt_cv_ld_exported_symbols_list"; then
+ _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym'
else
- _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}'
+ _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib'
fi
- if test "$DSYMUTIL" != ":" && test "$lt_cv_ld_force_load" = "no"; then
+ if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then
_lt_dsymutil='~$DSYMUTIL $lib || :'
else
_lt_dsymutil=
fi
;;
esac
for ac_header in dlfcn.h
do :
ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default
"
if test "x$ac_cv_header_dlfcn_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_DLFCN_H 1
_ACEOF
fi
done
func_stripname_cnf ()
{
- case ${2} in
- .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;;
- *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;;
+ case $2 in
+ .*) func_stripname_result=`$ECHO "$3" | $SED "s%^$1%%; s%\\\\$2\$%%"`;;
+ *) func_stripname_result=`$ECHO "$3" | $SED "s%^$1%%; s%$2\$%%"`;;
esac
} # func_stripname_cnf
# Set options
enable_dlopen=no
enable_win32_dll=no
# Check whether --enable-shared was given.
if test "${enable_shared+set}" = set; then :
enableval=$enable_shared; p=${PACKAGE-default}
case $enableval in
yes) enable_shared=yes ;;
no) enable_shared=no ;;
*)
enable_shared=no
# Look at the argument we got. We use all the common list separators.
- lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for pkg in $enableval; do
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
if test "X$pkg" = "X$p"; then
enable_shared=yes
fi
done
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
;;
esac
else
enable_shared=yes
fi
# Check whether --enable-static was given.
if test "${enable_static+set}" = set; then :
enableval=$enable_static; p=${PACKAGE-default}
case $enableval in
yes) enable_static=yes ;;
no) enable_static=no ;;
*)
enable_static=no
# Look at the argument we got. We use all the common list separators.
- lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for pkg in $enableval; do
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
if test "X$pkg" = "X$p"; then
enable_static=yes
fi
done
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
;;
esac
else
enable_static=yes
fi
# Check whether --with-pic was given.
if test "${with_pic+set}" = set; then :
withval=$with_pic; lt_p=${PACKAGE-default}
case $withval in
yes|no) pic_mode=$withval ;;
*)
pic_mode=default
# Look at the argument we got. We use all the common list separators.
- lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for lt_pkg in $withval; do
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
if test "X$lt_pkg" = "X$lt_p"; then
pic_mode=yes
fi
done
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
;;
esac
else
pic_mode=default
fi
-test -z "$pic_mode" && pic_mode=default
-
# Check whether --enable-fast-install was given.
if test "${enable_fast_install+set}" = set; then :
enableval=$enable_fast_install; p=${PACKAGE-default}
case $enableval in
yes) enable_fast_install=yes ;;
no) enable_fast_install=no ;;
*)
enable_fast_install=no
# Look at the argument we got. We use all the common list separators.
- lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for pkg in $enableval; do
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
if test "X$pkg" = "X$p"; then
enable_fast_install=yes
fi
done
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
;;
esac
else
enable_fast_install=yes
fi
# This can be used to rebuild libtool when needed
-LIBTOOL_DEPS="$ltmain"
+LIBTOOL_DEPS=$ltmain
# Always use our own libtool.
LIBTOOL='$(SHELL) $(top_builddir)/libtool'
test -z "$LN_S" && LN_S="ln -s"
-if test -n "${ZSH_VERSION+set}" ; then
+if test -n "${ZSH_VERSION+set}"; then
setopt NO_GLOB_SUBST
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5
$as_echo_n "checking for objdir... " >&6; }
if ${lt_cv_objdir+:} false; then :
$as_echo_n "(cached) " >&6
else
rm -f .libs 2>/dev/null
mkdir .libs 2>/dev/null
if test -d .libs; then
lt_cv_objdir=.libs
else
# MS-DOS does not allow filenames that begin with a dot.
lt_cv_objdir=_libs
fi
rmdir .libs 2>/dev/null
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5
$as_echo "$lt_cv_objdir" >&6; }
objdir=$lt_cv_objdir
cat >>confdefs.h <<_ACEOF
#define LT_OBJDIR "$lt_cv_objdir/"
_ACEOF
case $host_os in
aix3*)
# AIX sometimes has problems with the GCC collect2 program. For some
# reason, if we set the COLLECT_NAMES environment variable, the problems
# vanish in a puff of smoke.
- if test "X${COLLECT_NAMES+set}" != Xset; then
+ if test set != "${COLLECT_NAMES+set}"; then
COLLECT_NAMES=
export COLLECT_NAMES
fi
;;
esac
# Global variables:
ofile=libtool
can_build_shared=yes
-# All known linkers require a `.a' archive for static linking (except MSVC,
+# All known linkers require a '.a' archive for static linking (except MSVC,
# which needs '.lib').
libext=a
-with_gnu_ld="$lt_cv_prog_gnu_ld"
+with_gnu_ld=$lt_cv_prog_gnu_ld
-old_CC="$CC"
-old_CFLAGS="$CFLAGS"
+old_CC=$CC
+old_CFLAGS=$CFLAGS
# Set sane defaults for various variables
test -z "$CC" && CC=cc
test -z "$LTCC" && LTCC=$CC
test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS
test -z "$LD" && LD=ld
test -z "$ac_objext" && ac_objext=o
for cc_temp in $compiler""; do
case $cc_temp in
compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
\-*) ;;
*) break;;
esac
done
cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"`
# Only perform the check for file, if the check method requires it
test -z "$MAGIC_CMD" && MAGIC_CMD=file
case $deplibs_check_method in
file_magic*)
if test "$file_magic_cmd" = '$MAGIC_CMD'; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5
$as_echo_n "checking for ${ac_tool_prefix}file... " >&6; }
if ${lt_cv_path_MAGIC_CMD+:} false; then :
$as_echo_n "(cached) " >&6
else
case $MAGIC_CMD in
[\\/*] | ?:[\\/]*)
- lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+ lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path.
;;
*)
- lt_save_MAGIC_CMD="$MAGIC_CMD"
- lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ lt_save_MAGIC_CMD=$MAGIC_CMD
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
for ac_dir in $ac_dummy; do
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
test -z "$ac_dir" && ac_dir=.
- if test -f $ac_dir/${ac_tool_prefix}file; then
- lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file"
+ if test -f "$ac_dir/${ac_tool_prefix}file"; then
+ lt_cv_path_MAGIC_CMD=$ac_dir/"${ac_tool_prefix}file"
if test -n "$file_magic_test_file"; then
case $deplibs_check_method in
"file_magic "*)
file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
- MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+ MAGIC_CMD=$lt_cv_path_MAGIC_CMD
if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
$EGREP "$file_magic_regex" > /dev/null; then
:
else
cat <<_LT_EOF 1>&2
*** Warning: the command libtool uses to detect shared libraries,
*** $file_magic_cmd, produces output that libtool cannot recognize.
*** The result is that libtool may fail to recognize shared libraries
*** as such. This will affect the creation of libtool libraries that
*** depend on shared libraries, but programs linked with such libtool
*** libraries will work regardless of this problem. Nevertheless, you
*** may want to report the problem to your system manager and/or to
*** bug-libtool@gnu.org
_LT_EOF
fi ;;
esac
fi
break
fi
done
- IFS="$lt_save_ifs"
- MAGIC_CMD="$lt_save_MAGIC_CMD"
+ IFS=$lt_save_ifs
+ MAGIC_CMD=$lt_save_MAGIC_CMD
;;
esac
fi
-MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+MAGIC_CMD=$lt_cv_path_MAGIC_CMD
if test -n "$MAGIC_CMD"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5
$as_echo "$MAGIC_CMD" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test -z "$lt_cv_path_MAGIC_CMD"; then
if test -n "$ac_tool_prefix"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5
$as_echo_n "checking for file... " >&6; }
if ${lt_cv_path_MAGIC_CMD+:} false; then :
$as_echo_n "(cached) " >&6
else
case $MAGIC_CMD in
[\\/*] | ?:[\\/]*)
- lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+ lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path.
;;
*)
- lt_save_MAGIC_CMD="$MAGIC_CMD"
- lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ lt_save_MAGIC_CMD=$MAGIC_CMD
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
for ac_dir in $ac_dummy; do
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
test -z "$ac_dir" && ac_dir=.
- if test -f $ac_dir/file; then
- lt_cv_path_MAGIC_CMD="$ac_dir/file"
+ if test -f "$ac_dir/file"; then
+ lt_cv_path_MAGIC_CMD=$ac_dir/"file"
if test -n "$file_magic_test_file"; then
case $deplibs_check_method in
"file_magic "*)
file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
- MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+ MAGIC_CMD=$lt_cv_path_MAGIC_CMD
if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
$EGREP "$file_magic_regex" > /dev/null; then
:
else
cat <<_LT_EOF 1>&2
*** Warning: the command libtool uses to detect shared libraries,
*** $file_magic_cmd, produces output that libtool cannot recognize.
*** The result is that libtool may fail to recognize shared libraries
*** as such. This will affect the creation of libtool libraries that
*** depend on shared libraries, but programs linked with such libtool
*** libraries will work regardless of this problem. Nevertheless, you
*** may want to report the problem to your system manager and/or to
*** bug-libtool@gnu.org
_LT_EOF
fi ;;
esac
fi
break
fi
done
- IFS="$lt_save_ifs"
- MAGIC_CMD="$lt_save_MAGIC_CMD"
+ IFS=$lt_save_ifs
+ MAGIC_CMD=$lt_save_MAGIC_CMD
;;
esac
fi
-MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+MAGIC_CMD=$lt_cv_path_MAGIC_CMD
if test -n "$MAGIC_CMD"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5
$as_echo "$MAGIC_CMD" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
else
MAGIC_CMD=:
fi
fi
fi
;;
esac
# Use C for the default configuration in the libtool script
-lt_save_CC="$CC"
+lt_save_CC=$CC
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
# Source file extension for C test sources.
ac_ext=c
# Object file extension for compiled C test sources.
objext=o
objext=$objext
# Code to be used in simple compile tests
lt_simple_compile_test_code="int some_variable = 0;"
# Code to be used in simple link tests
lt_simple_link_test_code='int main(){return(0);}'
# If no C compiler was specified, use CC.
LTCC=${LTCC-"$CC"}
# If no C compiler flags were specified, use CFLAGS.
LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
# Allow CC to be a program name with arguments.
compiler=$CC
# Save the default compiler, since it gets overwritten when the other
# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP.
compiler_DEFAULT=$CC
# save warnings/boilerplate of simple test code
ac_outfile=conftest.$ac_objext
echo "$lt_simple_compile_test_code" >conftest.$ac_ext
eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
_lt_compiler_boilerplate=`cat conftest.err`
$RM conftest*
ac_outfile=conftest.$ac_objext
echo "$lt_simple_link_test_code" >conftest.$ac_ext
eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
_lt_linker_boilerplate=`cat conftest.err`
$RM -r conftest*
## CAVEAT EMPTOR:
## There is no encapsulation within the following macros, do not change
## the running order or otherwise move them around unless you know exactly
## what you are doing...
if test -n "$compiler"; then
lt_prog_compiler_no_builtin_flag=
-if test "$GCC" = yes; then
+if test yes = "$GCC"; then
case $cc_basename in
nvcc*)
lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;;
*)
lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5
$as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; }
if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_prog_compiler_rtti_exceptions=no
ac_outfile=conftest.$ac_objext
echo "$lt_simple_compile_test_code" > conftest.$ac_ext
- lt_compiler_flag="-fno-rtti -fno-exceptions"
+ lt_compiler_flag="-fno-rtti -fno-exceptions" ## exclude from sc_useless_quotes_in_assignment
# Insert the option either (1) after the last *FLAGS variable, or
# (2) before a word containing "conftest.", or (3) at the end.
# Note that $ac_compile itself does not contain backslashes and begins
# with a dollar sign (not a hyphen), so the echo should work correctly.
# The option is referenced via a variable to avoid confusing sed.
lt_compile=`echo "$ac_compile" | $SED \
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
$ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp
$SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
lt_cv_prog_compiler_rtti_exceptions=yes
fi
fi
$RM conftest*
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5
$as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; }
-if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then
+if test yes = "$lt_cv_prog_compiler_rtti_exceptions"; then
lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions"
else
:
fi
fi
lt_prog_compiler_wl=
lt_prog_compiler_pic=
lt_prog_compiler_static=
- if test "$GCC" = yes; then
+ if test yes = "$GCC"; then
lt_prog_compiler_wl='-Wl,'
lt_prog_compiler_static='-static'
case $host_os in
aix*)
# All AIX code is PIC.
- if test "$host_cpu" = ia64; then
+ if test ia64 = "$host_cpu"; then
# AIX 5 now supports IA64 processor
lt_prog_compiler_static='-Bstatic'
fi
+ lt_prog_compiler_pic='-fPIC'
;;
amigaos*)
case $host_cpu in
powerpc)
# see comment about AmigaOS4 .so support
lt_prog_compiler_pic='-fPIC'
;;
m68k)
# FIXME: we need at least 68020 code to build shared libraries, but
- # adding the `-m68020' flag to GCC prevents building anything better,
- # like `-m68040'.
+ # adding the '-m68020' flag to GCC prevents building anything better,
+ # like '-m68040'.
lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4'
;;
esac
;;
beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
# PIC is the default for these OSes.
;;
mingw* | cygwin* | pw32* | os2* | cegcc*)
# This hack is so that the source file can tell whether it is being
# built for inclusion in a dll (and should export symbols for example).
# Although the cygwin gcc ignores -fPIC, still need this for old-style
# (--disable-auto-import) libraries
lt_prog_compiler_pic='-DDLL_EXPORT'
;;
darwin* | rhapsody*)
# PIC is the default on this platform
# Common symbols not allowed in MH_DYLIB files
lt_prog_compiler_pic='-fno-common'
;;
haiku*)
# PIC is the default for Haiku.
# The "-static" flag exists, but is broken.
lt_prog_compiler_static=
;;
hpux*)
# PIC is the default for 64-bit PA HP-UX, but not for 32-bit
# PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag
# sets the default TLS model and affects inlining.
case $host_cpu in
hppa*64*)
# +Z the default
;;
*)
lt_prog_compiler_pic='-fPIC'
;;
esac
;;
interix[3-9]*)
# Interix 3.x gcc -fpic/-fPIC options generate broken code.
# Instead, we relocate shared libraries at runtime.
;;
msdosdjgpp*)
# Just because we use GCC doesn't mean we suddenly get shared libraries
# on systems that don't support them.
lt_prog_compiler_can_build_shared=no
enable_shared=no
;;
*nto* | *qnx*)
# QNX uses GNU C++, but need to define -shared option too, otherwise
# it will coredump.
lt_prog_compiler_pic='-fPIC -shared'
;;
sysv4*MP*)
if test -d /usr/nec; then
lt_prog_compiler_pic=-Kconform_pic
fi
;;
*)
lt_prog_compiler_pic='-fPIC'
;;
esac
case $cc_basename in
nvcc*) # Cuda Compiler Driver 2.2
lt_prog_compiler_wl='-Xlinker '
if test -n "$lt_prog_compiler_pic"; then
lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic"
fi
;;
esac
else
# PORTME Check for flag to pass linker flags through the system compiler.
case $host_os in
aix*)
lt_prog_compiler_wl='-Wl,'
- if test "$host_cpu" = ia64; then
+ if test ia64 = "$host_cpu"; then
# AIX 5 now supports IA64 processor
lt_prog_compiler_static='-Bstatic'
else
lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp'
fi
;;
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ lt_prog_compiler_pic='-fno-common'
+ case $cc_basename in
+ nagfor*)
+ # NAG Fortran compiler
+ lt_prog_compiler_wl='-Wl,-Wl,,'
+ lt_prog_compiler_pic='-PIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+ esac
+ ;;
+
mingw* | cygwin* | pw32* | os2* | cegcc*)
# This hack is so that the source file can tell whether it is being
# built for inclusion in a dll (and should export symbols for example).
lt_prog_compiler_pic='-DDLL_EXPORT'
;;
hpux9* | hpux10* | hpux11*)
lt_prog_compiler_wl='-Wl,'
# PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
# not for PA HP-UX.
case $host_cpu in
hppa*64*|ia64*)
# +Z the default
;;
*)
lt_prog_compiler_pic='+Z'
;;
esac
# Is there a better lt_prog_compiler_static that works with the bundled CC?
- lt_prog_compiler_static='${wl}-a ${wl}archive'
+ lt_prog_compiler_static='$wl-a ${wl}archive'
;;
irix5* | irix6* | nonstopux*)
lt_prog_compiler_wl='-Wl,'
# PIC (with -KPIC) is the default.
lt_prog_compiler_static='-non_shared'
;;
- linux* | k*bsd*-gnu | kopensolaris*-gnu)
+ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
case $cc_basename in
- # old Intel for x86_64 which still supported -KPIC.
+ # old Intel for x86_64, which still supported -KPIC.
ecc*)
lt_prog_compiler_wl='-Wl,'
lt_prog_compiler_pic='-KPIC'
lt_prog_compiler_static='-static'
;;
# icc used to be incompatible with GCC.
# ICC 10 doesn't accept -KPIC any more.
icc* | ifort*)
lt_prog_compiler_wl='-Wl,'
lt_prog_compiler_pic='-fPIC'
lt_prog_compiler_static='-static'
;;
# Lahey Fortran 8.1.
lf95*)
lt_prog_compiler_wl='-Wl,'
lt_prog_compiler_pic='--shared'
lt_prog_compiler_static='--static'
;;
nagfor*)
# NAG Fortran compiler
lt_prog_compiler_wl='-Wl,-Wl,,'
lt_prog_compiler_pic='-PIC'
lt_prog_compiler_static='-Bstatic'
;;
+ tcc*)
+ # Fabrice Bellard et al's Tiny C Compiler
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fPIC'
+ lt_prog_compiler_static='-static'
+ ;;
pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*)
# Portland Group compilers (*not* the Pentium gcc compiler,
# which looks to be a dead project)
lt_prog_compiler_wl='-Wl,'
lt_prog_compiler_pic='-fpic'
lt_prog_compiler_static='-Bstatic'
;;
ccc*)
lt_prog_compiler_wl='-Wl,'
# All Alpha code is PIC.
lt_prog_compiler_static='-non_shared'
;;
xl* | bgxl* | bgf* | mpixl*)
# IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene
lt_prog_compiler_wl='-Wl,'
lt_prog_compiler_pic='-qpic'
lt_prog_compiler_static='-qstaticlink'
;;
*)
case `$CC -V 2>&1 | sed 5q` in
*Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*)
# Sun Fortran 8.3 passes all unrecognized flags to the linker
lt_prog_compiler_pic='-KPIC'
lt_prog_compiler_static='-Bstatic'
lt_prog_compiler_wl=''
;;
*Sun\ F* | *Sun*Fortran*)
lt_prog_compiler_pic='-KPIC'
lt_prog_compiler_static='-Bstatic'
lt_prog_compiler_wl='-Qoption ld '
;;
*Sun\ C*)
# Sun C 5.9
lt_prog_compiler_pic='-KPIC'
lt_prog_compiler_static='-Bstatic'
lt_prog_compiler_wl='-Wl,'
;;
*Intel*\ [CF]*Compiler*)
lt_prog_compiler_wl='-Wl,'
lt_prog_compiler_pic='-fPIC'
lt_prog_compiler_static='-static'
;;
*Portland\ Group*)
lt_prog_compiler_wl='-Wl,'
lt_prog_compiler_pic='-fpic'
lt_prog_compiler_static='-Bstatic'
;;
esac
;;
esac
;;
newsos6)
lt_prog_compiler_pic='-KPIC'
lt_prog_compiler_static='-Bstatic'
;;
*nto* | *qnx*)
# QNX uses GNU C++, but need to define -shared option too, otherwise
# it will coredump.
lt_prog_compiler_pic='-fPIC -shared'
;;
osf3* | osf4* | osf5*)
lt_prog_compiler_wl='-Wl,'
# All OSF/1 code is PIC.
lt_prog_compiler_static='-non_shared'
;;
rdos*)
lt_prog_compiler_static='-non_shared'
;;
solaris*)
lt_prog_compiler_pic='-KPIC'
lt_prog_compiler_static='-Bstatic'
case $cc_basename in
f77* | f90* | f95* | sunf77* | sunf90* | sunf95*)
lt_prog_compiler_wl='-Qoption ld ';;
*)
lt_prog_compiler_wl='-Wl,';;
esac
;;
sunos4*)
lt_prog_compiler_wl='-Qoption ld '
lt_prog_compiler_pic='-PIC'
lt_prog_compiler_static='-Bstatic'
;;
sysv4 | sysv4.2uw2* | sysv4.3*)
lt_prog_compiler_wl='-Wl,'
lt_prog_compiler_pic='-KPIC'
lt_prog_compiler_static='-Bstatic'
;;
sysv4*MP*)
- if test -d /usr/nec ;then
+ if test -d /usr/nec; then
lt_prog_compiler_pic='-Kconform_pic'
lt_prog_compiler_static='-Bstatic'
fi
;;
sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
lt_prog_compiler_wl='-Wl,'
lt_prog_compiler_pic='-KPIC'
lt_prog_compiler_static='-Bstatic'
;;
unicos*)
lt_prog_compiler_wl='-Wl,'
lt_prog_compiler_can_build_shared=no
;;
uts4*)
lt_prog_compiler_pic='-pic'
lt_prog_compiler_static='-Bstatic'
;;
*)
lt_prog_compiler_can_build_shared=no
;;
esac
fi
case $host_os in
- # For platforms which do not support PIC, -DPIC is meaningless:
+ # For platforms that do not support PIC, -DPIC is meaningless:
*djgpp*)
lt_prog_compiler_pic=
;;
*)
lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC"
;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5
$as_echo_n "checking for $compiler option to produce PIC... " >&6; }
if ${lt_cv_prog_compiler_pic+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_prog_compiler_pic=$lt_prog_compiler_pic
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5
$as_echo "$lt_cv_prog_compiler_pic" >&6; }
lt_prog_compiler_pic=$lt_cv_prog_compiler_pic
#
# Check to make sure the PIC flag actually works.
#
if test -n "$lt_prog_compiler_pic"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5
$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; }
if ${lt_cv_prog_compiler_pic_works+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_prog_compiler_pic_works=no
ac_outfile=conftest.$ac_objext
echo "$lt_simple_compile_test_code" > conftest.$ac_ext
- lt_compiler_flag="$lt_prog_compiler_pic -DPIC"
+ lt_compiler_flag="$lt_prog_compiler_pic -DPIC" ## exclude from sc_useless_quotes_in_assignment
# Insert the option either (1) after the last *FLAGS variable, or
# (2) before a word containing "conftest.", or (3) at the end.
# Note that $ac_compile itself does not contain backslashes and begins
# with a dollar sign (not a hyphen), so the echo should work correctly.
# The option is referenced via a variable to avoid confusing sed.
lt_compile=`echo "$ac_compile" | $SED \
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
$ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp
$SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
lt_cv_prog_compiler_pic_works=yes
fi
fi
$RM conftest*
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5
$as_echo "$lt_cv_prog_compiler_pic_works" >&6; }
-if test x"$lt_cv_prog_compiler_pic_works" = xyes; then
+if test yes = "$lt_cv_prog_compiler_pic_works"; then
case $lt_prog_compiler_pic in
"" | " "*) ;;
*) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;;
esac
else
lt_prog_compiler_pic=
lt_prog_compiler_can_build_shared=no
fi
fi
#
# Check to make sure the static flag actually works.
#
wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5
$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; }
if ${lt_cv_prog_compiler_static_works+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_prog_compiler_static_works=no
- save_LDFLAGS="$LDFLAGS"
+ save_LDFLAGS=$LDFLAGS
LDFLAGS="$LDFLAGS $lt_tmp_static_flag"
echo "$lt_simple_link_test_code" > conftest.$ac_ext
if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
# The linker can only warn and ignore the option if not recognized
# So say no if there are warnings
if test -s conftest.err; then
# Append any errors to the config.log.
cat conftest.err 1>&5
$ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp
$SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
if diff conftest.exp conftest.er2 >/dev/null; then
lt_cv_prog_compiler_static_works=yes
fi
else
lt_cv_prog_compiler_static_works=yes
fi
fi
$RM -r conftest*
- LDFLAGS="$save_LDFLAGS"
+ LDFLAGS=$save_LDFLAGS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5
$as_echo "$lt_cv_prog_compiler_static_works" >&6; }
-if test x"$lt_cv_prog_compiler_static_works" = xyes; then
+if test yes = "$lt_cv_prog_compiler_static_works"; then
:
else
lt_prog_compiler_static=
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
if ${lt_cv_prog_compiler_c_o+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_prog_compiler_c_o=no
$RM -r conftest 2>/dev/null
mkdir conftest
cd conftest
mkdir out
echo "$lt_simple_compile_test_code" > conftest.$ac_ext
lt_compiler_flag="-o out/conftest2.$ac_objext"
# Insert the option either (1) after the last *FLAGS variable, or
# (2) before a word containing "conftest.", or (3) at the end.
# Note that $ac_compile itself does not contain backslashes and begins
# with a dollar sign (not a hyphen), so the echo should work correctly.
lt_compile=`echo "$ac_compile" | $SED \
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings
$ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
$SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
lt_cv_prog_compiler_c_o=yes
fi
fi
chmod u+w . 2>&5
$RM conftest*
# SGI C++ compiler will create directory out/ii_files/ for
# template instantiation
test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
$RM out/* && rmdir out
cd ..
$RM -r conftest
$RM conftest*
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5
$as_echo "$lt_cv_prog_compiler_c_o" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
if ${lt_cv_prog_compiler_c_o+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_prog_compiler_c_o=no
$RM -r conftest 2>/dev/null
mkdir conftest
cd conftest
mkdir out
echo "$lt_simple_compile_test_code" > conftest.$ac_ext
lt_compiler_flag="-o out/conftest2.$ac_objext"
# Insert the option either (1) after the last *FLAGS variable, or
# (2) before a word containing "conftest.", or (3) at the end.
# Note that $ac_compile itself does not contain backslashes and begins
# with a dollar sign (not a hyphen), so the echo should work correctly.
lt_compile=`echo "$ac_compile" | $SED \
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings
$ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
$SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
lt_cv_prog_compiler_c_o=yes
fi
fi
chmod u+w . 2>&5
$RM conftest*
# SGI C++ compiler will create directory out/ii_files/ for
# template instantiation
test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
$RM out/* && rmdir out
cd ..
$RM -r conftest
$RM conftest*
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5
$as_echo "$lt_cv_prog_compiler_c_o" >&6; }
-hard_links="nottested"
-if test "$lt_cv_prog_compiler_c_o" = no && test "$need_locks" != no; then
+hard_links=nottested
+if test no = "$lt_cv_prog_compiler_c_o" && test no != "$need_locks"; then
# do not overwrite the value of need_locks provided by the user
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5
$as_echo_n "checking if we can lock with hard links... " >&6; }
hard_links=yes
$RM conftest*
ln conftest.a conftest.b 2>/dev/null && hard_links=no
touch conftest.a
ln conftest.a conftest.b 2>&5 || hard_links=no
ln conftest.a conftest.b 2>/dev/null && hard_links=no
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5
$as_echo "$hard_links" >&6; }
- if test "$hard_links" = no; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5
-$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;}
+ if test no = "$hard_links"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5
+$as_echo "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;}
need_locks=warn
fi
else
need_locks=no
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5
$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; }
runpath_var=
allow_undefined_flag=
always_export_symbols=no
archive_cmds=
archive_expsym_cmds=
compiler_needs_object=no
enable_shared_with_static_runtimes=no
export_dynamic_flag_spec=
export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
hardcode_automatic=no
hardcode_direct=no
hardcode_direct_absolute=no
hardcode_libdir_flag_spec=
hardcode_libdir_separator=
hardcode_minus_L=no
hardcode_shlibpath_var=unsupported
inherit_rpath=no
link_all_deplibs=unknown
module_cmds=
module_expsym_cmds=
old_archive_from_new_cmds=
old_archive_from_expsyms_cmds=
thread_safe_flag_spec=
whole_archive_flag_spec=
# include_expsyms should be a list of space-separated symbols to be *always*
# included in the symbol list
include_expsyms=
# exclude_expsyms can be an extended regexp of symbols to exclude
- # it will be wrapped by ` (' and `)$', so one must not match beginning or
- # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
- # as well as any symbol that contains `d'.
+ # it will be wrapped by ' (' and ')$', so one must not match beginning or
+ # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc',
+ # as well as any symbol that contains 'd'.
exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'
# Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
# platforms (ab)use it in PIC code, but their linkers get confused if
# the symbol is explicitly referenced. Since portable code cannot
# rely on this symbol name, it's probably fine to never include it in
# preloaded symbol tables.
# Exclude shared library initialization/finalization symbols.
extract_expsyms_cmds=
case $host_os in
cygwin* | mingw* | pw32* | cegcc*)
# FIXME: the MSVC++ port hasn't been tested in a loooong time
# When not using gcc, we currently assume that we are using
# Microsoft Visual C++.
- if test "$GCC" != yes; then
+ if test yes != "$GCC"; then
with_gnu_ld=no
fi
;;
interix*)
# we just hope/assume this is gcc and not c89 (= MSVC++)
with_gnu_ld=yes
;;
- openbsd*)
+ openbsd* | bitrig*)
with_gnu_ld=no
;;
esac
ld_shlibs=yes
# On some targets, GNU ld is compatible enough with the native linker
# that we're better off using the native interface for both.
lt_use_gnu_ld_interface=no
- if test "$with_gnu_ld" = yes; then
+ if test yes = "$with_gnu_ld"; then
case $host_os in
aix*)
# The AIX port of GNU ld has always aspired to compatibility
# with the native linker. However, as the warning in the GNU ld
# block says, versions before 2.19.5* couldn't really create working
# shared libraries, regardless of the interface used.
case `$LD -v 2>&1` in
*\ \(GNU\ Binutils\)\ 2.19.5*) ;;
*\ \(GNU\ Binutils\)\ 2.[2-9]*) ;;
*\ \(GNU\ Binutils\)\ [3-9]*) ;;
*)
lt_use_gnu_ld_interface=yes
;;
esac
;;
*)
lt_use_gnu_ld_interface=yes
;;
esac
fi
- if test "$lt_use_gnu_ld_interface" = yes; then
+ if test yes = "$lt_use_gnu_ld_interface"; then
# If archive_cmds runs LD, not CC, wlarc should be empty
- wlarc='${wl}'
+ wlarc='$wl'
# Set some defaults for GNU ld with shared library support. These
# are reset later if shared libraries are not supported. Putting them
# here allows them to be overridden if necessary.
runpath_var=LD_RUN_PATH
- hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
- export_dynamic_flag_spec='${wl}--export-dynamic'
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+ export_dynamic_flag_spec='$wl--export-dynamic'
# ancient GNU ld didn't support --whole-archive et. al.
if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
- whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+ whole_archive_flag_spec=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive'
else
whole_archive_flag_spec=
fi
supports_anon_versioning=no
- case `$LD -v 2>&1` in
+ case `$LD -v | $SED -e 's/(^)\+)\s\+//' 2>&1` in
*GNU\ gold*) supports_anon_versioning=yes ;;
*\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11
*\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
*\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
*\ 2.11.*) ;; # other 2.11 versions
*) supports_anon_versioning=yes ;;
esac
# See if GNU ld supports shared libraries.
case $host_os in
aix[3-9]*)
# On AIX/PPC, the GNU linker is very broken
- if test "$host_cpu" != ia64; then
+ if test ia64 != "$host_cpu"; then
ld_shlibs=no
cat <<_LT_EOF 1>&2
*** Warning: the GNU linker, at least up to release 2.19, is reported
*** to be unable to reliably create shared libraries on AIX.
*** Therefore, libtool is disabling shared libraries support. If you
*** really care for shared libraries, you may want to install binutils
*** 2.20 or above, or modify your PATH so that a non-GNU linker is found.
*** You will then need to restart the configuration process.
_LT_EOF
fi
;;
amigaos*)
case $host_cpu in
powerpc)
# see comment about AmigaOS4 .so support
- archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
archive_expsym_cmds=''
;;
m68k)
archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
hardcode_libdir_flag_spec='-L$libdir'
hardcode_minus_L=yes
;;
esac
;;
beos*)
if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
allow_undefined_flag=unsupported
# Joseph Beckenbach <jrb3@best.com> says some releases of gcc
# support --undefined. This deserves some investigation. FIXME
- archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
else
ld_shlibs=no
fi
;;
cygwin* | mingw* | pw32* | cegcc*)
# _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless,
# as there is no search path for DLLs.
hardcode_libdir_flag_spec='-L$libdir'
- export_dynamic_flag_spec='${wl}--export-all-symbols'
+ export_dynamic_flag_spec='$wl--export-all-symbols'
allow_undefined_flag=unsupported
always_export_symbols=no
enable_shared_with_static_runtimes=yes
export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols'
exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'
if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
- archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
- # If the export-symbols file already is a .def file (1st line
- # is EXPORTS), use it as is; otherwise, prepend...
- archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
- cp $export_symbols $output_objdir/$soname.def;
- else
- echo EXPORTS > $output_objdir/$soname.def;
- cat $export_symbols >> $output_objdir/$soname.def;
- fi~
- $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ # If the export-symbols file already is a .def file, use it as
+ # is; otherwise, prepend EXPORTS...
+ archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then
+ cp $export_symbols $output_objdir/$soname.def;
+ else
+ echo EXPORTS > $output_objdir/$soname.def;
+ cat $export_symbols >> $output_objdir/$soname.def;
+ fi~
+ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
else
ld_shlibs=no
fi
;;
haiku*)
- archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
link_all_deplibs=yes
;;
interix[3-9]*)
hardcode_direct=no
hardcode_shlibpath_var=no
- hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
- export_dynamic_flag_spec='${wl}-E'
+ hardcode_libdir_flag_spec='$wl-rpath,$libdir'
+ export_dynamic_flag_spec='$wl-E'
# Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
# Instead, shared libraries are loaded at an image base (0x10000000 by
# default) and relocated if they conflict, which is a slow very memory
# consuming and fragmenting process. To avoid this, we pick a random,
# 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
# time. Moving up from 0x10000000 also allows more sbrk(2) space.
- archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
- archive_expsym_cmds='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ archive_expsym_cmds='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
;;
gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu)
tmp_diet=no
- if test "$host_os" = linux-dietlibc; then
+ if test linux-dietlibc = "$host_os"; then
case $cc_basename in
diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn)
esac
fi
if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
- && test "$tmp_diet" = no
+ && test no = "$tmp_diet"
then
tmp_addflag=' $pic_flag'
tmp_sharedflag='-shared'
case $cc_basename,$host_cpu in
pgcc*) # Portland Group C compiler
- whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+ whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
tmp_addflag=' $pic_flag'
;;
pgf77* | pgf90* | pgf95* | pgfortran*)
# Portland Group f77 and f90 compilers
- whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+ whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
tmp_addflag=' $pic_flag -Mnomain' ;;
ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64
tmp_addflag=' -i_dynamic' ;;
efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64
tmp_addflag=' -i_dynamic -nofor_main' ;;
ifc* | ifort*) # Intel Fortran compiler
tmp_addflag=' -nofor_main' ;;
lf95*) # Lahey Fortran 8.1
whole_archive_flag_spec=
tmp_sharedflag='--shared' ;;
+ nagfor*) # NAGFOR 5.3
+ tmp_sharedflag='-Wl,-shared' ;;
xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below)
tmp_sharedflag='-qmkshrobj'
tmp_addflag= ;;
nvcc*) # Cuda Compiler Driver 2.2
- whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+ whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
compiler_needs_object=yes
;;
esac
case `$CC -V 2>&1 | sed 5q` in
*Sun\ C*) # Sun C 5.9
- whole_archive_flag_spec='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+ whole_archive_flag_spec='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
compiler_needs_object=yes
tmp_sharedflag='-G' ;;
*Sun\ F*) # Sun Fortran 8.3
tmp_sharedflag='-G' ;;
esac
- archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
- if test "x$supports_anon_versioning" = xyes; then
+ if test yes = "$supports_anon_versioning"; then
archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
- cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
- echo "local: *; };" >> $output_objdir/$libname.ver~
- $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib'
fi
case $cc_basename in
xlf* | bgf* | bgxlf* | mpixlf*)
# IBM XL Fortran 10.1 on PPC cannot create shared libs itself
whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive'
- hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib'
- if test "x$supports_anon_versioning" = xyes; then
+ if test yes = "$supports_anon_versioning"; then
archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
- cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
- echo "local: *; };" >> $output_objdir/$libname.ver~
- $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
fi
;;
esac
else
ld_shlibs=no
fi
;;
netbsd*)
if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
wlarc=
else
- archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
- archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
fi
;;
solaris*)
if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then
ld_shlibs=no
cat <<_LT_EOF 1>&2
*** Warning: The releases 2.8.* of the GNU linker cannot reliably
*** create shared libraries on Solaris systems. Therefore, libtool
*** is disabling shared libraries support. We urge you to upgrade GNU
*** binutils to release 2.9.1 or newer. Another option is to modify
*** your PATH or compiler configuration so that the native linker is
*** used, and then restart.
_LT_EOF
elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
- archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
- archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
else
ld_shlibs=no
fi
;;
sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
case `$LD -v 2>&1` in
*\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*)
ld_shlibs=no
cat <<_LT_EOF 1>&2
-*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not
+*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot
*** reliably create shared libraries on SCO systems. Therefore, libtool
*** is disabling shared libraries support. We urge you to upgrade GNU
*** binutils to release 2.16.91.0.3 or newer. Another option is to modify
*** your PATH or compiler configuration so that the native linker is
*** used, and then restart.
_LT_EOF
;;
*)
# For security reasons, it is highly recommended that you always
# use absolute paths for naming shared libraries, and exclude the
# DT_RUNPATH tag from executables and libraries. But doing so
# requires that you compile everything twice, which is a pain.
if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
- hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
- archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
- archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
else
ld_shlibs=no
fi
;;
esac
;;
sunos4*)
archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
wlarc=
hardcode_direct=yes
hardcode_shlibpath_var=no
;;
*)
if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
- archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
- archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
else
ld_shlibs=no
fi
;;
esac
- if test "$ld_shlibs" = no; then
+ if test no = "$ld_shlibs"; then
runpath_var=
hardcode_libdir_flag_spec=
export_dynamic_flag_spec=
whole_archive_flag_spec=
fi
else
# PORTME fill in a description of your system's linker (not GNU ld)
case $host_os in
aix3*)
allow_undefined_flag=unsupported
always_export_symbols=yes
archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
# Note: this linker hardcodes the directories in LIBPATH if there
# are no directories specified by -L.
hardcode_minus_L=yes
- if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then
+ if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then
# Neither direct hardcoding nor static linking is supported with a
# broken collect2.
hardcode_direct=unsupported
fi
;;
aix[4-9]*)
- if test "$host_cpu" = ia64; then
+ if test ia64 = "$host_cpu"; then
# On IA64, the linker does run time linking by default, so we don't
# have to do anything special.
aix_use_runtimelinking=no
exp_sym_flag='-Bexport'
- no_entry_flag=""
+ no_entry_flag=
else
# If we're using GNU nm, then we don't want the "-C" option.
# -C means demangle to AIX nm, but means don't demangle with GNU nm
# Also, AIX nm treats weak defined symbols like other global
# defined symbols, whereas GNU nm marks them as "W".
if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
else
export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
fi
aix_use_runtimelinking=no
# Test if we are trying to use run time linking or normal
# AIX style linking. If -brtl is somewhere in LDFLAGS, we
# need to do runtime linking.
case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*)
for ld_flag in $LDFLAGS; do
- if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
+ if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then
aix_use_runtimelinking=yes
break
fi
done
;;
esac
exp_sym_flag='-bexport'
no_entry_flag='-bnoentry'
fi
# When large executables or shared objects are built, AIX ld can
# have problems creating the table of contents. If linking a library
# or program results in "error TOC overflow" add -mminimal-toc to
# CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not
# enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
archive_cmds=''
hardcode_direct=yes
hardcode_direct_absolute=yes
hardcode_libdir_separator=':'
link_all_deplibs=yes
- file_list_spec='${wl}-f,'
+ file_list_spec='$wl-f,'
- if test "$GCC" = yes; then
+ if test yes = "$GCC"; then
case $host_os in aix4.[012]|aix4.[012].*)
# We only want to do this on AIX 4.2 and lower, the check
# below for broken collect2 doesn't work under 4.3+
- collect2name=`${CC} -print-prog-name=collect2`
+ collect2name=`$CC -print-prog-name=collect2`
if test -f "$collect2name" &&
strings "$collect2name" | $GREP resolve_lib_name >/dev/null
then
# We have reworked collect2
:
else
# We have old collect2
hardcode_direct=unsupported
# It fails to find uninstalled libraries when the uninstalled
# path is not listed in the libpath. Setting hardcode_minus_L
# to unsupported forces relinking
hardcode_minus_L=yes
hardcode_libdir_flag_spec='-L$libdir'
hardcode_libdir_separator=
fi
;;
esac
shared_flag='-shared'
- if test "$aix_use_runtimelinking" = yes; then
- shared_flag="$shared_flag "'${wl}-G'
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag="$shared_flag "'$wl-G'
fi
else
# not using gcc
- if test "$host_cpu" = ia64; then
+ if test ia64 = "$host_cpu"; then
# VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
# chokes on -Wl,-G. The following line is correct:
shared_flag='-G'
else
- if test "$aix_use_runtimelinking" = yes; then
- shared_flag='${wl}-G'
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag='$wl-G'
else
- shared_flag='${wl}-bM:SRE'
+ shared_flag='$wl-bM:SRE'
fi
fi
fi
- export_dynamic_flag_spec='${wl}-bexpall'
+ export_dynamic_flag_spec='$wl-bexpall'
# It seems that -bexpall does not export symbols beginning with
# underscore (_), so it is better to generate a list of symbols to export.
always_export_symbols=yes
- if test "$aix_use_runtimelinking" = yes; then
+ if test yes = "$aix_use_runtimelinking"; then
# Warning - without using the other runtime loading flags (-brtl),
# -berok will link without error, but may produce a broken library.
allow_undefined_flag='-berok'
# Determine the default libpath from the value encoded in an
# empty executable.
- if test "${lt_cv_aix_libpath+set}" = set; then
+ if test set = "${lt_cv_aix_libpath+set}"; then
aix_libpath=$lt_cv_aix_libpath
else
if ${lt_cv_aix_libpath_+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
lt_aix_libpath_sed='
/Import File Strings/,/^$/ {
/^0/ {
s/^0 *\([^ ]*\) *$/\1/
p
}
}'
lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
# Check for a 64-bit object if we didn't find anything.
if test -z "$lt_cv_aix_libpath_"; then
lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
fi
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
if test -z "$lt_cv_aix_libpath_"; then
- lt_cv_aix_libpath_="/usr/lib:/lib"
+ lt_cv_aix_libpath_=/usr/lib:/lib
fi
fi
aix_libpath=$lt_cv_aix_libpath_
fi
- hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
- archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+ hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath"
+ archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag
else
- if test "$host_cpu" = ia64; then
- hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib'
+ if test ia64 = "$host_cpu"; then
+ hardcode_libdir_flag_spec='$wl-R $libdir:/usr/lib:/lib'
allow_undefined_flag="-z nodefs"
- archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+ archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols"
else
# Determine the default libpath from the value encoded in an
# empty executable.
- if test "${lt_cv_aix_libpath+set}" = set; then
+ if test set = "${lt_cv_aix_libpath+set}"; then
aix_libpath=$lt_cv_aix_libpath
else
if ${lt_cv_aix_libpath_+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
lt_aix_libpath_sed='
/Import File Strings/,/^$/ {
/^0/ {
s/^0 *\([^ ]*\) *$/\1/
p
}
}'
lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
# Check for a 64-bit object if we didn't find anything.
if test -z "$lt_cv_aix_libpath_"; then
lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
fi
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
if test -z "$lt_cv_aix_libpath_"; then
- lt_cv_aix_libpath_="/usr/lib:/lib"
+ lt_cv_aix_libpath_=/usr/lib:/lib
fi
fi
aix_libpath=$lt_cv_aix_libpath_
fi
- hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+ hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath"
# Warning - without using the other run time loading flags,
# -berok will link without error, but may produce a broken library.
- no_undefined_flag=' ${wl}-bernotok'
- allow_undefined_flag=' ${wl}-berok'
- if test "$with_gnu_ld" = yes; then
+ no_undefined_flag=' $wl-bernotok'
+ allow_undefined_flag=' $wl-berok'
+ if test yes = "$with_gnu_ld"; then
# We only use this code for GNU lds that support --whole-archive.
- whole_archive_flag_spec='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
+ whole_archive_flag_spec='$wl--whole-archive$convenience $wl--no-whole-archive'
else
# Exported symbols can be pulled into shared objects from archives
whole_archive_flag_spec='$convenience'
fi
archive_cmds_need_lc=yes
# This is similar to how AIX traditionally builds its shared libraries.
- archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+ archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $wl-bnoentry $compiler_flags $wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
fi
fi
;;
amigaos*)
case $host_cpu in
powerpc)
# see comment about AmigaOS4 .so support
- archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
archive_expsym_cmds=''
;;
m68k)
archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
hardcode_libdir_flag_spec='-L$libdir'
hardcode_minus_L=yes
;;
esac
;;
bsdi[45]*)
export_dynamic_flag_spec=-rdynamic
;;
cygwin* | mingw* | pw32* | cegcc*)
# When not using gcc, we currently assume that we are using
# Microsoft Visual C++.
# hardcode_libdir_flag_spec is actually meaningless, as there is
# no search path for DLLs.
case $cc_basename in
cl*)
# Native MSVC
hardcode_libdir_flag_spec=' '
allow_undefined_flag=unsupported
always_export_symbols=yes
file_list_spec='@'
# Tell ltmain to make .lib files, not .a files.
libext=lib
# Tell ltmain to make .dll files, not .so files.
- shrext_cmds=".dll"
+ shrext_cmds=.dll
# FIXME: Setting linknames here is a bad hack.
- archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames='
- archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
- sed -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp;
- else
- sed -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp;
- fi~
- $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
- linknames='
+ archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames='
+ archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then
+ cp "$export_symbols" "$output_objdir/$soname.def";
+ echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp";
+ else
+ $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp;
+ fi~
+ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
+ linknames='
# The linker will not automatically build a static lib if we build a DLL.
# _LT_TAGVAR(old_archive_from_new_cmds, )='true'
enable_shared_with_static_runtimes=yes
exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols'
# Don't use ranlib
old_postinstall_cmds='chmod 644 $oldlib'
postlink_cmds='lt_outputfile="@OUTPUT@"~
- lt_tool_outputfile="@TOOL_OUTPUT@"~
- case $lt_outputfile in
- *.exe|*.EXE) ;;
- *)
- lt_outputfile="$lt_outputfile.exe"
- lt_tool_outputfile="$lt_tool_outputfile.exe"
- ;;
- esac~
- if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then
- $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
- $RM "$lt_outputfile.manifest";
- fi'
+ lt_tool_outputfile="@TOOL_OUTPUT@"~
+ case $lt_outputfile in
+ *.exe|*.EXE) ;;
+ *)
+ lt_outputfile=$lt_outputfile.exe
+ lt_tool_outputfile=$lt_tool_outputfile.exe
+ ;;
+ esac~
+ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then
+ $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
+ $RM "$lt_outputfile.manifest";
+ fi'
;;
*)
# Assume MSVC wrapper
hardcode_libdir_flag_spec=' '
allow_undefined_flag=unsupported
# Tell ltmain to make .lib files, not .a files.
libext=lib
# Tell ltmain to make .dll files, not .so files.
- shrext_cmds=".dll"
+ shrext_cmds=.dll
# FIXME: Setting linknames here is a bad hack.
archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames='
# The linker will automatically build a .lib file if we build a DLL.
old_archive_from_new_cmds='true'
# FIXME: Should let the user specify the lib program.
old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs'
enable_shared_with_static_runtimes=yes
;;
esac
;;
darwin* | rhapsody*)
archive_cmds_need_lc=no
hardcode_direct=no
hardcode_automatic=yes
hardcode_shlibpath_var=unsupported
- if test "$lt_cv_ld_force_load" = "yes"; then
- whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`'
+ if test yes = "$lt_cv_ld_force_load"; then
+ whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`'
else
whole_archive_flag_spec=''
fi
link_all_deplibs=yes
- allow_undefined_flag="$_lt_dar_allow_undefined"
+ allow_undefined_flag=$_lt_dar_allow_undefined
case $cc_basename in
- ifort*) _lt_dar_can_shared=yes ;;
+ ifort*|nagfor*) _lt_dar_can_shared=yes ;;
*) _lt_dar_can_shared=$GCC ;;
esac
- if test "$_lt_dar_can_shared" = "yes"; then
+ if test yes = "$_lt_dar_can_shared"; then
output_verbose_link_cmd=func_echo_all
- archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}"
- module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}"
- archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}"
- module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}"
+ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil"
+ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil"
+ archive_expsym_cmds="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil"
+ module_expsym_cmds="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil"
else
ld_shlibs=no
fi
;;
dgux*)
archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
hardcode_libdir_flag_spec='-L$libdir'
hardcode_shlibpath_var=no
;;
# FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
# support. Future versions do this automatically, but an explicit c++rt0.o
# does not break anything, and helps significantly (at the cost of a little
# extra space).
freebsd2.2*)
archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
hardcode_libdir_flag_spec='-R$libdir'
hardcode_direct=yes
hardcode_shlibpath_var=no
;;
# Unfortunately, older versions of FreeBSD 2 do not have this feature.
freebsd2.*)
archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
hardcode_direct=yes
hardcode_minus_L=yes
hardcode_shlibpath_var=no
;;
# FreeBSD 3 and greater uses gcc -shared to do shared libraries.
freebsd* | dragonfly*)
archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
hardcode_libdir_flag_spec='-R$libdir'
hardcode_direct=yes
hardcode_shlibpath_var=no
;;
hpux9*)
- if test "$GCC" = yes; then
- archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ if test yes = "$GCC"; then
+ archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
else
- archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
fi
- hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+ hardcode_libdir_flag_spec='$wl+b $wl$libdir'
hardcode_libdir_separator=:
hardcode_direct=yes
# hardcode_minus_L: Not really in the search PATH,
# but as the default location of the library.
hardcode_minus_L=yes
- export_dynamic_flag_spec='${wl}-E'
+ export_dynamic_flag_spec='$wl-E'
;;
hpux10*)
- if test "$GCC" = yes && test "$with_gnu_ld" = no; then
- archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ if test yes,no = "$GCC,$with_gnu_ld"; then
+ archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
else
archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
fi
- if test "$with_gnu_ld" = no; then
- hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+ if test no = "$with_gnu_ld"; then
+ hardcode_libdir_flag_spec='$wl+b $wl$libdir'
hardcode_libdir_separator=:
hardcode_direct=yes
hardcode_direct_absolute=yes
- export_dynamic_flag_spec='${wl}-E'
+ export_dynamic_flag_spec='$wl-E'
# hardcode_minus_L: Not really in the search PATH,
# but as the default location of the library.
hardcode_minus_L=yes
fi
;;
hpux11*)
- if test "$GCC" = yes && test "$with_gnu_ld" = no; then
+ if test yes,no = "$GCC,$with_gnu_ld"; then
case $host_cpu in
hppa*64*)
- archive_cmds='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_cmds='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
;;
ia64*)
- archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
;;
*)
- archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
;;
esac
else
case $host_cpu in
hppa*64*)
- archive_cmds='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_cmds='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
;;
ia64*)
- archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ archive_cmds='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
;;
*)
# Older versions of the 11.00 compiler do not understand -b yet
# (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does)
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5
$as_echo_n "checking if $CC understands -b... " >&6; }
if ${lt_cv_prog_compiler__b+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_prog_compiler__b=no
- save_LDFLAGS="$LDFLAGS"
+ save_LDFLAGS=$LDFLAGS
LDFLAGS="$LDFLAGS -b"
echo "$lt_simple_link_test_code" > conftest.$ac_ext
if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
# The linker can only warn and ignore the option if not recognized
# So say no if there are warnings
if test -s conftest.err; then
# Append any errors to the config.log.
cat conftest.err 1>&5
$ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp
$SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
if diff conftest.exp conftest.er2 >/dev/null; then
lt_cv_prog_compiler__b=yes
fi
else
lt_cv_prog_compiler__b=yes
fi
fi
$RM -r conftest*
- LDFLAGS="$save_LDFLAGS"
+ LDFLAGS=$save_LDFLAGS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5
$as_echo "$lt_cv_prog_compiler__b" >&6; }
-if test x"$lt_cv_prog_compiler__b" = xyes; then
- archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+if test yes = "$lt_cv_prog_compiler__b"; then
+ archive_cmds='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
else
archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
fi
;;
esac
fi
- if test "$with_gnu_ld" = no; then
- hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+ if test no = "$with_gnu_ld"; then
+ hardcode_libdir_flag_spec='$wl+b $wl$libdir'
hardcode_libdir_separator=:
case $host_cpu in
hppa*64*|ia64*)
hardcode_direct=no
hardcode_shlibpath_var=no
;;
*)
hardcode_direct=yes
hardcode_direct_absolute=yes
- export_dynamic_flag_spec='${wl}-E'
+ export_dynamic_flag_spec='$wl-E'
# hardcode_minus_L: Not really in the search PATH,
# but as the default location of the library.
hardcode_minus_L=yes
;;
esac
fi
;;
irix5* | irix6* | nonstopux*)
- if test "$GCC" = yes; then
- archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ if test yes = "$GCC"; then
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
# Try to use the -exported_symbol ld option, if it does not
# work, assume that -exports_file does not work either and
# implicitly export all symbols.
# This should be the same for all languages, so no per-tag cache variable.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5
$as_echo_n "checking whether the $host_os linker accepts -exported_symbol... " >&6; }
if ${lt_cv_irix_exported_symbol+:} false; then :
$as_echo_n "(cached) " >&6
else
- save_LDFLAGS="$LDFLAGS"
- LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null"
+ save_LDFLAGS=$LDFLAGS
+ LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int foo (void) { return 0; }
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
lt_cv_irix_exported_symbol=yes
else
lt_cv_irix_exported_symbol=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
- LDFLAGS="$save_LDFLAGS"
+ LDFLAGS=$save_LDFLAGS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5
$as_echo "$lt_cv_irix_exported_symbol" >&6; }
- if test "$lt_cv_irix_exported_symbol" = yes; then
- archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib'
+ if test yes = "$lt_cv_irix_exported_symbol"; then
+ archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib'
fi
else
- archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
- archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib'
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib'
fi
archive_cmds_need_lc='no'
- hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
hardcode_libdir_separator=:
inherit_rpath=yes
link_all_deplibs=yes
;;
netbsd*)
if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out
else
archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF
fi
hardcode_libdir_flag_spec='-R$libdir'
hardcode_direct=yes
hardcode_shlibpath_var=no
;;
newsos6)
archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
hardcode_direct=yes
- hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
hardcode_libdir_separator=:
hardcode_shlibpath_var=no
;;
*nto* | *qnx*)
;;
- openbsd*)
+ openbsd* | bitrig*)
if test -f /usr/libexec/ld.so; then
hardcode_direct=yes
hardcode_shlibpath_var=no
hardcode_direct_absolute=yes
- if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
- archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols'
- hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
- export_dynamic_flag_spec='${wl}-E'
+ archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols'
+ hardcode_libdir_flag_spec='$wl-rpath,$libdir'
+ export_dynamic_flag_spec='$wl-E'
else
- case $host_os in
- openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*)
- archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
- hardcode_libdir_flag_spec='-R$libdir'
- ;;
- *)
- archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
- hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
- ;;
- esac
+ archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ hardcode_libdir_flag_spec='$wl-rpath,$libdir'
fi
else
ld_shlibs=no
fi
;;
os2*)
hardcode_libdir_flag_spec='-L$libdir'
hardcode_minus_L=yes
allow_undefined_flag=unsupported
archive_cmds='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~echo DATA >> $output_objdir/$libname.def~echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def'
old_archive_from_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def'
;;
osf3*)
- if test "$GCC" = yes; then
- allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
- archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ if test yes = "$GCC"; then
+ allow_undefined_flag=' $wl-expect_unresolved $wl\*'
+ archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
else
allow_undefined_flag=' -expect_unresolved \*'
- archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+ archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
fi
archive_cmds_need_lc='no'
- hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
hardcode_libdir_separator=:
;;
osf4* | osf5*) # as osf3* with the addition of -msym flag
- if test "$GCC" = yes; then
- allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
- archive_cmds='$CC -shared${allow_undefined_flag} $pic_flag $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
- hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ if test yes = "$GCC"; then
+ allow_undefined_flag=' $wl-expect_unresolved $wl\*'
+ archive_cmds='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
else
allow_undefined_flag=' -expect_unresolved \*'
- archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+ archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~
- $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp'
+ $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp'
# Both c and cxx compiler support -rpath directly
hardcode_libdir_flag_spec='-rpath $libdir'
fi
archive_cmds_need_lc='no'
hardcode_libdir_separator=:
;;
solaris*)
no_undefined_flag=' -z defs'
- if test "$GCC" = yes; then
- wlarc='${wl}'
- archive_cmds='$CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ if test yes = "$GCC"; then
+ wlarc='$wl'
+ archive_cmds='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
- $CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
else
case `$CC -V 2>&1` in
*"Compilers 5.0"*)
wlarc=''
- archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ archive_cmds='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags'
archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
- $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
+ $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
;;
*)
- wlarc='${wl}'
- archive_cmds='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ wlarc='$wl'
+ archive_cmds='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags'
archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
- $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
;;
esac
fi
hardcode_libdir_flag_spec='-R$libdir'
hardcode_shlibpath_var=no
case $host_os in
solaris2.[0-5] | solaris2.[0-5].*) ;;
*)
# The compiler driver will combine and reorder linker options,
- # but understands `-z linker_flag'. GCC discards it without `$wl',
+ # but understands '-z linker_flag'. GCC discards it without '$wl',
# but is careful enough not to reorder.
# Supported since Solaris 2.6 (maybe 2.5.1?)
- if test "$GCC" = yes; then
- whole_archive_flag_spec='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+ if test yes = "$GCC"; then
+ whole_archive_flag_spec='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract'
else
whole_archive_flag_spec='-z allextract$convenience -z defaultextract'
fi
;;
esac
link_all_deplibs=yes
;;
sunos4*)
- if test "x$host_vendor" = xsequent; then
+ if test sequent = "$host_vendor"; then
# Use $CC to link under sequent, because it throws in some extra .o
# files that make .init and .fini sections work.
- archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_cmds='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags'
else
archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
fi
hardcode_libdir_flag_spec='-L$libdir'
hardcode_direct=yes
hardcode_minus_L=yes
hardcode_shlibpath_var=no
;;
sysv4)
case $host_vendor in
sni)
archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
hardcode_direct=yes # is this really true???
;;
siemens)
## LD is ld it makes a PLAMLIB
## CC just makes a GrossModule.
archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags'
reload_cmds='$CC -r -o $output$reload_objs'
hardcode_direct=no
;;
motorola)
archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
hardcode_direct=no #Motorola manual says yes, but my tests say they lie
;;
esac
runpath_var='LD_RUN_PATH'
hardcode_shlibpath_var=no
;;
sysv4.3*)
archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
hardcode_shlibpath_var=no
export_dynamic_flag_spec='-Bexport'
;;
sysv4*MP*)
if test -d /usr/nec; then
archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
hardcode_shlibpath_var=no
runpath_var=LD_RUN_PATH
hardcode_runpath_var=yes
ld_shlibs=yes
fi
;;
sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*)
- no_undefined_flag='${wl}-z,text'
+ no_undefined_flag='$wl-z,text'
archive_cmds_need_lc=no
hardcode_shlibpath_var=no
runpath_var='LD_RUN_PATH'
- if test "$GCC" = yes; then
- archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
- archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ if test yes = "$GCC"; then
+ archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
else
- archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
- archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
fi
;;
sysv5* | sco3.2v5* | sco5v6*)
- # Note: We can NOT use -z defs as we might desire, because we do not
+ # Note: We CANNOT use -z defs as we might desire, because we do not
# link with -lc, and that would cause any symbols used from libc to
# always be unresolved, which means just about no library would
# ever link correctly. If we're not using GNU ld we use -z text
# though, which does catch some bad symbols but isn't as heavy-handed
# as -z defs.
- no_undefined_flag='${wl}-z,text'
- allow_undefined_flag='${wl}-z,nodefs'
+ no_undefined_flag='$wl-z,text'
+ allow_undefined_flag='$wl-z,nodefs'
archive_cmds_need_lc=no
hardcode_shlibpath_var=no
- hardcode_libdir_flag_spec='${wl}-R,$libdir'
+ hardcode_libdir_flag_spec='$wl-R,$libdir'
hardcode_libdir_separator=':'
link_all_deplibs=yes
- export_dynamic_flag_spec='${wl}-Bexport'
+ export_dynamic_flag_spec='$wl-Bexport'
runpath_var='LD_RUN_PATH'
- if test "$GCC" = yes; then
- archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
- archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ if test yes = "$GCC"; then
+ archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
else
- archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
- archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
fi
;;
uts4*)
archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
hardcode_libdir_flag_spec='-L$libdir'
hardcode_shlibpath_var=no
;;
*)
ld_shlibs=no
;;
esac
- if test x$host_vendor = xsni; then
+ if test sni = "$host_vendor"; then
case $host in
sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
- export_dynamic_flag_spec='${wl}-Blargedynsym'
+ export_dynamic_flag_spec='$wl-Blargedynsym'
;;
esac
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5
$as_echo "$ld_shlibs" >&6; }
-test "$ld_shlibs" = no && can_build_shared=no
+test no = "$ld_shlibs" && can_build_shared=no
with_gnu_ld=$with_gnu_ld
#
# Do we need to explicitly link libc?
#
case "x$archive_cmds_need_lc" in
x|xyes)
# Assume -lc should be added
archive_cmds_need_lc=yes
- if test "$enable_shared" = yes && test "$GCC" = yes; then
+ if test yes,yes = "$GCC,$enable_shared"; then
case $archive_cmds in
*'~'*)
# FIXME: we may have to deal with multi-command sequences.
;;
'$CC '*)
# Test whether the compiler implicitly links with -lc since on some
# systems, -lgcc has to come before -lc. If gcc already passes -lc
# to ld, don't add -lc before -lgcc.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5
$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; }
if ${lt_cv_archive_cmds_need_lc+:} false; then :
$as_echo_n "(cached) " >&6
else
$RM conftest*
echo "$lt_simple_compile_test_code" > conftest.$ac_ext
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } 2>conftest.err; then
soname=conftest
lib=conftest
libobjs=conftest.$ac_objext
deplibs=
wl=$lt_prog_compiler_wl
pic_flag=$lt_prog_compiler_pic
compiler_flags=-v
linker_flags=-v
verstring=
output_objdir=.
libname=conftest
lt_save_allow_undefined_flag=$allow_undefined_flag
allow_undefined_flag=
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5
(eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }
then
lt_cv_archive_cmds_need_lc=no
else
lt_cv_archive_cmds_need_lc=yes
fi
allow_undefined_flag=$lt_save_allow_undefined_flag
else
cat conftest.err 1>&5
fi
$RM conftest*
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5
$as_echo "$lt_cv_archive_cmds_need_lc" >&6; }
archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc
;;
esac
fi
;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5
$as_echo_n "checking dynamic linker characteristics... " >&6; }
-if test "$GCC" = yes; then
+if test yes = "$GCC"; then
case $host_os in
- darwin*) lt_awk_arg="/^libraries:/,/LR/" ;;
- *) lt_awk_arg="/^libraries:/" ;;
+ darwin*) lt_awk_arg='/^libraries:/,/LR/' ;;
+ *) lt_awk_arg='/^libraries:/' ;;
esac
case $host_os in
- mingw* | cegcc*) lt_sed_strip_eq="s,=\([A-Za-z]:\),\1,g" ;;
- *) lt_sed_strip_eq="s,=/,/,g" ;;
+ mingw* | cegcc*) lt_sed_strip_eq='s|=\([A-Za-z]:\)|\1|g' ;;
+ *) lt_sed_strip_eq='s|=/|/|g' ;;
esac
lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq`
case $lt_search_path_spec in
*\;*)
# if the path contains ";" then we assume it to be the separator
# otherwise default to the standard path separator (i.e. ":") - it is
# assumed that no part of a normal pathname contains ";" but that should
# okay in the real world where ";" in dirpaths is itself problematic.
lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'`
;;
*)
lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"`
;;
esac
# Ok, now we have the path, separated by spaces, we can step through it
- # and add multilib dir if necessary.
+ # and add multilib dir if necessary...
lt_tmp_lt_search_path_spec=
- lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+ lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+ # ...but if some path component already ends with the multilib dir we assume
+ # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer).
+ case "$lt_multi_os_dir; $lt_search_path_spec " in
+ "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*)
+ lt_multi_os_dir=
+ ;;
+ esac
for lt_sys_path in $lt_search_path_spec; do
- if test -d "$lt_sys_path/$lt_multi_os_dir"; then
- lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir"
- else
+ if test -d "$lt_sys_path$lt_multi_os_dir"; then
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir"
+ elif test -n "$lt_multi_os_dir"; then
test -d "$lt_sys_path" && \
lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
fi
done
lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk '
-BEGIN {RS=" "; FS="/|\n";} {
- lt_foo="";
- lt_count=0;
+BEGIN {RS = " "; FS = "/|\n";} {
+ lt_foo = "";
+ lt_count = 0;
for (lt_i = NF; lt_i > 0; lt_i--) {
if ($lt_i != "" && $lt_i != ".") {
if ($lt_i == "..") {
lt_count++;
} else {
if (lt_count == 0) {
- lt_foo="/" $lt_i lt_foo;
+ lt_foo = "/" $lt_i lt_foo;
} else {
lt_count--;
}
}
}
}
if (lt_foo != "") { lt_freq[lt_foo]++; }
if (lt_freq[lt_foo] == 1) { print lt_foo; }
}'`
# AWK program above erroneously prepends '/' to C:/dos/paths
# for these hosts.
case $host_os in
mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\
- $SED 's,/\([A-Za-z]:\),\1,g'` ;;
+ $SED 's|/\([A-Za-z]:\)|\1|g'` ;;
esac
sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP`
else
sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
fi
library_names_spec=
libname_spec='lib$name'
soname_spec=
-shrext_cmds=".so"
+shrext_cmds=.so
postinstall_cmds=
postuninstall_cmds=
finish_cmds=
finish_eval=
shlibpath_var=
shlibpath_overrides_runpath=unknown
version_type=none
dynamic_linker="$host_os ld.so"
sys_lib_dlsearch_path_spec="/lib /usr/lib"
need_lib_prefix=unknown
hardcode_into_libs=no
# when you set need_version to no, make sure it does not cause -set_version
# flags to be left without arguments
need_version=unknown
case $host_os in
aix3*)
version_type=linux # correct to gnu/linux during the next big refactor
- library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname.a'
shlibpath_var=LIBPATH
# AIX 3 has no versioning support, so we append a major version to the name.
- soname_spec='${libname}${release}${shared_ext}$major'
+ soname_spec='$libname$release$shared_ext$major'
;;
aix[4-9]*)
version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
hardcode_into_libs=yes
- if test "$host_cpu" = ia64; then
+ if test ia64 = "$host_cpu"; then
# AIX 5 supports IA64
- library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}'
+ library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext'
shlibpath_var=LD_LIBRARY_PATH
else
# With GCC up to 2.95.x, collect2 would create an import file
# for dependence libraries. The import file would start with
- # the line `#! .'. This would cause the generated library to
- # depend on `.', always an invalid library. This was fixed in
+ # the line '#! .'. This would cause the generated library to
+ # depend on '.', always an invalid library. This was fixed in
# development snapshots of GCC prior to 3.0.
case $host_os in
aix4 | aix4.[01] | aix4.[01].*)
if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
echo ' yes '
- echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then
+ echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then
:
else
can_build_shared=no
fi
;;
esac
- # AIX (on Power*) has no versioning support, so currently we can not hardcode correct
+ # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct
# soname into executable. Probably we can add versioning support to
# collect2, so additional links can be useful in future.
- if test "$aix_use_runtimelinking" = yes; then
+ if test yes = "$aix_use_runtimelinking"; then
# If using run time linking (on AIX 4.2 or later) use lib<name>.so
# instead of lib<name>.a to let people know that these are not
# typical AIX shared libraries.
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
else
# We preserve .a as extension for shared libraries through AIX4.2
# and later when we are not doing run time linking.
- library_names_spec='${libname}${release}.a $libname.a'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release.a $libname.a'
+ soname_spec='$libname$release$shared_ext$major'
fi
shlibpath_var=LIBPATH
fi
;;
amigaos*)
case $host_cpu in
powerpc)
# Since July 2007 AmigaOS4 officially supports .so libraries.
# When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
;;
m68k)
library_names_spec='$libname.ixlibrary $libname.a'
# Create ${libname}_ixlibrary.a entries in /sys/libs.
- finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+ finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
;;
esac
;;
beos*)
- library_names_spec='${libname}${shared_ext}'
+ library_names_spec='$libname$shared_ext'
dynamic_linker="$host_os ld.so"
shlibpath_var=LIBRARY_PATH
;;
bsdi[45]*)
version_type=linux # correct to gnu/linux during the next big refactor
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
shlibpath_var=LD_LIBRARY_PATH
sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
# the default ld.so.conf also contains /usr/contrib/lib and
# /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
# libtool to hard-code these into programs
;;
cygwin* | mingw* | pw32* | cegcc*)
version_type=windows
- shrext_cmds=".dll"
+ shrext_cmds=.dll
need_version=no
need_lib_prefix=no
case $GCC,$cc_basename in
yes,*)
# gcc
library_names_spec='$libname.dll.a'
# DLL is installed to $(libdir)/../bin by postinstall_cmds
- postinstall_cmds='base_file=`basename \${file}`~
- dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
dldir=$destdir/`dirname \$dlpath`~
test -d \$dldir || mkdir -p \$dldir~
$install_prog $dir/$dlname \$dldir/$dlname~
chmod a+x \$dldir/$dlname~
if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
fi'
postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
dlpath=$dir/\$dldll~
$RM \$dlpath'
shlibpath_overrides_runpath=yes
case $host_os in
cygwin*)
# Cygwin DLLs use 'cyg' prefix rather than 'lib'
- soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+ soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"
;;
mingw* | cegcc*)
# MinGW DLLs use traditional 'lib' prefix
- soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+ soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
;;
pw32*)
# pw32 DLLs use 'pw' prefix rather than 'lib'
- library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+ library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
;;
esac
dynamic_linker='Win32 ld.exe'
;;
*,cl*)
# Native MSVC
libname_spec='$name'
- soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
- library_names_spec='${libname}.dll.lib'
+ soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
+ library_names_spec='$libname.dll.lib'
case $build_os in
mingw*)
sys_lib_search_path_spec=
lt_save_ifs=$IFS
IFS=';'
for lt_path in $LIB
do
IFS=$lt_save_ifs
# Let DOS variable expansion print the short 8.3 style file name.
lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"`
sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path"
done
IFS=$lt_save_ifs
# Convert to MSYS style.
sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'`
;;
cygwin*)
# Convert to unix form, then to dos form, then back to unix form
# but this time dos style (no spaces!) so that the unix form looks
# like /cygdrive/c/PROGRA~1:/cygdr...
sys_lib_search_path_spec=`cygpath --path --unix "$LIB"`
sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null`
sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
;;
*)
- sys_lib_search_path_spec="$LIB"
+ sys_lib_search_path_spec=$LIB
if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then
# It is most probably a Windows format PATH.
sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
else
sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
fi
# FIXME: find the short name or the path components, as spaces are
# common. (e.g. "Program Files" -> "PROGRA~1")
;;
esac
# DLL is installed to $(libdir)/../bin by postinstall_cmds
- postinstall_cmds='base_file=`basename \${file}`~
- dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
dldir=$destdir/`dirname \$dlpath`~
test -d \$dldir || mkdir -p \$dldir~
$install_prog $dir/$dlname \$dldir/$dlname'
postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
dlpath=$dir/\$dldll~
$RM \$dlpath'
shlibpath_overrides_runpath=yes
dynamic_linker='Win32 link.exe'
;;
*)
# Assume MSVC wrapper
- library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib'
+ library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib'
dynamic_linker='Win32 ld.exe'
;;
esac
# FIXME: first we should search . and the directory the executable is in
shlibpath_var=PATH
;;
darwin* | rhapsody*)
dynamic_linker="$host_os dyld"
version_type=darwin
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext'
- soname_spec='${libname}${release}${major}$shared_ext'
+ library_names_spec='$libname$release$major$shared_ext $libname$shared_ext'
+ soname_spec='$libname$release$major$shared_ext'
shlibpath_overrides_runpath=yes
shlibpath_var=DYLD_LIBRARY_PATH
shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"
sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
;;
dgux*)
version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
;;
freebsd* | dragonfly*)
# DragonFly does not have aout. When/if they implement a new
# versioning mechanism, adjust this.
if test -x /usr/bin/objformat; then
objformat=`/usr/bin/objformat`
else
case $host_os in
freebsd[23].*) objformat=aout ;;
*) objformat=elf ;;
esac
fi
version_type=freebsd-$objformat
case $version_type in
freebsd-elf*)
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext'
need_version=no
need_lib_prefix=no
;;
freebsd-*)
- library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
need_version=yes
;;
esac
shlibpath_var=LD_LIBRARY_PATH
case $host_os in
freebsd2.*)
shlibpath_overrides_runpath=yes
;;
freebsd3.[01]* | freebsdelf3.[01]*)
shlibpath_overrides_runpath=yes
hardcode_into_libs=yes
;;
freebsd3.[2-9]* | freebsdelf3.[2-9]* | \
freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1)
shlibpath_overrides_runpath=no
hardcode_into_libs=yes
;;
*) # from 4.6 on, and DragonFly
shlibpath_overrides_runpath=yes
hardcode_into_libs=yes
;;
esac
;;
-gnu*)
- version_type=linux # correct to gnu/linux during the next big refactor
- need_lib_prefix=no
- need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
- shlibpath_var=LD_LIBRARY_PATH
- shlibpath_overrides_runpath=no
- hardcode_into_libs=yes
- ;;
-
haiku*)
version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
dynamic_linker="$host_os runtime_loader"
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LIBRARY_PATH
- shlibpath_overrides_runpath=yes
+ shlibpath_overrides_runpath=no
sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib'
hardcode_into_libs=yes
;;
hpux9* | hpux10* | hpux11*)
# Give a soname corresponding to the major version so that dld.sl refuses to
# link against other versions.
version_type=sunos
need_lib_prefix=no
need_version=no
case $host_cpu in
ia64*)
shrext_cmds='.so'
hardcode_into_libs=yes
dynamic_linker="$host_os dld.so"
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
- if test "X$HPUX_IA64_MODE" = X32; then
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ if test 32 = "$HPUX_IA64_MODE"; then
sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
else
sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
fi
sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
;;
hppa*64*)
shrext_cmds='.sl'
hardcode_into_libs=yes
dynamic_linker="$host_os dld.sl"
shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
;;
*)
shrext_cmds='.sl'
dynamic_linker="$host_os dld.sl"
shlibpath_var=SHLIB_PATH
shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
;;
esac
# HP-UX runs *really* slowly unless shared libraries are mode 555, ...
postinstall_cmds='chmod 555 $lib'
# or fails outright, so override atomically:
install_override_mode=555
;;
interix[3-9]*)
version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=no
hardcode_into_libs=yes
;;
irix5* | irix6* | nonstopux*)
case $host_os in
nonstopux*) version_type=nonstopux ;;
*)
- if test "$lt_cv_prog_gnu_ld" = yes; then
+ if test yes = "$lt_cv_prog_gnu_ld"; then
version_type=linux # correct to gnu/linux during the next big refactor
else
version_type=irix
fi ;;
esac
need_lib_prefix=no
need_version=no
- soname_spec='${libname}${release}${shared_ext}$major'
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}'
+ soname_spec='$libname$release$shared_ext$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext'
case $host_os in
irix5* | nonstopux*)
libsuff= shlibsuff=
;;
*)
case $LD in # libtool.m4 will add one of these switches to LD
*-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
libsuff= shlibsuff= libmagic=32-bit;;
*-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
libsuff=32 shlibsuff=N32 libmagic=N32;;
*-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
libsuff=64 shlibsuff=64 libmagic=64-bit;;
*) libsuff= shlibsuff= libmagic=never-match;;
esac
;;
esac
shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
shlibpath_overrides_runpath=no
- sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
- sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+ sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff"
+ sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff"
hardcode_into_libs=yes
;;
# No shared lib support for Linux oldld, aout, or coff.
linux*oldld* | linux*aout* | linux*coff*)
dynamic_linker=no
;;
+linux*android*)
+ version_type=none # Android doesn't support versioned libraries.
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext'
+ soname_spec='$libname$release$shared_ext'
+ finish_cmds=
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+
+ # This implies no fast_install, which is unacceptable.
+ # Some rework will be needed to allow for fast_install
+ # before this can be enabled.
+ hardcode_into_libs=yes
+
+ dynamic_linker='Android linker'
+ # Don't embed -rpath directories since the linker doesn't support them.
+ hardcode_libdir_flag_spec='-L$libdir'
+ ;;
+
# This must be glibc/ELF.
-linux* | k*bsd*-gnu | kopensolaris*-gnu)
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=no
# Some binutils ld are patched to set DT_RUNPATH
if ${lt_cv_shlibpath_overrides_runpath+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_shlibpath_overrides_runpath=no
save_LDFLAGS=$LDFLAGS
save_libdir=$libdir
eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \
LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\""
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then :
lt_cv_shlibpath_overrides_runpath=yes
fi
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LDFLAGS=$save_LDFLAGS
libdir=$save_libdir
fi
shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath
# This implies no fast_install, which is unacceptable.
# Some rework will be needed to allow for fast_install
# before this can be enabled.
hardcode_into_libs=yes
# Append ld.so.conf contents to the search path
if test -f /etc/ld.so.conf; then
lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '`
sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
fi
# We used to test for /lib/ld.so.1 and disable shared libraries on
# powerpc, because MkLinux only supported shared libraries with the
# GNU dynamic linker. Since this was broken with cross compilers,
# most powerpc-linux boxes support dynamic linking these days and
# people can always --disable-shared, the test was removed, and we
# assume the GNU/Linux dynamic linker is in use.
dynamic_linker='GNU/Linux ld.so'
;;
netbsd*)
version_type=sunos
need_lib_prefix=no
need_version=no
if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
dynamic_linker='NetBSD (a.out) ld.so'
else
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
dynamic_linker='NetBSD ld.elf_so'
fi
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes
hardcode_into_libs=yes
;;
newsos6)
version_type=linux # correct to gnu/linux during the next big refactor
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes
;;
*nto* | *qnx*)
version_type=qnx
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=no
hardcode_into_libs=yes
dynamic_linker='ldqnx.so'
;;
-openbsd*)
+openbsd* | bitrig*)
version_type=sunos
- sys_lib_dlsearch_path_spec="/usr/lib"
+ sys_lib_dlsearch_path_spec=/usr/lib
need_lib_prefix=no
- # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs.
- case $host_os in
- openbsd3.3 | openbsd3.3.*) need_version=yes ;;
- *) need_version=no ;;
- esac
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
- finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
- shlibpath_var=LD_LIBRARY_PATH
- if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
- case $host_os in
- openbsd2.[89] | openbsd2.[89].*)
- shlibpath_overrides_runpath=no
- ;;
- *)
- shlibpath_overrides_runpath=yes
- ;;
- esac
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
+ need_version=no
else
- shlibpath_overrides_runpath=yes
+ need_version=yes
fi
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
;;
os2*)
libname_spec='$name'
- shrext_cmds=".dll"
+ shrext_cmds=.dll
need_lib_prefix=no
- library_names_spec='$libname${shared_ext} $libname.a'
+ library_names_spec='$libname$shared_ext $libname.a'
dynamic_linker='OS/2 ld.exe'
shlibpath_var=LIBPATH
;;
osf3* | osf4* | osf5*)
version_type=osf
need_lib_prefix=no
need_version=no
- soname_spec='${libname}${release}${shared_ext}$major'
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='$libname$release$shared_ext$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
shlibpath_var=LD_LIBRARY_PATH
sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
- sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
;;
rdos*)
dynamic_linker=no
;;
solaris*)
version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes
hardcode_into_libs=yes
# ldd complains unless libraries are executable
postinstall_cmds='chmod +x $lib'
;;
sunos4*)
version_type=sunos
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes
- if test "$with_gnu_ld" = yes; then
+ if test yes = "$with_gnu_ld"; then
need_lib_prefix=no
fi
need_version=yes
;;
sysv4 | sysv4.3*)
version_type=linux # correct to gnu/linux during the next big refactor
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
case $host_vendor in
sni)
shlibpath_overrides_runpath=no
need_lib_prefix=no
runpath_var=LD_RUN_PATH
;;
siemens)
need_lib_prefix=no
;;
motorola)
need_lib_prefix=no
need_version=no
shlibpath_overrides_runpath=no
sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
;;
esac
;;
sysv4*MP*)
- if test -d /usr/nec ;then
+ if test -d /usr/nec; then
version_type=linux # correct to gnu/linux during the next big refactor
- library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}'
- soname_spec='$libname${shared_ext}.$major'
+ library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext'
+ soname_spec='$libname$shared_ext.$major'
shlibpath_var=LD_LIBRARY_PATH
fi
;;
sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
version_type=freebsd-elf
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes
hardcode_into_libs=yes
- if test "$with_gnu_ld" = yes; then
+ if test yes = "$with_gnu_ld"; then
sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
else
sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
case $host_os in
sco3.2v5*)
sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
;;
esac
fi
sys_lib_dlsearch_path_spec='/usr/lib'
;;
tpf*)
# TPF is a cross-target only. Preferred cross-host = GNU/Linux.
version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=no
hardcode_into_libs=yes
;;
uts4*)
version_type=linux # correct to gnu/linux during the next big refactor
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
;;
*)
dynamic_linker=no
;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5
$as_echo "$dynamic_linker" >&6; }
-test "$dynamic_linker" = no && can_build_shared=no
+test no = "$dynamic_linker" && can_build_shared=no
variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
-if test "$GCC" = yes; then
+if test yes = "$GCC"; then
variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
fi
-if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then
- sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec"
+if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then
+ sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec
fi
-if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then
- sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec"
+if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then
+ sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5
$as_echo_n "checking how to hardcode library paths into programs... " >&6; }
hardcode_action=
if test -n "$hardcode_libdir_flag_spec" ||
test -n "$runpath_var" ||
- test "X$hardcode_automatic" = "Xyes" ; then
+ test yes = "$hardcode_automatic"; then
# We can hardcode non-existent directories.
- if test "$hardcode_direct" != no &&
+ if test no != "$hardcode_direct" &&
# If the only mechanism to avoid hardcoding is shlibpath_var, we
# have to relink, otherwise we might link with an installed library
# when we should be linking with a yet-to-be-installed one
- ## test "$_LT_TAGVAR(hardcode_shlibpath_var, )" != no &&
- test "$hardcode_minus_L" != no; then
+ ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, )" &&
+ test no != "$hardcode_minus_L"; then
# Linking always hardcodes the temporary library directory.
hardcode_action=relink
else
# We can link without hardcoding, and we can hardcode nonexisting dirs.
hardcode_action=immediate
fi
else
# We cannot hardcode anything, or else we can only hardcode existing
# directories.
hardcode_action=unsupported
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5
$as_echo "$hardcode_action" >&6; }
-if test "$hardcode_action" = relink ||
- test "$inherit_rpath" = yes; then
+if test relink = "$hardcode_action" ||
+ test yes = "$inherit_rpath"; then
# Fast installation is not supported
enable_fast_install=no
-elif test "$shlibpath_overrides_runpath" = yes ||
- test "$enable_shared" = no; then
+elif test yes = "$shlibpath_overrides_runpath" ||
+ test no = "$enable_shared"; then
# Fast installation is not necessary
enable_fast_install=needless
fi
- if test "x$enable_dlopen" != xyes; then
+ if test yes != "$enable_dlopen"; then
enable_dlopen=unknown
enable_dlopen_self=unknown
enable_dlopen_self_static=unknown
else
lt_cv_dlopen=no
lt_cv_dlopen_libs=
case $host_os in
beos*)
- lt_cv_dlopen="load_add_on"
+ lt_cv_dlopen=load_add_on
lt_cv_dlopen_libs=
lt_cv_dlopen_self=yes
;;
mingw* | pw32* | cegcc*)
- lt_cv_dlopen="LoadLibrary"
+ lt_cv_dlopen=LoadLibrary
lt_cv_dlopen_libs=
;;
cygwin*)
- lt_cv_dlopen="dlopen"
+ lt_cv_dlopen=dlopen
lt_cv_dlopen_libs=
;;
darwin*)
- # if libdl is installed we need to link against it
+ # if libdl is installed we need to link against it
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
$as_echo_n "checking for dlopen in -ldl... " >&6; }
if ${ac_cv_lib_dl_dlopen+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-ldl $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char dlopen ();
int
main ()
{
return dlopen ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_dl_dlopen=yes
else
ac_cv_lib_dl_dlopen=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
- lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+ lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl
else
- lt_cv_dlopen="dyld"
+ lt_cv_dlopen=dyld
lt_cv_dlopen_libs=
lt_cv_dlopen_self=yes
fi
;;
+ tpf*)
+ # Don't try to run any link tests for TPF. We know it's impossible
+ # because TPF is a cross-compiler, and we know how we open DSOs.
+ lt_cv_dlopen=dlopen
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=no
+ ;;
+
*)
ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load"
if test "x$ac_cv_func_shl_load" = xyes; then :
- lt_cv_dlopen="shl_load"
+ lt_cv_dlopen=shl_load
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5
$as_echo_n "checking for shl_load in -ldld... " >&6; }
if ${ac_cv_lib_dld_shl_load+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-ldld $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char shl_load ();
int
main ()
{
return shl_load ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_dld_shl_load=yes
else
ac_cv_lib_dld_shl_load=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5
$as_echo "$ac_cv_lib_dld_shl_load" >&6; }
if test "x$ac_cv_lib_dld_shl_load" = xyes; then :
- lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"
+ lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld
else
ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen"
if test "x$ac_cv_func_dlopen" = xyes; then :
- lt_cv_dlopen="dlopen"
+ lt_cv_dlopen=dlopen
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
$as_echo_n "checking for dlopen in -ldl... " >&6; }
if ${ac_cv_lib_dl_dlopen+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-ldl $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char dlopen ();
int
main ()
{
return dlopen ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_dl_dlopen=yes
else
ac_cv_lib_dl_dlopen=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
- lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+ lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5
$as_echo_n "checking for dlopen in -lsvld... " >&6; }
if ${ac_cv_lib_svld_dlopen+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lsvld $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char dlopen ();
int
main ()
{
return dlopen ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_svld_dlopen=yes
else
ac_cv_lib_svld_dlopen=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5
$as_echo "$ac_cv_lib_svld_dlopen" >&6; }
if test "x$ac_cv_lib_svld_dlopen" = xyes; then :
- lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"
+ lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5
$as_echo_n "checking for dld_link in -ldld... " >&6; }
if ${ac_cv_lib_dld_dld_link+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-ldld $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char dld_link ();
int
main ()
{
return dld_link ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_dld_dld_link=yes
else
ac_cv_lib_dld_dld_link=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5
$as_echo "$ac_cv_lib_dld_dld_link" >&6; }
if test "x$ac_cv_lib_dld_dld_link" = xyes; then :
- lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"
+ lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld
fi
fi
fi
fi
fi
fi
;;
esac
- if test "x$lt_cv_dlopen" != xno; then
- enable_dlopen=yes
- else
+ if test no = "$lt_cv_dlopen"; then
enable_dlopen=no
+ else
+ enable_dlopen=yes
fi
case $lt_cv_dlopen in
dlopen)
- save_CPPFLAGS="$CPPFLAGS"
- test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+ save_CPPFLAGS=$CPPFLAGS
+ test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
- save_LDFLAGS="$LDFLAGS"
+ save_LDFLAGS=$LDFLAGS
wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
- save_LIBS="$LIBS"
+ save_LIBS=$LIBS
LIBS="$lt_cv_dlopen_libs $LIBS"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5
$as_echo_n "checking whether a program can dlopen itself... " >&6; }
if ${lt_cv_dlopen_self+:} false; then :
$as_echo_n "(cached) " >&6
else
- if test "$cross_compiling" = yes; then :
+ if test yes = "$cross_compiling"; then :
lt_cv_dlopen_self=cross
else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
#line $LINENO "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
#include <dlfcn.h>
#endif
#include <stdio.h>
#ifdef RTLD_GLOBAL
# define LT_DLGLOBAL RTLD_GLOBAL
#else
# ifdef DL_GLOBAL
# define LT_DLGLOBAL DL_GLOBAL
# else
# define LT_DLGLOBAL 0
# endif
#endif
/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
find out it does not work in some platform. */
#ifndef LT_DLLAZY_OR_NOW
# ifdef RTLD_LAZY
# define LT_DLLAZY_OR_NOW RTLD_LAZY
# else
# ifdef DL_LAZY
# define LT_DLLAZY_OR_NOW DL_LAZY
# else
# ifdef RTLD_NOW
# define LT_DLLAZY_OR_NOW RTLD_NOW
# else
# ifdef DL_NOW
# define LT_DLLAZY_OR_NOW DL_NOW
# else
# define LT_DLLAZY_OR_NOW 0
# endif
# endif
# endif
# endif
#endif
/* When -fvisbility=hidden is used, assume the code has been annotated
correspondingly for the symbols needed. */
-#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
+#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
int fnord () __attribute__((visibility("default")));
#endif
int fnord () { return 42; }
int main ()
{
void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
int status = $lt_dlunknown;
if (self)
{
if (dlsym (self,"fnord")) status = $lt_dlno_uscore;
else
{
if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
else puts (dlerror ());
}
/* dlclose (self); */
}
else
puts (dlerror ());
return status;
}
_LT_EOF
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
(eval $ac_link) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then
+ test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then
(./conftest; exit; ) >&5 2>/dev/null
lt_status=$?
case x$lt_status in
x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;;
x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;;
x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;;
esac
else :
# compilation failed
lt_cv_dlopen_self=no
fi
fi
rm -fr conftest*
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5
$as_echo "$lt_cv_dlopen_self" >&6; }
- if test "x$lt_cv_dlopen_self" = xyes; then
+ if test yes = "$lt_cv_dlopen_self"; then
wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5
$as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; }
if ${lt_cv_dlopen_self_static+:} false; then :
$as_echo_n "(cached) " >&6
else
- if test "$cross_compiling" = yes; then :
+ if test yes = "$cross_compiling"; then :
lt_cv_dlopen_self_static=cross
else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
#line $LINENO "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
#include <dlfcn.h>
#endif
#include <stdio.h>
#ifdef RTLD_GLOBAL
# define LT_DLGLOBAL RTLD_GLOBAL
#else
# ifdef DL_GLOBAL
# define LT_DLGLOBAL DL_GLOBAL
# else
# define LT_DLGLOBAL 0
# endif
#endif
/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
find out it does not work in some platform. */
#ifndef LT_DLLAZY_OR_NOW
# ifdef RTLD_LAZY
# define LT_DLLAZY_OR_NOW RTLD_LAZY
# else
# ifdef DL_LAZY
# define LT_DLLAZY_OR_NOW DL_LAZY
# else
# ifdef RTLD_NOW
# define LT_DLLAZY_OR_NOW RTLD_NOW
# else
# ifdef DL_NOW
# define LT_DLLAZY_OR_NOW DL_NOW
# else
# define LT_DLLAZY_OR_NOW 0
# endif
# endif
# endif
# endif
#endif
/* When -fvisbility=hidden is used, assume the code has been annotated
correspondingly for the symbols needed. */
-#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
+#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
int fnord () __attribute__((visibility("default")));
#endif
int fnord () { return 42; }
int main ()
{
void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
int status = $lt_dlunknown;
if (self)
{
if (dlsym (self,"fnord")) status = $lt_dlno_uscore;
else
{
if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
else puts (dlerror ());
}
/* dlclose (self); */
}
else
puts (dlerror ());
return status;
}
_LT_EOF
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
(eval $ac_link) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then
+ test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then
(./conftest; exit; ) >&5 2>/dev/null
lt_status=$?
case x$lt_status in
x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;;
x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;;
x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;;
esac
else :
# compilation failed
lt_cv_dlopen_self_static=no
fi
fi
rm -fr conftest*
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5
$as_echo "$lt_cv_dlopen_self_static" >&6; }
fi
- CPPFLAGS="$save_CPPFLAGS"
- LDFLAGS="$save_LDFLAGS"
- LIBS="$save_LIBS"
+ CPPFLAGS=$save_CPPFLAGS
+ LDFLAGS=$save_LDFLAGS
+ LIBS=$save_LIBS
;;
esac
case $lt_cv_dlopen_self in
yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
*) enable_dlopen_self=unknown ;;
esac
case $lt_cv_dlopen_self_static in
yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
*) enable_dlopen_self_static=unknown ;;
esac
fi
striplib=
old_striplib=
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5
$as_echo_n "checking whether stripping libraries is possible... " >&6; }
if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then
test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
test -z "$striplib" && striplib="$STRIP --strip-unneeded"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
# FIXME - insert some real tests, host_os isn't really good enough
case $host_os in
darwin*)
- if test -n "$STRIP" ; then
+ if test -n "$STRIP"; then
striplib="$STRIP -x"
old_striplib="$STRIP -S"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
;;
*)
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
;;
esac
fi
- # Report which library types will actually be built
+ # Report what library types will actually be built
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5
$as_echo_n "checking if libtool supports shared libraries... " >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5
$as_echo "$can_build_shared" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5
$as_echo_n "checking whether to build shared libraries... " >&6; }
- test "$can_build_shared" = "no" && enable_shared=no
+ test no = "$can_build_shared" && enable_shared=no
# On AIX, shared libraries and static libraries use the same namespace, and
# are all built from PIC.
case $host_os in
aix3*)
- test "$enable_shared" = yes && enable_static=no
+ test yes = "$enable_shared" && enable_static=no
if test -n "$RANLIB"; then
archive_cmds="$archive_cmds~\$RANLIB \$lib"
postinstall_cmds='$RANLIB $lib'
fi
;;
aix[4-9]*)
- if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
- test "$enable_shared" = yes && enable_static=no
+ if test ia64 != "$host_cpu" && test no = "$aix_use_runtimelinking"; then
+ test yes = "$enable_shared" && enable_static=no
fi
;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5
$as_echo "$enable_shared" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5
$as_echo_n "checking whether to build static libraries... " >&6; }
# Make sure either enable_shared or enable_static is yes.
- test "$enable_shared" = yes || enable_static=yes
+ test yes = "$enable_shared" || enable_static=yes
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5
$as_echo "$enable_static" >&6; }
fi
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
-CC="$lt_save_CC"
+CC=$lt_save_CC
- if test -n "$CXX" && ( test "X$CXX" != "Xno" &&
- ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) ||
- (test "X$CXX" != "Xg++"))) ; then
+ if test -n "$CXX" && ( test no != "$CXX" &&
+ ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) ||
+ (test g++ != "$CXX"))); then
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5
$as_echo_n "checking how to run the C++ preprocessor... " >&6; }
if test -z "$CXXCPP"; then
if ${ac_cv_prog_CXXCPP+:} false; then :
$as_echo_n "(cached) " >&6
else
# Double quotes because CXXCPP needs to be expanded
for CXXCPP in "$CXX -E" "/lib/cpp"
do
ac_preproc_ok=false
for ac_cxx_preproc_warn_flag in '' yes
do
# Use a header file that comes with gcc, so configuring glibc
# with a fresh cross-compiler works.
# Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
# <limits.h> exists even on freestanding compilers.
# On the NeXT, cc -E runs the code through the compiler's parser,
# not just through cpp. "Syntax error" is here to catch this case.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#ifdef __STDC__
# include <limits.h>
#else
# include <assert.h>
#endif
Syntax error
_ACEOF
if ac_fn_cxx_try_cpp "$LINENO"; then :
else
# Broken: fails on valid input.
continue
fi
rm -f conftest.err conftest.i conftest.$ac_ext
# OK, works on sane cases. Now check whether nonexistent headers
# can be detected and how.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <ac_nonexistent.h>
_ACEOF
if ac_fn_cxx_try_cpp "$LINENO"; then :
# Broken: success on invalid input.
continue
else
# Passes both tests.
ac_preproc_ok=:
break
fi
rm -f conftest.err conftest.i conftest.$ac_ext
done
# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
rm -f conftest.i conftest.err conftest.$ac_ext
if $ac_preproc_ok; then :
break
fi
done
ac_cv_prog_CXXCPP=$CXXCPP
fi
CXXCPP=$ac_cv_prog_CXXCPP
else
ac_cv_prog_CXXCPP=$CXXCPP
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5
$as_echo "$CXXCPP" >&6; }
ac_preproc_ok=false
for ac_cxx_preproc_warn_flag in '' yes
do
# Use a header file that comes with gcc, so configuring glibc
# with a fresh cross-compiler works.
# Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
# <limits.h> exists even on freestanding compilers.
# On the NeXT, cc -E runs the code through the compiler's parser,
# not just through cpp. "Syntax error" is here to catch this case.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#ifdef __STDC__
# include <limits.h>
#else
# include <assert.h>
#endif
Syntax error
_ACEOF
if ac_fn_cxx_try_cpp "$LINENO"; then :
else
# Broken: fails on valid input.
continue
fi
rm -f conftest.err conftest.i conftest.$ac_ext
# OK, works on sane cases. Now check whether nonexistent headers
# can be detected and how.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <ac_nonexistent.h>
_ACEOF
if ac_fn_cxx_try_cpp "$LINENO"; then :
# Broken: success on invalid input.
continue
else
# Passes both tests.
ac_preproc_ok=:
break
fi
rm -f conftest.err conftest.i conftest.$ac_ext
done
# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
rm -f conftest.i conftest.err conftest.$ac_ext
if $ac_preproc_ok; then :
else
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check
See \`config.log' for more details" "$LINENO" 5; }
fi
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
else
_lt_caught_CXX_error=yes
fi
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
archive_cmds_need_lc_CXX=no
allow_undefined_flag_CXX=
always_export_symbols_CXX=no
archive_expsym_cmds_CXX=
compiler_needs_object_CXX=no
export_dynamic_flag_spec_CXX=
hardcode_direct_CXX=no
hardcode_direct_absolute_CXX=no
hardcode_libdir_flag_spec_CXX=
hardcode_libdir_separator_CXX=
hardcode_minus_L_CXX=no
hardcode_shlibpath_var_CXX=unsupported
hardcode_automatic_CXX=no
inherit_rpath_CXX=no
module_cmds_CXX=
module_expsym_cmds_CXX=
link_all_deplibs_CXX=unknown
old_archive_cmds_CXX=$old_archive_cmds
reload_flag_CXX=$reload_flag
reload_cmds_CXX=$reload_cmds
no_undefined_flag_CXX=
whole_archive_flag_spec_CXX=
enable_shared_with_static_runtimes_CXX=no
# Source file extension for C++ test sources.
ac_ext=cpp
# Object file extension for compiled C++ test sources.
objext=o
objext_CXX=$objext
# No sense in running all these tests if we already determined that
# the CXX compiler isn't working. Some variables (like enable_shared)
# are currently assumed to apply to all compilers on this platform,
# and will be corrupted by setting them based on a non-working compiler.
-if test "$_lt_caught_CXX_error" != yes; then
+if test yes != "$_lt_caught_CXX_error"; then
# Code to be used in simple compile tests
lt_simple_compile_test_code="int some_variable = 0;"
# Code to be used in simple link tests
lt_simple_link_test_code='int main(int, char *[]) { return(0); }'
# ltmain only uses $CC for tagged configurations so make sure $CC is set.
# If no C compiler was specified, use CC.
LTCC=${LTCC-"$CC"}
# If no C compiler flags were specified, use CFLAGS.
LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
# Allow CC to be a program name with arguments.
compiler=$CC
# save warnings/boilerplate of simple test code
ac_outfile=conftest.$ac_objext
echo "$lt_simple_compile_test_code" >conftest.$ac_ext
eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
_lt_compiler_boilerplate=`cat conftest.err`
$RM conftest*
ac_outfile=conftest.$ac_objext
echo "$lt_simple_link_test_code" >conftest.$ac_ext
eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
_lt_linker_boilerplate=`cat conftest.err`
$RM -r conftest*
# Allow CC to be a program name with arguments.
lt_save_CC=$CC
lt_save_CFLAGS=$CFLAGS
lt_save_LD=$LD
lt_save_GCC=$GCC
GCC=$GXX
lt_save_with_gnu_ld=$with_gnu_ld
lt_save_path_LD=$lt_cv_path_LD
if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then
lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx
else
$as_unset lt_cv_prog_gnu_ld
fi
if test -n "${lt_cv_path_LDCXX+set}"; then
lt_cv_path_LD=$lt_cv_path_LDCXX
else
$as_unset lt_cv_path_LD
fi
test -z "${LDCXX+set}" || LD=$LDCXX
CC=${CXX-"c++"}
CFLAGS=$CXXFLAGS
compiler=$CC
compiler_CXX=$CC
for cc_temp in $compiler""; do
case $cc_temp in
compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
\-*) ;;
*) break;;
esac
done
cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"`
if test -n "$compiler"; then
# We don't want -fno-exception when compiling C++ code, so set the
# no_builtin_flag separately
- if test "$GXX" = yes; then
+ if test yes = "$GXX"; then
lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin'
else
lt_prog_compiler_no_builtin_flag_CXX=
fi
- if test "$GXX" = yes; then
+ if test yes = "$GXX"; then
# Set up default GNU C++ configuration
# Check whether --with-gnu-ld was given.
if test "${with_gnu_ld+set}" = set; then :
- withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes
+ withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes
else
with_gnu_ld=no
fi
ac_prog=ld
-if test "$GCC" = yes; then
+if test yes = "$GCC"; then
# Check if gcc -print-prog-name=ld gives a path.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5
$as_echo_n "checking for ld used by $CC... " >&6; }
case $host in
*-*-mingw*)
- # gcc leaves a trailing carriage return which upsets mingw
+ # gcc leaves a trailing carriage return, which upsets mingw
ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
*)
ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
esac
case $ac_prog in
# Accept absolute paths.
[\\/]* | ?:[\\/]*)
re_direlt='/[^/][^/]*/\.\./'
# Canonicalize the pathname of ld
ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
done
- test -z "$LD" && LD="$ac_prog"
+ test -z "$LD" && LD=$ac_prog
;;
"")
# If it fails, then pretend we aren't using GCC.
ac_prog=ld
;;
*)
# If it is relative, then search for the first ld in PATH.
with_gnu_ld=unknown
;;
esac
-elif test "$with_gnu_ld" = yes; then
+elif test yes = "$with_gnu_ld"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5
$as_echo_n "checking for GNU ld... " >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5
$as_echo_n "checking for non-GNU ld... " >&6; }
fi
if ${lt_cv_path_LD+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -z "$LD"; then
- lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
for ac_dir in $PATH; do
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
test -z "$ac_dir" && ac_dir=.
if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
- lt_cv_path_LD="$ac_dir/$ac_prog"
+ lt_cv_path_LD=$ac_dir/$ac_prog
# Check to see if the program is GNU ld. I'd rather use --version,
# but apparently some variants of GNU ld only accept -v.
# Break only if it was the GNU/non-GNU ld that we prefer.
case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
*GNU* | *'with BFD'*)
- test "$with_gnu_ld" != no && break
+ test no != "$with_gnu_ld" && break
;;
*)
- test "$with_gnu_ld" != yes && break
+ test yes != "$with_gnu_ld" && break
;;
esac
fi
done
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
else
- lt_cv_path_LD="$LD" # Let the user override the test with a path.
+ lt_cv_path_LD=$LD # Let the user override the test with a path.
fi
fi
-LD="$lt_cv_path_LD"
+LD=$lt_cv_path_LD
if test -n "$LD"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5
$as_echo "$LD" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5
$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; }
if ${lt_cv_prog_gnu_ld+:} false; then :
$as_echo_n "(cached) " >&6
else
# I'd rather use --version here, but apparently some GNU lds only accept -v.
case `$LD -v 2>&1 </dev/null` in
*GNU* | *'with BFD'*)
lt_cv_prog_gnu_ld=yes
;;
*)
lt_cv_prog_gnu_ld=no
;;
esac
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_gnu_ld" >&5
$as_echo "$lt_cv_prog_gnu_ld" >&6; }
with_gnu_ld=$lt_cv_prog_gnu_ld
# Check if GNU C++ uses GNU ld as the underlying linker, since the
# archiving commands below assume that GNU ld is being used.
- if test "$with_gnu_ld" = yes; then
- archive_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
- archive_expsym_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ if test yes = "$with_gnu_ld"; then
+ archive_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
- hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir'
- export_dynamic_flag_spec_CXX='${wl}--export-dynamic'
+ hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir'
+ export_dynamic_flag_spec_CXX='$wl--export-dynamic'
# If archive_cmds runs LD, not CC, wlarc should be empty
# XXX I think wlarc can be eliminated in ltcf-cxx, but I need to
# investigate it a little bit more. (MM)
- wlarc='${wl}'
+ wlarc='$wl'
# ancient GNU ld didn't support --whole-archive et. al.
if eval "`$CC -print-prog-name=ld` --help 2>&1" |
$GREP 'no-whole-archive' > /dev/null; then
- whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+ whole_archive_flag_spec_CXX=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive'
else
whole_archive_flag_spec_CXX=
fi
else
with_gnu_ld=no
wlarc=
# A generic and very simple default shared library creation
# command for GNU C++ for the case where it uses the native
# linker, instead of GNU ld. If possible, this setting should
# overridden to take advantage of the native linker features on
# the platform it is being used on.
archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
fi
# Commands to make compiler produce verbose output that lists
# what "hidden" libraries, object files and flags are used when
# linking a shared library.
output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
else
GXX=no
with_gnu_ld=no
wlarc=
fi
# PORTME: fill in a description of your system's C++ link characteristics
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5
$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; }
ld_shlibs_CXX=yes
case $host_os in
aix3*)
# FIXME: insert proper C++ library support
ld_shlibs_CXX=no
;;
aix[4-9]*)
- if test "$host_cpu" = ia64; then
+ if test ia64 = "$host_cpu"; then
# On IA64, the linker does run time linking by default, so we don't
# have to do anything special.
aix_use_runtimelinking=no
exp_sym_flag='-Bexport'
- no_entry_flag=""
+ no_entry_flag=
else
aix_use_runtimelinking=no
# Test if we are trying to use run time linking or normal
# AIX style linking. If -brtl is somewhere in LDFLAGS, we
# need to do runtime linking.
case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*)
for ld_flag in $LDFLAGS; do
case $ld_flag in
*-brtl*)
aix_use_runtimelinking=yes
break
;;
esac
done
;;
esac
exp_sym_flag='-bexport'
no_entry_flag='-bnoentry'
fi
# When large executables or shared objects are built, AIX ld can
# have problems creating the table of contents. If linking a library
# or program results in "error TOC overflow" add -mminimal-toc to
# CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not
# enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
archive_cmds_CXX=''
hardcode_direct_CXX=yes
hardcode_direct_absolute_CXX=yes
hardcode_libdir_separator_CXX=':'
link_all_deplibs_CXX=yes
- file_list_spec_CXX='${wl}-f,'
+ file_list_spec_CXX='$wl-f,'
- if test "$GXX" = yes; then
+ if test yes = "$GXX"; then
case $host_os in aix4.[012]|aix4.[012].*)
# We only want to do this on AIX 4.2 and lower, the check
# below for broken collect2 doesn't work under 4.3+
- collect2name=`${CC} -print-prog-name=collect2`
+ collect2name=`$CC -print-prog-name=collect2`
if test -f "$collect2name" &&
strings "$collect2name" | $GREP resolve_lib_name >/dev/null
then
# We have reworked collect2
:
else
# We have old collect2
hardcode_direct_CXX=unsupported
# It fails to find uninstalled libraries when the uninstalled
# path is not listed in the libpath. Setting hardcode_minus_L
# to unsupported forces relinking
hardcode_minus_L_CXX=yes
hardcode_libdir_flag_spec_CXX='-L$libdir'
hardcode_libdir_separator_CXX=
fi
esac
shared_flag='-shared'
- if test "$aix_use_runtimelinking" = yes; then
- shared_flag="$shared_flag "'${wl}-G'
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag=$shared_flag' $wl-G'
fi
else
# not using gcc
- if test "$host_cpu" = ia64; then
+ if test ia64 = "$host_cpu"; then
# VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
# chokes on -Wl,-G. The following line is correct:
shared_flag='-G'
else
- if test "$aix_use_runtimelinking" = yes; then
- shared_flag='${wl}-G'
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag='$wl-G'
else
- shared_flag='${wl}-bM:SRE'
+ shared_flag='$wl-bM:SRE'
fi
fi
fi
- export_dynamic_flag_spec_CXX='${wl}-bexpall'
+ export_dynamic_flag_spec_CXX='$wl-bexpall'
# It seems that -bexpall does not export symbols beginning with
# underscore (_), so it is better to generate a list of symbols to
# export.
always_export_symbols_CXX=yes
- if test "$aix_use_runtimelinking" = yes; then
+ if test yes = "$aix_use_runtimelinking"; then
# Warning - without using the other runtime loading flags (-brtl),
# -berok will link without error, but may produce a broken library.
allow_undefined_flag_CXX='-berok'
# Determine the default libpath from the value encoded in an empty
# executable.
- if test "${lt_cv_aix_libpath+set}" = set; then
+ if test set = "${lt_cv_aix_libpath+set}"; then
aix_libpath=$lt_cv_aix_libpath
else
if ${lt_cv_aix_libpath__CXX+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_cxx_try_link "$LINENO"; then :
lt_aix_libpath_sed='
/Import File Strings/,/^$/ {
/^0/ {
s/^0 *\([^ ]*\) *$/\1/
p
}
}'
lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
# Check for a 64-bit object if we didn't find anything.
if test -z "$lt_cv_aix_libpath__CXX"; then
lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
fi
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
if test -z "$lt_cv_aix_libpath__CXX"; then
- lt_cv_aix_libpath__CXX="/usr/lib:/lib"
+ lt_cv_aix_libpath__CXX=/usr/lib:/lib
fi
fi
aix_libpath=$lt_cv_aix_libpath__CXX
fi
- hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath"
+ hardcode_libdir_flag_spec_CXX='$wl-blibpath:$libdir:'"$aix_libpath"
- archive_expsym_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+ archive_expsym_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag
else
- if test "$host_cpu" = ia64; then
- hardcode_libdir_flag_spec_CXX='${wl}-R $libdir:/usr/lib:/lib'
+ if test ia64 = "$host_cpu"; then
+ hardcode_libdir_flag_spec_CXX='$wl-R $libdir:/usr/lib:/lib'
allow_undefined_flag_CXX="-z nodefs"
- archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+ archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols"
else
# Determine the default libpath from the value encoded in an
# empty executable.
- if test "${lt_cv_aix_libpath+set}" = set; then
+ if test set = "${lt_cv_aix_libpath+set}"; then
aix_libpath=$lt_cv_aix_libpath
else
if ${lt_cv_aix_libpath__CXX+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_cxx_try_link "$LINENO"; then :
lt_aix_libpath_sed='
/Import File Strings/,/^$/ {
/^0/ {
s/^0 *\([^ ]*\) *$/\1/
p
}
}'
lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
# Check for a 64-bit object if we didn't find anything.
if test -z "$lt_cv_aix_libpath__CXX"; then
lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
fi
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
if test -z "$lt_cv_aix_libpath__CXX"; then
- lt_cv_aix_libpath__CXX="/usr/lib:/lib"
+ lt_cv_aix_libpath__CXX=/usr/lib:/lib
fi
fi
aix_libpath=$lt_cv_aix_libpath__CXX
fi
- hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath"
+ hardcode_libdir_flag_spec_CXX='$wl-blibpath:$libdir:'"$aix_libpath"
# Warning - without using the other run time loading flags,
# -berok will link without error, but may produce a broken library.
- no_undefined_flag_CXX=' ${wl}-bernotok'
- allow_undefined_flag_CXX=' ${wl}-berok'
- if test "$with_gnu_ld" = yes; then
+ no_undefined_flag_CXX=' $wl-bernotok'
+ allow_undefined_flag_CXX=' $wl-berok'
+ if test yes = "$with_gnu_ld"; then
# We only use this code for GNU lds that support --whole-archive.
- whole_archive_flag_spec_CXX='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
+ whole_archive_flag_spec_CXX='$wl--whole-archive$convenience $wl--no-whole-archive'
else
# Exported symbols can be pulled into shared objects from archives
whole_archive_flag_spec_CXX='$convenience'
fi
archive_cmds_need_lc_CXX=yes
# This is similar to how AIX traditionally builds its shared
# libraries.
- archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+ archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $wl-bnoentry $compiler_flags $wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
fi
fi
;;
beos*)
if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
allow_undefined_flag_CXX=unsupported
# Joseph Beckenbach <jrb3@best.com> says some releases of gcc
# support --undefined. This deserves some investigation. FIXME
- archive_cmds_CXX='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_cmds_CXX='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
else
ld_shlibs_CXX=no
fi
;;
chorus*)
case $cc_basename in
*)
# FIXME: insert proper C++ library support
ld_shlibs_CXX=no
;;
esac
;;
cygwin* | mingw* | pw32* | cegcc*)
case $GXX,$cc_basename in
,cl* | no,cl*)
# Native MSVC
# hardcode_libdir_flag_spec is actually meaningless, as there is
# no search path for DLLs.
hardcode_libdir_flag_spec_CXX=' '
allow_undefined_flag_CXX=unsupported
always_export_symbols_CXX=yes
file_list_spec_CXX='@'
# Tell ltmain to make .lib files, not .a files.
libext=lib
# Tell ltmain to make .dll files, not .so files.
- shrext_cmds=".dll"
+ shrext_cmds=.dll
# FIXME: Setting linknames here is a bad hack.
- archive_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames='
- archive_expsym_cmds_CXX='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
- $SED -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp;
- else
- $SED -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp;
- fi~
- $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
- linknames='
+ archive_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames='
+ archive_expsym_cmds_CXX='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then
+ cp "$export_symbols" "$output_objdir/$soname.def";
+ echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp";
+ else
+ $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp;
+ fi~
+ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
+ linknames='
# The linker will not automatically build a static lib if we build a DLL.
# _LT_TAGVAR(old_archive_from_new_cmds, CXX)='true'
enable_shared_with_static_runtimes_CXX=yes
# Don't use ranlib
old_postinstall_cmds_CXX='chmod 644 $oldlib'
postlink_cmds_CXX='lt_outputfile="@OUTPUT@"~
- lt_tool_outputfile="@TOOL_OUTPUT@"~
- case $lt_outputfile in
- *.exe|*.EXE) ;;
- *)
- lt_outputfile="$lt_outputfile.exe"
- lt_tool_outputfile="$lt_tool_outputfile.exe"
- ;;
- esac~
- func_to_tool_file "$lt_outputfile"~
- if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then
- $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
- $RM "$lt_outputfile.manifest";
- fi'
+ lt_tool_outputfile="@TOOL_OUTPUT@"~
+ case $lt_outputfile in
+ *.exe|*.EXE) ;;
+ *)
+ lt_outputfile=$lt_outputfile.exe
+ lt_tool_outputfile=$lt_tool_outputfile.exe
+ ;;
+ esac~
+ func_to_tool_file "$lt_outputfile"~
+ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then
+ $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
+ $RM "$lt_outputfile.manifest";
+ fi'
;;
*)
# g++
# _LT_TAGVAR(hardcode_libdir_flag_spec, CXX) is actually meaningless,
# as there is no search path for DLLs.
hardcode_libdir_flag_spec_CXX='-L$libdir'
- export_dynamic_flag_spec_CXX='${wl}--export-all-symbols'
+ export_dynamic_flag_spec_CXX='$wl--export-all-symbols'
allow_undefined_flag_CXX=unsupported
always_export_symbols_CXX=no
enable_shared_with_static_runtimes_CXX=yes
if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
- archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
- # If the export-symbols file already is a .def file (1st line
- # is EXPORTS), use it as is; otherwise, prepend...
- archive_expsym_cmds_CXX='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
- cp $export_symbols $output_objdir/$soname.def;
- else
- echo EXPORTS > $output_objdir/$soname.def;
- cat $export_symbols >> $output_objdir/$soname.def;
- fi~
- $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ # If the export-symbols file already is a .def file, use it as
+ # is; otherwise, prepend EXPORTS...
+ archive_expsym_cmds_CXX='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then
+ cp $export_symbols $output_objdir/$soname.def;
+ else
+ echo EXPORTS > $output_objdir/$soname.def;
+ cat $export_symbols >> $output_objdir/$soname.def;
+ fi~
+ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
else
ld_shlibs_CXX=no
fi
;;
esac
;;
darwin* | rhapsody*)
archive_cmds_need_lc_CXX=no
hardcode_direct_CXX=no
hardcode_automatic_CXX=yes
hardcode_shlibpath_var_CXX=unsupported
- if test "$lt_cv_ld_force_load" = "yes"; then
- whole_archive_flag_spec_CXX='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`'
+ if test yes = "$lt_cv_ld_force_load"; then
+ whole_archive_flag_spec_CXX='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`'
else
whole_archive_flag_spec_CXX=''
fi
link_all_deplibs_CXX=yes
- allow_undefined_flag_CXX="$_lt_dar_allow_undefined"
+ allow_undefined_flag_CXX=$_lt_dar_allow_undefined
case $cc_basename in
- ifort*) _lt_dar_can_shared=yes ;;
+ ifort*|nagfor*) _lt_dar_can_shared=yes ;;
*) _lt_dar_can_shared=$GCC ;;
esac
- if test "$_lt_dar_can_shared" = "yes"; then
+ if test yes = "$_lt_dar_can_shared"; then
output_verbose_link_cmd=func_echo_all
- archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}"
- module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}"
- archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}"
- module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}"
- if test "$lt_cv_apple_cc_single_mod" != "yes"; then
- archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}"
- archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}"
+ archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil"
+ module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil"
+ archive_expsym_cmds_CXX="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil"
+ module_expsym_cmds_CXX="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil"
+ if test yes != "$lt_cv_apple_cc_single_mod"; then
+ archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil"
+ archive_expsym_cmds_CXX="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil"
fi
else
ld_shlibs_CXX=no
fi
;;
dgux*)
case $cc_basename in
ec++*)
# FIXME: insert proper C++ library support
ld_shlibs_CXX=no
;;
ghcx*)
# Green Hills C++ Compiler
# FIXME: insert proper C++ library support
ld_shlibs_CXX=no
;;
*)
# FIXME: insert proper C++ library support
ld_shlibs_CXX=no
;;
esac
;;
freebsd2.*)
# C++ shared libraries reported to be fairly broken before
# switch to ELF
ld_shlibs_CXX=no
;;
freebsd-elf*)
archive_cmds_need_lc_CXX=no
;;
freebsd* | dragonfly*)
# FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF
# conventions
ld_shlibs_CXX=yes
;;
- gnu*)
- ;;
-
haiku*)
- archive_cmds_CXX='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_cmds_CXX='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
link_all_deplibs_CXX=yes
;;
hpux9*)
- hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir'
+ hardcode_libdir_flag_spec_CXX='$wl+b $wl$libdir'
hardcode_libdir_separator_CXX=:
- export_dynamic_flag_spec_CXX='${wl}-E'
+ export_dynamic_flag_spec_CXX='$wl-E'
hardcode_direct_CXX=yes
hardcode_minus_L_CXX=yes # Not in the search PATH,
# but as the default
# location of the library.
case $cc_basename in
CC*)
# FIXME: insert proper C++ library support
ld_shlibs_CXX=no
;;
aCC*)
- archive_cmds_CXX='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ archive_cmds_CXX='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
# Commands to make compiler produce verbose output that lists
# what "hidden" libraries, object files and flags are used when
# linking a shared library.
#
# There doesn't appear to be a way to prevent this compiler from
# explicitly linking system object files so we need to strip them
# from the output so that they don't get included in the library
# dependencies.
- output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+ output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
;;
*)
- if test "$GXX" = yes; then
- archive_cmds_CXX='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ if test yes = "$GXX"; then
+ archive_cmds_CXX='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
else
# FIXME: insert proper C++ library support
ld_shlibs_CXX=no
fi
;;
esac
;;
hpux10*|hpux11*)
- if test $with_gnu_ld = no; then
- hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir'
+ if test no = "$with_gnu_ld"; then
+ hardcode_libdir_flag_spec_CXX='$wl+b $wl$libdir'
hardcode_libdir_separator_CXX=:
case $host_cpu in
hppa*64*|ia64*)
;;
*)
- export_dynamic_flag_spec_CXX='${wl}-E'
+ export_dynamic_flag_spec_CXX='$wl-E'
;;
esac
fi
case $host_cpu in
hppa*64*|ia64*)
hardcode_direct_CXX=no
hardcode_shlibpath_var_CXX=no
;;
*)
hardcode_direct_CXX=yes
hardcode_direct_absolute_CXX=yes
hardcode_minus_L_CXX=yes # Not in the search PATH,
# but as the default
# location of the library.
;;
esac
case $cc_basename in
CC*)
# FIXME: insert proper C++ library support
ld_shlibs_CXX=no
;;
aCC*)
case $host_cpu in
hppa*64*)
- archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ archive_cmds_CXX='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
;;
ia64*)
- archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ archive_cmds_CXX='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
;;
*)
- archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ archive_cmds_CXX='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
;;
esac
# Commands to make compiler produce verbose output that lists
# what "hidden" libraries, object files and flags are used when
# linking a shared library.
#
# There doesn't appear to be a way to prevent this compiler from
# explicitly linking system object files so we need to strip them
# from the output so that they don't get included in the library
# dependencies.
- output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+ output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
;;
*)
- if test "$GXX" = yes; then
- if test $with_gnu_ld = no; then
+ if test yes = "$GXX"; then
+ if test no = "$with_gnu_ld"; then
case $host_cpu in
hppa*64*)
- archive_cmds_CXX='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ archive_cmds_CXX='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
;;
ia64*)
- archive_cmds_CXX='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ archive_cmds_CXX='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
;;
*)
- archive_cmds_CXX='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ archive_cmds_CXX='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
;;
esac
fi
else
# FIXME: insert proper C++ library support
ld_shlibs_CXX=no
fi
;;
esac
;;
interix[3-9]*)
hardcode_direct_CXX=no
hardcode_shlibpath_var_CXX=no
- hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir'
- export_dynamic_flag_spec_CXX='${wl}-E'
+ hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir'
+ export_dynamic_flag_spec_CXX='$wl-E'
# Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
# Instead, shared libraries are loaded at an image base (0x10000000 by
# default) and relocated if they conflict, which is a slow very memory
# consuming and fragmenting process. To avoid this, we pick a random,
# 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
# time. Moving up from 0x10000000 also allows more sbrk(2) space.
- archive_cmds_CXX='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
- archive_expsym_cmds_CXX='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ archive_cmds_CXX='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ archive_expsym_cmds_CXX='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
;;
irix5* | irix6*)
case $cc_basename in
CC*)
# SGI C++
- archive_cmds_CXX='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+ archive_cmds_CXX='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
# Archives containing C++ object files must be created using
# "CC -ar", where "CC" is the IRIX C++ compiler. This is
# necessary to make sure instantiated templates are included
# in the archive.
old_archive_cmds_CXX='$CC -ar -WR,-u -o $oldlib $oldobjs'
;;
*)
- if test "$GXX" = yes; then
- if test "$with_gnu_ld" = no; then
- archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ if test yes = "$GXX"; then
+ if test no = "$with_gnu_ld"; then
+ archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
else
- archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` -o $lib'
+ archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` -o $lib'
fi
fi
link_all_deplibs_CXX=yes
;;
esac
- hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir'
hardcode_libdir_separator_CXX=:
inherit_rpath_CXX=yes
;;
- linux* | k*bsd*-gnu | kopensolaris*-gnu)
+ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
case $cc_basename in
KCC*)
# Kuck and Associates, Inc. (KAI) C++ Compiler
# KCC will only create a shared library if the output file
# ends with ".so" (or ".sl" for HP-UX), so rename the library
# to its proper name (with version) after linking.
- archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
- archive_expsym_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib'
+ archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+ archive_expsym_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib $wl-retain-symbols-file,$export_symbols; mv \$templib $lib'
# Commands to make compiler produce verbose output that lists
# what "hidden" libraries, object files and flags are used when
# linking a shared library.
#
# There doesn't appear to be a way to prevent this compiler from
# explicitly linking system object files so we need to strip them
# from the output so that they don't get included in the library
# dependencies.
- output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+ output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
- hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir'
- export_dynamic_flag_spec_CXX='${wl}--export-dynamic'
+ hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir'
+ export_dynamic_flag_spec_CXX='$wl--export-dynamic'
# Archives containing C++ object files must be created using
# "CC -Bstatic", where "CC" is the KAI C++ compiler.
old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs'
;;
icpc* | ecpc* )
# Intel C++
with_gnu_ld=yes
# version 8.0 and above of icpc choke on multiply defined symbols
# if we add $predep_objects and $postdep_objects, however 7.1 and
# earlier do not add the objects themselves.
case `$CC -V 2>&1` in
*"Version 7."*)
- archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
- archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
;;
*) # Version 8.0 or newer
tmp_idyn=
case $host_cpu in
ia64*) tmp_idyn=' -i_dynamic';;
esac
- archive_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
- archive_expsym_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ archive_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
;;
esac
archive_cmds_need_lc_CXX=no
- hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir'
- export_dynamic_flag_spec_CXX='${wl}--export-dynamic'
- whole_archive_flag_spec_CXX='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
+ hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir'
+ export_dynamic_flag_spec_CXX='$wl--export-dynamic'
+ whole_archive_flag_spec_CXX='$wl--whole-archive$convenience $wl--no-whole-archive'
;;
pgCC* | pgcpp*)
# Portland Group C++ compiler
case `$CC -V` in
*pgCC\ [1-5].* | *pgcpp\ [1-5].*)
prelink_cmds_CXX='tpldir=Template.dir~
- rm -rf $tpldir~
- $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~
- compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"'
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~
+ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"'
old_archive_cmds_CXX='tpldir=Template.dir~
- rm -rf $tpldir~
- $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~
- $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~
- $RANLIB $oldlib'
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~
+ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~
+ $RANLIB $oldlib'
archive_cmds_CXX='tpldir=Template.dir~
- rm -rf $tpldir~
- $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
- $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
archive_expsym_cmds_CXX='tpldir=Template.dir~
- rm -rf $tpldir~
- $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
- $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
;;
*) # Version 6 and above use weak symbols
- archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
- archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+ archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
;;
esac
- hardcode_libdir_flag_spec_CXX='${wl}--rpath ${wl}$libdir'
- export_dynamic_flag_spec_CXX='${wl}--export-dynamic'
- whole_archive_flag_spec_CXX='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+ hardcode_libdir_flag_spec_CXX='$wl--rpath $wl$libdir'
+ export_dynamic_flag_spec_CXX='$wl--export-dynamic'
+ whole_archive_flag_spec_CXX='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
;;
cxx*)
# Compaq C++
- archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
- archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols'
+ archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib $wl-retain-symbols-file $wl$export_symbols'
runpath_var=LD_RUN_PATH
hardcode_libdir_flag_spec_CXX='-rpath $libdir'
hardcode_libdir_separator_CXX=:
# Commands to make compiler produce verbose output that lists
# what "hidden" libraries, object files and flags are used when
# linking a shared library.
#
# There doesn't appear to be a way to prevent this compiler from
# explicitly linking system object files so we need to strip them
# from the output so that they don't get included in the library
# dependencies.
- output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed'
+ output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed'
;;
xl* | mpixl* | bgxl*)
# IBM XL 8.0 on PPC, with GNU ld
- hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir'
- export_dynamic_flag_spec_CXX='${wl}--export-dynamic'
- archive_cmds_CXX='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
- if test "x$supports_anon_versioning" = xyes; then
+ hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir'
+ export_dynamic_flag_spec_CXX='$wl--export-dynamic'
+ archive_cmds_CXX='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ if test yes = "$supports_anon_versioning"; then
archive_expsym_cmds_CXX='echo "{ global:" > $output_objdir/$libname.ver~
- cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
- echo "local: *; };" >> $output_objdir/$libname.ver~
- $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib'
fi
;;
*)
case `$CC -V 2>&1 | sed 5q` in
*Sun\ C*)
# Sun C++ 5.9
no_undefined_flag_CXX=' -zdefs'
- archive_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
- archive_expsym_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols'
+ archive_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ archive_expsym_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols'
hardcode_libdir_flag_spec_CXX='-R$libdir'
- whole_archive_flag_spec_CXX='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+ whole_archive_flag_spec_CXX='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
compiler_needs_object_CXX=yes
# Not sure whether something based on
# $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1
# would be better.
output_verbose_link_cmd='func_echo_all'
# Archives containing C++ object files must be created using
# "CC -xar", where "CC" is the Sun C++ compiler. This is
# necessary to make sure instantiated templates are included
# in the archive.
old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs'
;;
esac
;;
esac
;;
lynxos*)
# FIXME: insert proper C++ library support
ld_shlibs_CXX=no
;;
m88k*)
# FIXME: insert proper C++ library support
ld_shlibs_CXX=no
;;
mvs*)
case $cc_basename in
cxx*)
# FIXME: insert proper C++ library support
ld_shlibs_CXX=no
;;
*)
# FIXME: insert proper C++ library support
ld_shlibs_CXX=no
;;
esac
;;
netbsd*)
if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
archive_cmds_CXX='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags'
wlarc=
hardcode_libdir_flag_spec_CXX='-R$libdir'
hardcode_direct_CXX=yes
hardcode_shlibpath_var_CXX=no
fi
# Workaround some broken pre-1.5 toolchains
output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"'
;;
*nto* | *qnx*)
ld_shlibs_CXX=yes
;;
- openbsd2*)
- # C++ shared libraries are fairly broken
- ld_shlibs_CXX=no
- ;;
-
- openbsd*)
+ openbsd* | bitrig*)
if test -f /usr/libexec/ld.so; then
hardcode_direct_CXX=yes
hardcode_shlibpath_var_CXX=no
hardcode_direct_absolute_CXX=yes
archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
- hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir'
- if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
- archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib'
- export_dynamic_flag_spec_CXX='${wl}-E'
- whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+ hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir'
+ if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then
+ archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib'
+ export_dynamic_flag_spec_CXX='$wl-E'
+ whole_archive_flag_spec_CXX=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive'
fi
output_verbose_link_cmd=func_echo_all
else
ld_shlibs_CXX=no
fi
;;
osf3* | osf4* | osf5*)
case $cc_basename in
KCC*)
# Kuck and Associates, Inc. (KAI) C++ Compiler
# KCC will only create a shared library if the output file
# ends with ".so" (or ".sl" for HP-UX), so rename the library
# to its proper name (with version) after linking.
- archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+ archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
- hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir'
+ hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir'
hardcode_libdir_separator_CXX=:
# Archives containing C++ object files must be created using
# the KAI C++ compiler.
case $host in
osf3*) old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;;
*) old_archive_cmds_CXX='$CC -o $oldlib $oldobjs' ;;
esac
;;
RCC*)
# Rational C++ 2.4.1
# FIXME: insert proper C++ library support
ld_shlibs_CXX=no
;;
cxx*)
case $host in
osf3*)
- allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*'
- archive_cmds_CXX='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && func_echo_all "${wl}-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
- hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir'
+ allow_undefined_flag_CXX=' $wl-expect_unresolved $wl\*'
+ archive_cmds_CXX='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir'
;;
*)
allow_undefined_flag_CXX=' -expect_unresolved \*'
- archive_cmds_CXX='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+ archive_cmds_CXX='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
archive_expsym_cmds_CXX='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~
- echo "-hidden">> $lib.exp~
- $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~
- $RM $lib.exp'
+ echo "-hidden">> $lib.exp~
+ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~
+ $RM $lib.exp'
hardcode_libdir_flag_spec_CXX='-rpath $libdir'
;;
esac
hardcode_libdir_separator_CXX=:
# Commands to make compiler produce verbose output that lists
# what "hidden" libraries, object files and flags are used when
# linking a shared library.
#
# There doesn't appear to be a way to prevent this compiler from
# explicitly linking system object files so we need to strip them
# from the output so that they don't get included in the library
# dependencies.
- output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+ output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
;;
*)
- if test "$GXX" = yes && test "$with_gnu_ld" = no; then
- allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*'
+ if test yes,no = "$GXX,$with_gnu_ld"; then
+ allow_undefined_flag_CXX=' $wl-expect_unresolved $wl\*'
case $host in
osf3*)
- archive_cmds_CXX='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ archive_cmds_CXX='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
;;
*)
- archive_cmds_CXX='$CC -shared $pic_flag -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
;;
esac
- hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir'
hardcode_libdir_separator_CXX=:
# Commands to make compiler produce verbose output that lists
# what "hidden" libraries, object files and flags are used when
# linking a shared library.
output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
else
# FIXME: insert proper C++ library support
ld_shlibs_CXX=no
fi
;;
esac
;;
psos*)
# FIXME: insert proper C++ library support
ld_shlibs_CXX=no
;;
sunos4*)
case $cc_basename in
CC*)
# Sun C++ 4.x
# FIXME: insert proper C++ library support
ld_shlibs_CXX=no
;;
lcc*)
# Lucid
# FIXME: insert proper C++ library support
ld_shlibs_CXX=no
;;
*)
# FIXME: insert proper C++ library support
ld_shlibs_CXX=no
;;
esac
;;
solaris*)
case $cc_basename in
CC* | sunCC*)
# Sun C++ 4.2, 5.x and Centerline C++
archive_cmds_need_lc_CXX=yes
no_undefined_flag_CXX=' -zdefs'
- archive_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ archive_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
- $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+ $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
hardcode_libdir_flag_spec_CXX='-R$libdir'
hardcode_shlibpath_var_CXX=no
case $host_os in
solaris2.[0-5] | solaris2.[0-5].*) ;;
*)
# The compiler driver will combine and reorder linker options,
- # but understands `-z linker_flag'.
+ # but understands '-z linker_flag'.
# Supported since Solaris 2.6 (maybe 2.5.1?)
whole_archive_flag_spec_CXX='-z allextract$convenience -z defaultextract'
;;
esac
link_all_deplibs_CXX=yes
output_verbose_link_cmd='func_echo_all'
# Archives containing C++ object files must be created using
# "CC -xar", where "CC" is the Sun C++ compiler. This is
# necessary to make sure instantiated templates are included
# in the archive.
old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs'
;;
gcx*)
# Green Hills C++ Compiler
- archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+ archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib'
# The C++ compiler must be used to create the archive.
old_archive_cmds_CXX='$CC $LDFLAGS -archive -o $oldlib $oldobjs'
;;
*)
# GNU C++ compiler with Solaris linker
- if test "$GXX" = yes && test "$with_gnu_ld" = no; then
- no_undefined_flag_CXX=' ${wl}-z ${wl}defs'
+ if test yes,no = "$GXX,$with_gnu_ld"; then
+ no_undefined_flag_CXX=' $wl-z ${wl}defs'
if $CC --version | $GREP -v '^2\.7' > /dev/null; then
- archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+ archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib'
archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
- $CC -shared $pic_flag -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+ $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
# Commands to make compiler produce verbose output that lists
# what "hidden" libraries, object files and flags are used when
# linking a shared library.
output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
else
- # g++ 2.7 appears to require `-G' NOT `-shared' on this
+ # g++ 2.7 appears to require '-G' NOT '-shared' on this
# platform.
- archive_cmds_CXX='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+ archive_cmds_CXX='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib'
archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
- $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+ $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
# Commands to make compiler produce verbose output that lists
# what "hidden" libraries, object files and flags are used when
# linking a shared library.
output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
fi
- hardcode_libdir_flag_spec_CXX='${wl}-R $wl$libdir'
+ hardcode_libdir_flag_spec_CXX='$wl-R $wl$libdir'
case $host_os in
solaris2.[0-5] | solaris2.[0-5].*) ;;
*)
- whole_archive_flag_spec_CXX='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+ whole_archive_flag_spec_CXX='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract'
;;
esac
fi
;;
esac
;;
sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*)
- no_undefined_flag_CXX='${wl}-z,text'
+ no_undefined_flag_CXX='$wl-z,text'
archive_cmds_need_lc_CXX=no
hardcode_shlibpath_var_CXX=no
runpath_var='LD_RUN_PATH'
case $cc_basename in
CC*)
- archive_cmds_CXX='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
- archive_expsym_cmds_CXX='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_cmds_CXX='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds_CXX='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
;;
*)
- archive_cmds_CXX='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
- archive_expsym_cmds_CXX='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_cmds_CXX='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds_CXX='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
;;
esac
;;
sysv5* | sco3.2v5* | sco5v6*)
- # Note: We can NOT use -z defs as we might desire, because we do not
+ # Note: We CANNOT use -z defs as we might desire, because we do not
# link with -lc, and that would cause any symbols used from libc to
# always be unresolved, which means just about no library would
# ever link correctly. If we're not using GNU ld we use -z text
# though, which does catch some bad symbols but isn't as heavy-handed
# as -z defs.
- no_undefined_flag_CXX='${wl}-z,text'
- allow_undefined_flag_CXX='${wl}-z,nodefs'
+ no_undefined_flag_CXX='$wl-z,text'
+ allow_undefined_flag_CXX='$wl-z,nodefs'
archive_cmds_need_lc_CXX=no
hardcode_shlibpath_var_CXX=no
- hardcode_libdir_flag_spec_CXX='${wl}-R,$libdir'
+ hardcode_libdir_flag_spec_CXX='$wl-R,$libdir'
hardcode_libdir_separator_CXX=':'
link_all_deplibs_CXX=yes
- export_dynamic_flag_spec_CXX='${wl}-Bexport'
+ export_dynamic_flag_spec_CXX='$wl-Bexport'
runpath_var='LD_RUN_PATH'
case $cc_basename in
CC*)
- archive_cmds_CXX='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
- archive_expsym_cmds_CXX='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_cmds_CXX='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds_CXX='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
old_archive_cmds_CXX='$CC -Tprelink_objects $oldobjs~
- '"$old_archive_cmds_CXX"
+ '"$old_archive_cmds_CXX"
reload_cmds_CXX='$CC -Tprelink_objects $reload_objs~
- '"$reload_cmds_CXX"
+ '"$reload_cmds_CXX"
;;
*)
- archive_cmds_CXX='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
- archive_expsym_cmds_CXX='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_cmds_CXX='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds_CXX='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
;;
esac
;;
tandem*)
case $cc_basename in
NCC*)
# NonStop-UX NCC 3.20
# FIXME: insert proper C++ library support
ld_shlibs_CXX=no
;;
*)
# FIXME: insert proper C++ library support
ld_shlibs_CXX=no
;;
esac
;;
vxworks*)
# FIXME: insert proper C++ library support
ld_shlibs_CXX=no
;;
*)
# FIXME: insert proper C++ library support
ld_shlibs_CXX=no
;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5
$as_echo "$ld_shlibs_CXX" >&6; }
- test "$ld_shlibs_CXX" = no && can_build_shared=no
+ test no = "$ld_shlibs_CXX" && can_build_shared=no
- GCC_CXX="$GXX"
- LD_CXX="$LD"
+ GCC_CXX=$GXX
+ LD_CXX=$LD
## CAVEAT EMPTOR:
## There is no encapsulation within the following macros, do not change
## the running order or otherwise move them around unless you know exactly
## what you are doing...
# Dependencies to place before and after the object being linked:
predep_objects_CXX=
postdep_objects_CXX=
predeps_CXX=
postdeps_CXX=
compiler_lib_search_path_CXX=
cat > conftest.$ac_ext <<_LT_EOF
class Foo
{
public:
Foo (void) { a = 0; }
private:
int a;
};
_LT_EOF
_lt_libdeps_save_CFLAGS=$CFLAGS
case "$CC $CFLAGS " in #(
*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;;
*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;;
*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;;
esac
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
# Parse the compiler output and extract the necessary
# objects, libraries and library flags.
# Sentinel used to keep track of whether or not we are before
# the conftest object file.
pre_test_object_deps_done=no
for p in `eval "$output_verbose_link_cmd"`; do
- case ${prev}${p} in
+ case $prev$p in
-L* | -R* | -l*)
# Some compilers place space between "-{L,R}" and the path.
# Remove the space.
- if test $p = "-L" ||
- test $p = "-R"; then
+ if test x-L = "$p" ||
+ test x-R = "$p"; then
prev=$p
continue
fi
# Expand the sysroot to ease extracting the directories later.
if test -z "$prev"; then
case $p in
-L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;;
-R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;;
-l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;;
esac
fi
case $p in
=*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;;
esac
- if test "$pre_test_object_deps_done" = no; then
- case ${prev} in
+ if test no = "$pre_test_object_deps_done"; then
+ case $prev in
-L | -R)
# Internal compiler library paths should come after those
# provided the user. The postdeps already come after the
# user supplied libs so there is no need to process them.
if test -z "$compiler_lib_search_path_CXX"; then
- compiler_lib_search_path_CXX="${prev}${p}"
+ compiler_lib_search_path_CXX=$prev$p
else
- compiler_lib_search_path_CXX="${compiler_lib_search_path_CXX} ${prev}${p}"
+ compiler_lib_search_path_CXX="${compiler_lib_search_path_CXX} $prev$p"
fi
;;
# The "-l" case would never come before the object being
# linked, so don't bother handling this case.
esac
else
if test -z "$postdeps_CXX"; then
- postdeps_CXX="${prev}${p}"
+ postdeps_CXX=$prev$p
else
- postdeps_CXX="${postdeps_CXX} ${prev}${p}"
+ postdeps_CXX="${postdeps_CXX} $prev$p"
fi
fi
prev=
;;
*.lto.$objext) ;; # Ignore GCC LTO objects
*.$objext)
# This assumes that the test object file only shows up
# once in the compiler output.
if test "$p" = "conftest.$objext"; then
pre_test_object_deps_done=yes
continue
fi
- if test "$pre_test_object_deps_done" = no; then
+ if test no = "$pre_test_object_deps_done"; then
if test -z "$predep_objects_CXX"; then
- predep_objects_CXX="$p"
+ predep_objects_CXX=$p
else
predep_objects_CXX="$predep_objects_CXX $p"
fi
else
if test -z "$postdep_objects_CXX"; then
- postdep_objects_CXX="$p"
+ postdep_objects_CXX=$p
else
postdep_objects_CXX="$postdep_objects_CXX $p"
fi
fi
;;
*) ;; # Ignore the rest.
esac
done
# Clean up.
rm -f a.out a.exe
else
echo "libtool.m4: error: problem compiling CXX test program"
fi
$RM -f confest.$objext
CFLAGS=$_lt_libdeps_save_CFLAGS
# PORTME: override above test on systems where it is broken
case $host_os in
interix[3-9]*)
# Interix 3.5 installs completely hosed .la files for C++, so rather than
# hack all around it, let's just trust "g++" to DTRT.
predep_objects_CXX=
postdep_objects_CXX=
postdeps_CXX=
;;
linux*)
case `$CC -V 2>&1 | sed 5q` in
*Sun\ C*)
# Sun C++ 5.9
# The more standards-conforming stlport4 library is
# incompatible with the Cstd library. Avoid specifying
# it if it's in CXXFLAGS. Ignore libCrun as
# -library=stlport4 depends on it.
case " $CXX $CXXFLAGS " in
*" -library=stlport4 "*)
solaris_use_stlport4=yes
;;
esac
- if test "$solaris_use_stlport4" != yes; then
+ if test yes != "$solaris_use_stlport4"; then
postdeps_CXX='-library=Cstd -library=Crun'
fi
;;
esac
;;
solaris*)
case $cc_basename in
CC* | sunCC*)
# The more standards-conforming stlport4 library is
# incompatible with the Cstd library. Avoid specifying
# it if it's in CXXFLAGS. Ignore libCrun as
# -library=stlport4 depends on it.
case " $CXX $CXXFLAGS " in
*" -library=stlport4 "*)
solaris_use_stlport4=yes
;;
esac
# Adding this requires a known-good setup of shared libraries for
# Sun compiler versions before 5.6, else PIC objects from an old
# archive will be linked into the output, leading to subtle bugs.
- if test "$solaris_use_stlport4" != yes; then
+ if test yes != "$solaris_use_stlport4"; then
postdeps_CXX='-library=Cstd -library=Crun'
fi
;;
esac
;;
esac
case " $postdeps_CXX " in
*" -lc "*) archive_cmds_need_lc_CXX=no ;;
esac
compiler_lib_search_dirs_CXX=
if test -n "${compiler_lib_search_path_CXX}"; then
- compiler_lib_search_dirs_CXX=`echo " ${compiler_lib_search_path_CXX}" | ${SED} -e 's! -L! !g' -e 's!^ !!'`
+ compiler_lib_search_dirs_CXX=`echo " ${compiler_lib_search_path_CXX}" | $SED -e 's! -L! !g' -e 's!^ !!'`
fi
lt_prog_compiler_wl_CXX=
lt_prog_compiler_pic_CXX=
lt_prog_compiler_static_CXX=
# C++ specific cases for pic, static, wl, etc.
- if test "$GXX" = yes; then
+ if test yes = "$GXX"; then
lt_prog_compiler_wl_CXX='-Wl,'
lt_prog_compiler_static_CXX='-static'
case $host_os in
aix*)
# All AIX code is PIC.
- if test "$host_cpu" = ia64; then
+ if test ia64 = "$host_cpu"; then
# AIX 5 now supports IA64 processor
lt_prog_compiler_static_CXX='-Bstatic'
fi
+ lt_prog_compiler_pic_CXX='-fPIC'
;;
amigaos*)
case $host_cpu in
powerpc)
# see comment about AmigaOS4 .so support
lt_prog_compiler_pic_CXX='-fPIC'
;;
m68k)
# FIXME: we need at least 68020 code to build shared libraries, but
- # adding the `-m68020' flag to GCC prevents building anything better,
- # like `-m68040'.
+ # adding the '-m68020' flag to GCC prevents building anything better,
+ # like '-m68040'.
lt_prog_compiler_pic_CXX='-m68020 -resident32 -malways-restore-a4'
;;
esac
;;
beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
# PIC is the default for these OSes.
;;
mingw* | cygwin* | os2* | pw32* | cegcc*)
# This hack is so that the source file can tell whether it is being
# built for inclusion in a dll (and should export symbols for example).
# Although the cygwin gcc ignores -fPIC, still need this for old-style
# (--disable-auto-import) libraries
lt_prog_compiler_pic_CXX='-DDLL_EXPORT'
;;
darwin* | rhapsody*)
# PIC is the default on this platform
# Common symbols not allowed in MH_DYLIB files
lt_prog_compiler_pic_CXX='-fno-common'
;;
*djgpp*)
# DJGPP does not support shared libraries at all
lt_prog_compiler_pic_CXX=
;;
haiku*)
# PIC is the default for Haiku.
# The "-static" flag exists, but is broken.
lt_prog_compiler_static_CXX=
;;
interix[3-9]*)
# Interix 3.x gcc -fpic/-fPIC options generate broken code.
# Instead, we relocate shared libraries at runtime.
;;
sysv4*MP*)
if test -d /usr/nec; then
lt_prog_compiler_pic_CXX=-Kconform_pic
fi
;;
hpux*)
# PIC is the default for 64-bit PA HP-UX, but not for 32-bit
# PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag
# sets the default TLS model and affects inlining.
case $host_cpu in
hppa*64*)
;;
*)
lt_prog_compiler_pic_CXX='-fPIC'
;;
esac
;;
*qnx* | *nto*)
# QNX uses GNU C++, but need to define -shared option too, otherwise
# it will coredump.
lt_prog_compiler_pic_CXX='-fPIC -shared'
;;
*)
lt_prog_compiler_pic_CXX='-fPIC'
;;
esac
else
case $host_os in
aix[4-9]*)
# All AIX code is PIC.
- if test "$host_cpu" = ia64; then
+ if test ia64 = "$host_cpu"; then
# AIX 5 now supports IA64 processor
lt_prog_compiler_static_CXX='-Bstatic'
else
lt_prog_compiler_static_CXX='-bnso -bI:/lib/syscalls.exp'
fi
;;
chorus*)
case $cc_basename in
cxch68*)
# Green Hills C++ Compiler
# _LT_TAGVAR(lt_prog_compiler_static, CXX)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a"
;;
esac
;;
mingw* | cygwin* | os2* | pw32* | cegcc*)
# This hack is so that the source file can tell whether it is being
# built for inclusion in a dll (and should export symbols for example).
lt_prog_compiler_pic_CXX='-DDLL_EXPORT'
;;
dgux*)
case $cc_basename in
ec++*)
lt_prog_compiler_pic_CXX='-KPIC'
;;
ghcx*)
# Green Hills C++ Compiler
lt_prog_compiler_pic_CXX='-pic'
;;
*)
;;
esac
;;
freebsd* | dragonfly*)
# FreeBSD uses GNU C++
;;
hpux9* | hpux10* | hpux11*)
case $cc_basename in
CC*)
lt_prog_compiler_wl_CXX='-Wl,'
- lt_prog_compiler_static_CXX='${wl}-a ${wl}archive'
- if test "$host_cpu" != ia64; then
+ lt_prog_compiler_static_CXX='$wl-a ${wl}archive'
+ if test ia64 != "$host_cpu"; then
lt_prog_compiler_pic_CXX='+Z'
fi
;;
aCC*)
lt_prog_compiler_wl_CXX='-Wl,'
- lt_prog_compiler_static_CXX='${wl}-a ${wl}archive'
+ lt_prog_compiler_static_CXX='$wl-a ${wl}archive'
case $host_cpu in
hppa*64*|ia64*)
# +Z the default
;;
*)
lt_prog_compiler_pic_CXX='+Z'
;;
esac
;;
*)
;;
esac
;;
interix*)
# This is c89, which is MS Visual C++ (no shared libs)
# Anyone wants to do a port?
;;
irix5* | irix6* | nonstopux*)
case $cc_basename in
CC*)
lt_prog_compiler_wl_CXX='-Wl,'
lt_prog_compiler_static_CXX='-non_shared'
# CC pic flag -KPIC is the default.
;;
*)
;;
esac
;;
- linux* | k*bsd*-gnu | kopensolaris*-gnu)
+ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
case $cc_basename in
KCC*)
# KAI C++ Compiler
lt_prog_compiler_wl_CXX='--backend -Wl,'
lt_prog_compiler_pic_CXX='-fPIC'
;;
ecpc* )
- # old Intel C++ for x86_64 which still supported -KPIC.
+ # old Intel C++ for x86_64, which still supported -KPIC.
lt_prog_compiler_wl_CXX='-Wl,'
lt_prog_compiler_pic_CXX='-KPIC'
lt_prog_compiler_static_CXX='-static'
;;
icpc* )
# Intel C++, used to be incompatible with GCC.
# ICC 10 doesn't accept -KPIC any more.
lt_prog_compiler_wl_CXX='-Wl,'
lt_prog_compiler_pic_CXX='-fPIC'
lt_prog_compiler_static_CXX='-static'
;;
pgCC* | pgcpp*)
# Portland Group C++ compiler
lt_prog_compiler_wl_CXX='-Wl,'
lt_prog_compiler_pic_CXX='-fpic'
lt_prog_compiler_static_CXX='-Bstatic'
;;
cxx*)
# Compaq C++
# Make sure the PIC flag is empty. It appears that all Alpha
# Linux and Compaq Tru64 Unix objects are PIC.
lt_prog_compiler_pic_CXX=
lt_prog_compiler_static_CXX='-non_shared'
;;
xlc* | xlC* | bgxl[cC]* | mpixl[cC]*)
# IBM XL 8.0, 9.0 on PPC and BlueGene
lt_prog_compiler_wl_CXX='-Wl,'
lt_prog_compiler_pic_CXX='-qpic'
lt_prog_compiler_static_CXX='-qstaticlink'
;;
*)
case `$CC -V 2>&1 | sed 5q` in
*Sun\ C*)
# Sun C++ 5.9
lt_prog_compiler_pic_CXX='-KPIC'
lt_prog_compiler_static_CXX='-Bstatic'
lt_prog_compiler_wl_CXX='-Qoption ld '
;;
esac
;;
esac
;;
lynxos*)
;;
m88k*)
;;
mvs*)
case $cc_basename in
cxx*)
lt_prog_compiler_pic_CXX='-W c,exportall'
;;
*)
;;
esac
;;
netbsd*)
;;
*qnx* | *nto*)
# QNX uses GNU C++, but need to define -shared option too, otherwise
# it will coredump.
lt_prog_compiler_pic_CXX='-fPIC -shared'
;;
osf3* | osf4* | osf5*)
case $cc_basename in
KCC*)
lt_prog_compiler_wl_CXX='--backend -Wl,'
;;
RCC*)
# Rational C++ 2.4.1
lt_prog_compiler_pic_CXX='-pic'
;;
cxx*)
# Digital/Compaq C++
lt_prog_compiler_wl_CXX='-Wl,'
# Make sure the PIC flag is empty. It appears that all Alpha
# Linux and Compaq Tru64 Unix objects are PIC.
lt_prog_compiler_pic_CXX=
lt_prog_compiler_static_CXX='-non_shared'
;;
*)
;;
esac
;;
psos*)
;;
solaris*)
case $cc_basename in
CC* | sunCC*)
# Sun C++ 4.2, 5.x and Centerline C++
lt_prog_compiler_pic_CXX='-KPIC'
lt_prog_compiler_static_CXX='-Bstatic'
lt_prog_compiler_wl_CXX='-Qoption ld '
;;
gcx*)
# Green Hills C++ Compiler
lt_prog_compiler_pic_CXX='-PIC'
;;
*)
;;
esac
;;
sunos4*)
case $cc_basename in
CC*)
# Sun C++ 4.x
lt_prog_compiler_pic_CXX='-pic'
lt_prog_compiler_static_CXX='-Bstatic'
;;
lcc*)
# Lucid
lt_prog_compiler_pic_CXX='-pic'
;;
*)
;;
esac
;;
sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
case $cc_basename in
CC*)
lt_prog_compiler_wl_CXX='-Wl,'
lt_prog_compiler_pic_CXX='-KPIC'
lt_prog_compiler_static_CXX='-Bstatic'
;;
esac
;;
tandem*)
case $cc_basename in
NCC*)
# NonStop-UX NCC 3.20
lt_prog_compiler_pic_CXX='-KPIC'
;;
*)
;;
esac
;;
vxworks*)
;;
*)
lt_prog_compiler_can_build_shared_CXX=no
;;
esac
fi
case $host_os in
- # For platforms which do not support PIC, -DPIC is meaningless:
+ # For platforms that do not support PIC, -DPIC is meaningless:
*djgpp*)
lt_prog_compiler_pic_CXX=
;;
*)
lt_prog_compiler_pic_CXX="$lt_prog_compiler_pic_CXX -DPIC"
;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5
$as_echo_n "checking for $compiler option to produce PIC... " >&6; }
if ${lt_cv_prog_compiler_pic_CXX+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_prog_compiler_pic_CXX=$lt_prog_compiler_pic_CXX
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_CXX" >&5
$as_echo "$lt_cv_prog_compiler_pic_CXX" >&6; }
lt_prog_compiler_pic_CXX=$lt_cv_prog_compiler_pic_CXX
#
# Check to make sure the PIC flag actually works.
#
if test -n "$lt_prog_compiler_pic_CXX"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works" >&5
$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works... " >&6; }
if ${lt_cv_prog_compiler_pic_works_CXX+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_prog_compiler_pic_works_CXX=no
ac_outfile=conftest.$ac_objext
echo "$lt_simple_compile_test_code" > conftest.$ac_ext
- lt_compiler_flag="$lt_prog_compiler_pic_CXX -DPIC"
+ lt_compiler_flag="$lt_prog_compiler_pic_CXX -DPIC" ## exclude from sc_useless_quotes_in_assignment
# Insert the option either (1) after the last *FLAGS variable, or
# (2) before a word containing "conftest.", or (3) at the end.
# Note that $ac_compile itself does not contain backslashes and begins
# with a dollar sign (not a hyphen), so the echo should work correctly.
# The option is referenced via a variable to avoid confusing sed.
lt_compile=`echo "$ac_compile" | $SED \
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
$ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp
$SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
lt_cv_prog_compiler_pic_works_CXX=yes
fi
fi
$RM conftest*
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works_CXX" >&5
$as_echo "$lt_cv_prog_compiler_pic_works_CXX" >&6; }
-if test x"$lt_cv_prog_compiler_pic_works_CXX" = xyes; then
+if test yes = "$lt_cv_prog_compiler_pic_works_CXX"; then
case $lt_prog_compiler_pic_CXX in
"" | " "*) ;;
*) lt_prog_compiler_pic_CXX=" $lt_prog_compiler_pic_CXX" ;;
esac
else
lt_prog_compiler_pic_CXX=
lt_prog_compiler_can_build_shared_CXX=no
fi
fi
#
# Check to make sure the static flag actually works.
#
wl=$lt_prog_compiler_wl_CXX eval lt_tmp_static_flag=\"$lt_prog_compiler_static_CXX\"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5
$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; }
if ${lt_cv_prog_compiler_static_works_CXX+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_prog_compiler_static_works_CXX=no
- save_LDFLAGS="$LDFLAGS"
+ save_LDFLAGS=$LDFLAGS
LDFLAGS="$LDFLAGS $lt_tmp_static_flag"
echo "$lt_simple_link_test_code" > conftest.$ac_ext
if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
# The linker can only warn and ignore the option if not recognized
# So say no if there are warnings
if test -s conftest.err; then
# Append any errors to the config.log.
cat conftest.err 1>&5
$ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp
$SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
if diff conftest.exp conftest.er2 >/dev/null; then
lt_cv_prog_compiler_static_works_CXX=yes
fi
else
lt_cv_prog_compiler_static_works_CXX=yes
fi
fi
$RM -r conftest*
- LDFLAGS="$save_LDFLAGS"
+ LDFLAGS=$save_LDFLAGS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works_CXX" >&5
$as_echo "$lt_cv_prog_compiler_static_works_CXX" >&6; }
-if test x"$lt_cv_prog_compiler_static_works_CXX" = xyes; then
+if test yes = "$lt_cv_prog_compiler_static_works_CXX"; then
:
else
lt_prog_compiler_static_CXX=
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
if ${lt_cv_prog_compiler_c_o_CXX+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_prog_compiler_c_o_CXX=no
$RM -r conftest 2>/dev/null
mkdir conftest
cd conftest
mkdir out
echo "$lt_simple_compile_test_code" > conftest.$ac_ext
lt_compiler_flag="-o out/conftest2.$ac_objext"
# Insert the option either (1) after the last *FLAGS variable, or
# (2) before a word containing "conftest.", or (3) at the end.
# Note that $ac_compile itself does not contain backslashes and begins
# with a dollar sign (not a hyphen), so the echo should work correctly.
lt_compile=`echo "$ac_compile" | $SED \
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings
$ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
$SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
lt_cv_prog_compiler_c_o_CXX=yes
fi
fi
chmod u+w . 2>&5
$RM conftest*
# SGI C++ compiler will create directory out/ii_files/ for
# template instantiation
test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
$RM out/* && rmdir out
cd ..
$RM -r conftest
$RM conftest*
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5
$as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
if ${lt_cv_prog_compiler_c_o_CXX+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_prog_compiler_c_o_CXX=no
$RM -r conftest 2>/dev/null
mkdir conftest
cd conftest
mkdir out
echo "$lt_simple_compile_test_code" > conftest.$ac_ext
lt_compiler_flag="-o out/conftest2.$ac_objext"
# Insert the option either (1) after the last *FLAGS variable, or
# (2) before a word containing "conftest.", or (3) at the end.
# Note that $ac_compile itself does not contain backslashes and begins
# with a dollar sign (not a hyphen), so the echo should work correctly.
lt_compile=`echo "$ac_compile" | $SED \
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings
$ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
$SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
lt_cv_prog_compiler_c_o_CXX=yes
fi
fi
chmod u+w . 2>&5
$RM conftest*
# SGI C++ compiler will create directory out/ii_files/ for
# template instantiation
test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
$RM out/* && rmdir out
cd ..
$RM -r conftest
$RM conftest*
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5
$as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; }
-hard_links="nottested"
-if test "$lt_cv_prog_compiler_c_o_CXX" = no && test "$need_locks" != no; then
+hard_links=nottested
+if test no = "$lt_cv_prog_compiler_c_o_CXX" && test no != "$need_locks"; then
# do not overwrite the value of need_locks provided by the user
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5
$as_echo_n "checking if we can lock with hard links... " >&6; }
hard_links=yes
$RM conftest*
ln conftest.a conftest.b 2>/dev/null && hard_links=no
touch conftest.a
ln conftest.a conftest.b 2>&5 || hard_links=no
ln conftest.a conftest.b 2>/dev/null && hard_links=no
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5
$as_echo "$hard_links" >&6; }
- if test "$hard_links" = no; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5
-$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;}
+ if test no = "$hard_links"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5
+$as_echo "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;}
need_locks=warn
fi
else
need_locks=no
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5
$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; }
export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
exclude_expsyms_CXX='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'
case $host_os in
aix[4-9]*)
# If we're using GNU nm, then we don't want the "-C" option.
# -C means demangle to AIX nm, but means don't demangle with GNU nm
# Also, AIX nm treats weak defined symbols like other global defined
# symbols, whereas GNU nm marks them as "W".
if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
export_symbols_cmds_CXX='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
else
export_symbols_cmds_CXX='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
fi
;;
pw32*)
- export_symbols_cmds_CXX="$ltdll_cmds"
+ export_symbols_cmds_CXX=$ltdll_cmds
;;
cygwin* | mingw* | cegcc*)
case $cc_basename in
cl*)
exclude_expsyms_CXX='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
;;
*)
export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols'
exclude_expsyms_CXX='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'
;;
esac
;;
*)
export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5
$as_echo "$ld_shlibs_CXX" >&6; }
-test "$ld_shlibs_CXX" = no && can_build_shared=no
+test no = "$ld_shlibs_CXX" && can_build_shared=no
with_gnu_ld_CXX=$with_gnu_ld
#
# Do we need to explicitly link libc?
#
case "x$archive_cmds_need_lc_CXX" in
x|xyes)
# Assume -lc should be added
archive_cmds_need_lc_CXX=yes
- if test "$enable_shared" = yes && test "$GCC" = yes; then
+ if test yes,yes = "$GCC,$enable_shared"; then
case $archive_cmds_CXX in
*'~'*)
# FIXME: we may have to deal with multi-command sequences.
;;
'$CC '*)
# Test whether the compiler implicitly links with -lc since on some
# systems, -lgcc has to come before -lc. If gcc already passes -lc
# to ld, don't add -lc before -lgcc.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5
$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; }
if ${lt_cv_archive_cmds_need_lc_CXX+:} false; then :
$as_echo_n "(cached) " >&6
else
$RM conftest*
echo "$lt_simple_compile_test_code" > conftest.$ac_ext
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } 2>conftest.err; then
soname=conftest
lib=conftest
libobjs=conftest.$ac_objext
deplibs=
wl=$lt_prog_compiler_wl_CXX
pic_flag=$lt_prog_compiler_pic_CXX
compiler_flags=-v
linker_flags=-v
verstring=
output_objdir=.
libname=conftest
lt_save_allow_undefined_flag=$allow_undefined_flag_CXX
allow_undefined_flag_CXX=
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5
(eval $archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }
then
lt_cv_archive_cmds_need_lc_CXX=no
else
lt_cv_archive_cmds_need_lc_CXX=yes
fi
allow_undefined_flag_CXX=$lt_save_allow_undefined_flag
else
cat conftest.err 1>&5
fi
$RM conftest*
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc_CXX" >&5
$as_echo "$lt_cv_archive_cmds_need_lc_CXX" >&6; }
archive_cmds_need_lc_CXX=$lt_cv_archive_cmds_need_lc_CXX
;;
esac
fi
;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5
$as_echo_n "checking dynamic linker characteristics... " >&6; }
library_names_spec=
libname_spec='lib$name'
soname_spec=
-shrext_cmds=".so"
+shrext_cmds=.so
postinstall_cmds=
postuninstall_cmds=
finish_cmds=
finish_eval=
shlibpath_var=
shlibpath_overrides_runpath=unknown
version_type=none
dynamic_linker="$host_os ld.so"
sys_lib_dlsearch_path_spec="/lib /usr/lib"
need_lib_prefix=unknown
hardcode_into_libs=no
# when you set need_version to no, make sure it does not cause -set_version
# flags to be left without arguments
need_version=unknown
case $host_os in
aix3*)
version_type=linux # correct to gnu/linux during the next big refactor
- library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname.a'
shlibpath_var=LIBPATH
# AIX 3 has no versioning support, so we append a major version to the name.
- soname_spec='${libname}${release}${shared_ext}$major'
+ soname_spec='$libname$release$shared_ext$major'
;;
aix[4-9]*)
version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
hardcode_into_libs=yes
- if test "$host_cpu" = ia64; then
+ if test ia64 = "$host_cpu"; then
# AIX 5 supports IA64
- library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}'
+ library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext'
shlibpath_var=LD_LIBRARY_PATH
else
# With GCC up to 2.95.x, collect2 would create an import file
# for dependence libraries. The import file would start with
- # the line `#! .'. This would cause the generated library to
- # depend on `.', always an invalid library. This was fixed in
+ # the line '#! .'. This would cause the generated library to
+ # depend on '.', always an invalid library. This was fixed in
# development snapshots of GCC prior to 3.0.
case $host_os in
aix4 | aix4.[01] | aix4.[01].*)
if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
echo ' yes '
- echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then
+ echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then
:
else
can_build_shared=no
fi
;;
esac
- # AIX (on Power*) has no versioning support, so currently we can not hardcode correct
+ # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct
# soname into executable. Probably we can add versioning support to
# collect2, so additional links can be useful in future.
- if test "$aix_use_runtimelinking" = yes; then
+ if test yes = "$aix_use_runtimelinking"; then
# If using run time linking (on AIX 4.2 or later) use lib<name>.so
# instead of lib<name>.a to let people know that these are not
# typical AIX shared libraries.
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
else
# We preserve .a as extension for shared libraries through AIX4.2
# and later when we are not doing run time linking.
- library_names_spec='${libname}${release}.a $libname.a'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release.a $libname.a'
+ soname_spec='$libname$release$shared_ext$major'
fi
shlibpath_var=LIBPATH
fi
;;
amigaos*)
case $host_cpu in
powerpc)
# Since July 2007 AmigaOS4 officially supports .so libraries.
# When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
;;
m68k)
library_names_spec='$libname.ixlibrary $libname.a'
# Create ${libname}_ixlibrary.a entries in /sys/libs.
- finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+ finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
;;
esac
;;
beos*)
- library_names_spec='${libname}${shared_ext}'
+ library_names_spec='$libname$shared_ext'
dynamic_linker="$host_os ld.so"
shlibpath_var=LIBRARY_PATH
;;
bsdi[45]*)
version_type=linux # correct to gnu/linux during the next big refactor
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
shlibpath_var=LD_LIBRARY_PATH
sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
# the default ld.so.conf also contains /usr/contrib/lib and
# /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
# libtool to hard-code these into programs
;;
cygwin* | mingw* | pw32* | cegcc*)
version_type=windows
- shrext_cmds=".dll"
+ shrext_cmds=.dll
need_version=no
need_lib_prefix=no
case $GCC,$cc_basename in
yes,*)
# gcc
library_names_spec='$libname.dll.a'
# DLL is installed to $(libdir)/../bin by postinstall_cmds
- postinstall_cmds='base_file=`basename \${file}`~
- dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
dldir=$destdir/`dirname \$dlpath`~
test -d \$dldir || mkdir -p \$dldir~
$install_prog $dir/$dlname \$dldir/$dlname~
chmod a+x \$dldir/$dlname~
if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
fi'
postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
dlpath=$dir/\$dldll~
$RM \$dlpath'
shlibpath_overrides_runpath=yes
case $host_os in
cygwin*)
# Cygwin DLLs use 'cyg' prefix rather than 'lib'
- soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+ soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
;;
mingw* | cegcc*)
# MinGW DLLs use traditional 'lib' prefix
- soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+ soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
;;
pw32*)
# pw32 DLLs use 'pw' prefix rather than 'lib'
- library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+ library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
;;
esac
dynamic_linker='Win32 ld.exe'
;;
*,cl*)
# Native MSVC
libname_spec='$name'
- soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
- library_names_spec='${libname}.dll.lib'
+ soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
+ library_names_spec='$libname.dll.lib'
case $build_os in
mingw*)
sys_lib_search_path_spec=
lt_save_ifs=$IFS
IFS=';'
for lt_path in $LIB
do
IFS=$lt_save_ifs
# Let DOS variable expansion print the short 8.3 style file name.
lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"`
sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path"
done
IFS=$lt_save_ifs
# Convert to MSYS style.
sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'`
;;
cygwin*)
# Convert to unix form, then to dos form, then back to unix form
# but this time dos style (no spaces!) so that the unix form looks
# like /cygdrive/c/PROGRA~1:/cygdr...
sys_lib_search_path_spec=`cygpath --path --unix "$LIB"`
sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null`
sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
;;
*)
- sys_lib_search_path_spec="$LIB"
+ sys_lib_search_path_spec=$LIB
if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then
# It is most probably a Windows format PATH.
sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
else
sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
fi
# FIXME: find the short name or the path components, as spaces are
# common. (e.g. "Program Files" -> "PROGRA~1")
;;
esac
# DLL is installed to $(libdir)/../bin by postinstall_cmds
- postinstall_cmds='base_file=`basename \${file}`~
- dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
dldir=$destdir/`dirname \$dlpath`~
test -d \$dldir || mkdir -p \$dldir~
$install_prog $dir/$dlname \$dldir/$dlname'
postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
dlpath=$dir/\$dldll~
$RM \$dlpath'
shlibpath_overrides_runpath=yes
dynamic_linker='Win32 link.exe'
;;
*)
# Assume MSVC wrapper
- library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib'
+ library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib'
dynamic_linker='Win32 ld.exe'
;;
esac
# FIXME: first we should search . and the directory the executable is in
shlibpath_var=PATH
;;
darwin* | rhapsody*)
dynamic_linker="$host_os dyld"
version_type=darwin
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext'
- soname_spec='${libname}${release}${major}$shared_ext'
+ library_names_spec='$libname$release$major$shared_ext $libname$shared_ext'
+ soname_spec='$libname$release$major$shared_ext'
shlibpath_overrides_runpath=yes
shlibpath_var=DYLD_LIBRARY_PATH
shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
;;
dgux*)
version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
;;
freebsd* | dragonfly*)
# DragonFly does not have aout. When/if they implement a new
# versioning mechanism, adjust this.
if test -x /usr/bin/objformat; then
objformat=`/usr/bin/objformat`
else
case $host_os in
freebsd[23].*) objformat=aout ;;
*) objformat=elf ;;
esac
fi
version_type=freebsd-$objformat
case $version_type in
freebsd-elf*)
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext'
need_version=no
need_lib_prefix=no
;;
freebsd-*)
- library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
need_version=yes
;;
esac
shlibpath_var=LD_LIBRARY_PATH
case $host_os in
freebsd2.*)
shlibpath_overrides_runpath=yes
;;
freebsd3.[01]* | freebsdelf3.[01]*)
shlibpath_overrides_runpath=yes
hardcode_into_libs=yes
;;
freebsd3.[2-9]* | freebsdelf3.[2-9]* | \
freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1)
shlibpath_overrides_runpath=no
hardcode_into_libs=yes
;;
*) # from 4.6 on, and DragonFly
shlibpath_overrides_runpath=yes
hardcode_into_libs=yes
;;
esac
;;
-gnu*)
- version_type=linux # correct to gnu/linux during the next big refactor
- need_lib_prefix=no
- need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
- shlibpath_var=LD_LIBRARY_PATH
- shlibpath_overrides_runpath=no
- hardcode_into_libs=yes
- ;;
-
haiku*)
version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
dynamic_linker="$host_os runtime_loader"
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LIBRARY_PATH
- shlibpath_overrides_runpath=yes
+ shlibpath_overrides_runpath=no
sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib'
hardcode_into_libs=yes
;;
hpux9* | hpux10* | hpux11*)
# Give a soname corresponding to the major version so that dld.sl refuses to
# link against other versions.
version_type=sunos
need_lib_prefix=no
need_version=no
case $host_cpu in
ia64*)
shrext_cmds='.so'
hardcode_into_libs=yes
dynamic_linker="$host_os dld.so"
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
- if test "X$HPUX_IA64_MODE" = X32; then
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ if test 32 = "$HPUX_IA64_MODE"; then
sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
else
sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
fi
sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
;;
hppa*64*)
shrext_cmds='.sl'
hardcode_into_libs=yes
dynamic_linker="$host_os dld.sl"
shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
;;
*)
shrext_cmds='.sl'
dynamic_linker="$host_os dld.sl"
shlibpath_var=SHLIB_PATH
shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
;;
esac
# HP-UX runs *really* slowly unless shared libraries are mode 555, ...
postinstall_cmds='chmod 555 $lib'
# or fails outright, so override atomically:
install_override_mode=555
;;
interix[3-9]*)
version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=no
hardcode_into_libs=yes
;;
irix5* | irix6* | nonstopux*)
case $host_os in
nonstopux*) version_type=nonstopux ;;
*)
- if test "$lt_cv_prog_gnu_ld" = yes; then
+ if test yes = "$lt_cv_prog_gnu_ld"; then
version_type=linux # correct to gnu/linux during the next big refactor
else
version_type=irix
fi ;;
esac
need_lib_prefix=no
need_version=no
- soname_spec='${libname}${release}${shared_ext}$major'
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}'
+ soname_spec='$libname$release$shared_ext$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext'
case $host_os in
irix5* | nonstopux*)
libsuff= shlibsuff=
;;
*)
case $LD in # libtool.m4 will add one of these switches to LD
*-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
libsuff= shlibsuff= libmagic=32-bit;;
*-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
libsuff=32 shlibsuff=N32 libmagic=N32;;
*-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
libsuff=64 shlibsuff=64 libmagic=64-bit;;
*) libsuff= shlibsuff= libmagic=never-match;;
esac
;;
esac
shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
shlibpath_overrides_runpath=no
- sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
- sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+ sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff"
+ sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff"
hardcode_into_libs=yes
;;
# No shared lib support for Linux oldld, aout, or coff.
linux*oldld* | linux*aout* | linux*coff*)
dynamic_linker=no
;;
+linux*android*)
+ version_type=none # Android doesn't support versioned libraries.
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext'
+ soname_spec='$libname$release$shared_ext'
+ finish_cmds=
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+
+ # This implies no fast_install, which is unacceptable.
+ # Some rework will be needed to allow for fast_install
+ # before this can be enabled.
+ hardcode_into_libs=yes
+
+ dynamic_linker='Android linker'
+ # Don't embed -rpath directories since the linker doesn't support them.
+ hardcode_libdir_flag_spec_CXX='-L$libdir'
+ ;;
+
# This must be glibc/ELF.
-linux* | k*bsd*-gnu | kopensolaris*-gnu)
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=no
# Some binutils ld are patched to set DT_RUNPATH
if ${lt_cv_shlibpath_overrides_runpath+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_shlibpath_overrides_runpath=no
save_LDFLAGS=$LDFLAGS
save_libdir=$libdir
eval "libdir=/foo; wl=\"$lt_prog_compiler_wl_CXX\"; \
LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec_CXX\""
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_cxx_try_link "$LINENO"; then :
if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then :
lt_cv_shlibpath_overrides_runpath=yes
fi
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LDFLAGS=$save_LDFLAGS
libdir=$save_libdir
fi
shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath
# This implies no fast_install, which is unacceptable.
# Some rework will be needed to allow for fast_install
# before this can be enabled.
hardcode_into_libs=yes
# Append ld.so.conf contents to the search path
if test -f /etc/ld.so.conf; then
lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '`
sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
fi
# We used to test for /lib/ld.so.1 and disable shared libraries on
# powerpc, because MkLinux only supported shared libraries with the
# GNU dynamic linker. Since this was broken with cross compilers,
# most powerpc-linux boxes support dynamic linking these days and
# people can always --disable-shared, the test was removed, and we
# assume the GNU/Linux dynamic linker is in use.
dynamic_linker='GNU/Linux ld.so'
;;
netbsd*)
version_type=sunos
need_lib_prefix=no
need_version=no
if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
dynamic_linker='NetBSD (a.out) ld.so'
else
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
dynamic_linker='NetBSD ld.elf_so'
fi
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes
hardcode_into_libs=yes
;;
newsos6)
version_type=linux # correct to gnu/linux during the next big refactor
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes
;;
*nto* | *qnx*)
version_type=qnx
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=no
hardcode_into_libs=yes
dynamic_linker='ldqnx.so'
;;
-openbsd*)
+openbsd* | bitrig*)
version_type=sunos
- sys_lib_dlsearch_path_spec="/usr/lib"
+ sys_lib_dlsearch_path_spec=/usr/lib
need_lib_prefix=no
- # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs.
- case $host_os in
- openbsd3.3 | openbsd3.3.*) need_version=yes ;;
- *) need_version=no ;;
- esac
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
- finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
- shlibpath_var=LD_LIBRARY_PATH
- if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
- case $host_os in
- openbsd2.[89] | openbsd2.[89].*)
- shlibpath_overrides_runpath=no
- ;;
- *)
- shlibpath_overrides_runpath=yes
- ;;
- esac
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
+ need_version=no
else
- shlibpath_overrides_runpath=yes
+ need_version=yes
fi
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
;;
os2*)
libname_spec='$name'
- shrext_cmds=".dll"
+ shrext_cmds=.dll
need_lib_prefix=no
- library_names_spec='$libname${shared_ext} $libname.a'
+ library_names_spec='$libname$shared_ext $libname.a'
dynamic_linker='OS/2 ld.exe'
shlibpath_var=LIBPATH
;;
osf3* | osf4* | osf5*)
version_type=osf
need_lib_prefix=no
need_version=no
- soname_spec='${libname}${release}${shared_ext}$major'
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='$libname$release$shared_ext$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
shlibpath_var=LD_LIBRARY_PATH
sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
- sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
;;
rdos*)
dynamic_linker=no
;;
solaris*)
version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes
hardcode_into_libs=yes
# ldd complains unless libraries are executable
postinstall_cmds='chmod +x $lib'
;;
sunos4*)
version_type=sunos
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes
- if test "$with_gnu_ld" = yes; then
+ if test yes = "$with_gnu_ld"; then
need_lib_prefix=no
fi
need_version=yes
;;
sysv4 | sysv4.3*)
version_type=linux # correct to gnu/linux during the next big refactor
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
case $host_vendor in
sni)
shlibpath_overrides_runpath=no
need_lib_prefix=no
runpath_var=LD_RUN_PATH
;;
siemens)
need_lib_prefix=no
;;
motorola)
need_lib_prefix=no
need_version=no
shlibpath_overrides_runpath=no
sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
;;
esac
;;
sysv4*MP*)
- if test -d /usr/nec ;then
+ if test -d /usr/nec; then
version_type=linux # correct to gnu/linux during the next big refactor
- library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}'
- soname_spec='$libname${shared_ext}.$major'
+ library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext'
+ soname_spec='$libname$shared_ext.$major'
shlibpath_var=LD_LIBRARY_PATH
fi
;;
sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
version_type=freebsd-elf
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes
hardcode_into_libs=yes
- if test "$with_gnu_ld" = yes; then
+ if test yes = "$with_gnu_ld"; then
sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
else
sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
case $host_os in
sco3.2v5*)
sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
;;
esac
fi
sys_lib_dlsearch_path_spec='/usr/lib'
;;
tpf*)
# TPF is a cross-target only. Preferred cross-host = GNU/Linux.
version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=no
hardcode_into_libs=yes
;;
uts4*)
version_type=linux # correct to gnu/linux during the next big refactor
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
;;
*)
dynamic_linker=no
;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5
$as_echo "$dynamic_linker" >&6; }
-test "$dynamic_linker" = no && can_build_shared=no
+test no = "$dynamic_linker" && can_build_shared=no
variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
-if test "$GCC" = yes; then
+if test yes = "$GCC"; then
variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
fi
-if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then
- sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec"
+if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then
+ sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec
fi
-if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then
- sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec"
+if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then
+ sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5
$as_echo_n "checking how to hardcode library paths into programs... " >&6; }
hardcode_action_CXX=
if test -n "$hardcode_libdir_flag_spec_CXX" ||
test -n "$runpath_var_CXX" ||
- test "X$hardcode_automatic_CXX" = "Xyes" ; then
+ test yes = "$hardcode_automatic_CXX"; then
# We can hardcode non-existent directories.
- if test "$hardcode_direct_CXX" != no &&
+ if test no != "$hardcode_direct_CXX" &&
# If the only mechanism to avoid hardcoding is shlibpath_var, we
# have to relink, otherwise we might link with an installed library
# when we should be linking with a yet-to-be-installed one
- ## test "$_LT_TAGVAR(hardcode_shlibpath_var, CXX)" != no &&
- test "$hardcode_minus_L_CXX" != no; then
+ ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, CXX)" &&
+ test no != "$hardcode_minus_L_CXX"; then
# Linking always hardcodes the temporary library directory.
hardcode_action_CXX=relink
else
# We can link without hardcoding, and we can hardcode nonexisting dirs.
hardcode_action_CXX=immediate
fi
else
# We cannot hardcode anything, or else we can only hardcode existing
# directories.
hardcode_action_CXX=unsupported
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action_CXX" >&5
$as_echo "$hardcode_action_CXX" >&6; }
-if test "$hardcode_action_CXX" = relink ||
- test "$inherit_rpath_CXX" = yes; then
+if test relink = "$hardcode_action_CXX" ||
+ test yes = "$inherit_rpath_CXX"; then
# Fast installation is not supported
enable_fast_install=no
-elif test "$shlibpath_overrides_runpath" = yes ||
- test "$enable_shared" = no; then
+elif test yes = "$shlibpath_overrides_runpath" ||
+ test no = "$enable_shared"; then
# Fast installation is not necessary
enable_fast_install=needless
fi
fi # test -n "$compiler"
CC=$lt_save_CC
CFLAGS=$lt_save_CFLAGS
LDCXX=$LD
LD=$lt_save_LD
GCC=$lt_save_GCC
with_gnu_ld=$lt_save_with_gnu_ld
lt_cv_path_LDCXX=$lt_cv_path_LD
lt_cv_path_LD=$lt_save_path_LD
lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld
lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld
-fi # test "$_lt_caught_CXX_error" != yes
+fi # test yes != "$_lt_caught_CXX_error"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
ac_config_commands="$ac_config_commands libtool"
# Only expand once:
# Check whether --enable-experimental-libtool was given.
if test "${enable_experimental_libtool+set}" = set; then :
enableval=$enable_experimental_libtool; experimental_libtool=$enableval
else
experimental_libtool=no
fi
if test "$experimental_libtool" = "yes"; then
echo "using APR's libtool"
sh_libtool="`$apr_config --apr-libtool`"
LIBTOOL="$sh_libtool"
SVN_LIBTOOL="$sh_libtool"
else
sh_libtool="$abs_builddir/libtool"
SVN_LIBTOOL="\$(SHELL) $sh_libtool"
fi
lt_pversion=`$LIBTOOL --version 2>/dev/null|$SED -e 's/([^)]*)//g;s/^[^0-9]*//;s/[- ].*//g;q'`
lt_version=`echo $lt_pversion|$SED -e 's/\([a-z]*\)$/.\1/'`
lt_major_version=`echo $lt_version | cut -d'.' -f 1`
svn_enable_static=yes
svn_enable_shared=yes
# Check whether --enable-static was given.
if test "${enable_static+set}" = set; then :
enableval=$enable_static; svn_enable_static="$enableval"
else
svn_enable_static="yes"
fi
# Check whether --enable-shared was given.
if test "${enable_shared+set}" = set; then :
enableval=$enable_shared; svn_enable_shared="$enableval"
else
svn_enable_shared="yes"
fi
if test "$svn_enable_static" = "yes" && test "$svn_enable_shared" = "yes" ; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: building both shared and static libraries" >&5
$as_echo "$as_me: building both shared and static libraries" >&6;}
elif test "$svn_enable_static" = "yes" ; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: building static libraries only" >&5
$as_echo "$as_me: building static libraries only" >&6;}
LT_CFLAGS="-static $LT_CFLAGS"
LT_LDFLAGS="-static $LT_LDFLAGS"
elif test "$svn_enable_shared" = "yes" ; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: building shared libraries only" >&5
$as_echo "$as_me: building shared libraries only" >&6;}
if test "$lt_major_version" = "1" ; then
LT_CFLAGS="-prefer-pic $LT_CFLAGS"
elif test "$lt_major_version" = "2" ; then
LT_CFLAGS="-shared $LT_CFLAGS"
fi
LT_LDFLAGS="-shared $LT_LDFLAGS"
else
as_fn_error $? "cannot disable both shared and static libraries" "$LINENO" 5
fi
# Check whether --enable-all-static was given.
if test "${enable_all_static+set}" = set; then :
enableval=$enable_all_static;
if test "$enableval" = "yes" ; then
LT_LDFLAGS="-all-static $LT_LDFLAGS"
elif test "$enableval" != "no" ; then
as_fn_error $? "--enable-all-static doesn't accept argument" "$LINENO" 5
fi
fi
# Check whether --enable-local-library-preloading was given.
if test "${enable_local_library_preloading+set}" = set; then :
enableval=$enable_local_library_preloading;
if test "$enableval" != "no"; then
if test "$svn_enable_shared" = "yes"; then
TRANSFORM_LIBTOOL_SCRIPTS="transform-libtool-scripts"
else
as_fn_error $? "--enable-local-library-preloading conflicts with --disable-shared" "$LINENO" 5
fi
else
TRANSFORM_LIBTOOL_SCRIPTS=""
fi
else
TRANSFORM_LIBTOOL_SCRIPTS=""
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether libtool needs -no-undefined" >&5
$as_echo_n "checking whether libtool needs -no-undefined... " >&6; }
case $host in
*-*-cygwin*)
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
LT_NO_UNDEFINED="-no-undefined"
;;
*)
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
LT_NO_UNDEFINED=""
;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to avoid circular linkage at all costs" >&5
$as_echo_n "checking whether to avoid circular linkage at all costs... " >&6; }
case $host in
*-*-cygwin*)
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
$as_echo "#define SVN_AVOID_CIRCULAR_LINKAGE_AT_ALL_COSTS_HACK 1" >>confdefs.h
;;
*)
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
;;
esac
trang=yes
# Check whether --with-trang was given.
if test "${with_trang+set}" = set; then :
withval=$with_trang;
trang="$withval"
fi
if test "$trang" = "yes"; then
# Extract the first word of "trang", so it can be a program name with args.
set dummy trang; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_TRANG+:} false; then :
$as_echo_n "(cached) " >&6
else
case $TRANG in
[\\/]* | ?:[\\/]*)
ac_cv_path_TRANG="$TRANG" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_TRANG="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
test -z "$ac_cv_path_TRANG" && ac_cv_path_TRANG="none"
;;
esac
fi
TRANG=$ac_cv_path_TRANG
if test -n "$TRANG"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $TRANG" >&5
$as_echo "$TRANG" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
else
TRANG="$trang"
fi
doxygen=yes
# Check whether --with-doxygen was given.
if test "${with_doxygen+set}" = set; then :
withval=$with_doxygen;
doxygen="$withval"
fi
if test "$doxygen" = "yes"; then
# Extract the first word of "doxygen", so it can be a program name with args.
set dummy doxygen; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_DOXYGEN+:} false; then :
$as_echo_n "(cached) " >&6
else
case $DOXYGEN in
[\\/]* | ?:[\\/]*)
ac_cv_path_DOXYGEN="$DOXYGEN" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_DOXYGEN="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
test -z "$ac_cv_path_DOXYGEN" && ac_cv_path_DOXYGEN="none"
;;
esac
fi
DOXYGEN=$ac_cv_path_DOXYGEN
if test -n "$DOXYGEN"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $DOXYGEN" >&5
$as_echo "$DOXYGEN" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
else
DOXYGEN="$doxygen"
fi
# Check whether --with-expat was given.
if test "${with_expat+set}" = set; then :
withval=$with_expat; svn_lib_expat="$withval"
else
svn_lib_expat="::expat"
fi
# APR-util accepts "builtin" as an argument to this option so if the user
# passed "builtin" pretend the user didn't specify the --with-expat option
# at all. Expat will (hopefully) be found in apr-util.
test "_$svn_lib_expat" = "_builtin" && svn_lib_expat="::expat"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Expat" >&5
$as_echo_n "checking for Expat... " >&6; }
if test -n "`echo "$svn_lib_expat" | $EGREP ":.*:"`"; then
SVN_XML_INCLUDES=""
for i in `echo "$svn_lib_expat" | $SED -e "s/\([^:]*\):.*/\1/"`; do
SVN_XML_INCLUDES="$SVN_XML_INCLUDES -I$i"
done
SVN_XML_INCLUDES="${SVN_XML_INCLUDES## }"
for l in `echo "$svn_lib_expat" | $SED -e "s/.*:\([^:]*\):.*/\1/"`; do
LDFLAGS="$LDFLAGS -L$l"
done
for l in `echo "$svn_lib_expat" | $SED -e "s/.*:\([^:]*\)/\1/"`; do
SVN_XML_LIBS="$SVN_XML_LIBS -l$l"
done
SVN_XML_LIBS="${SVN_XML_LIBS## }"
old_CPPFLAGS="$CPPFLAGS"
old_LIBS="$LIBS"
CPPFLAGS="$CPPFLAGS $SVN_XML_INCLUDES"
LIBS="$LIBS $SVN_XML_LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <expat.h>
int main()
{XML_ParserCreate(NULL);}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
svn_lib_expat="yes"
else
svn_lib_expat="no"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS="$old_LIBS"
if test "$svn_lib_expat" = "yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
SVN_XML_INCLUDES=""
SVN_XML_LIBS=""
CPPFLAGS="$CPPFLAGS $SVN_APRUTIL_INCLUDES"
if test "$enable_all_static" != "yes"; then
SVN_APRUTIL_LIBS="$SVN_APRUTIL_LIBS `$apu_config --libs`"
fi
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <expat.h>
int main()
{XML_ParserCreate(NULL);}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
svn_lib_expat="yes"
else
svn_lib_expat="no"
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
if test "$svn_lib_expat" = "yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Expat found amongst libraries used by APR-Util, but Subversion libraries might be needlessly linked against additional unused libraries. It can be avoided by specifying exact location of Expat in argument of --with-expat option." >&5
$as_echo "$as_me: WARNING: Expat found amongst libraries used by APR-Util, but Subversion libraries might be needlessly linked against additional unused libraries. It can be avoided by specifying exact location of Expat in argument of --with-expat option." >&2;}
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
as_fn_error $? "Expat not found" "$LINENO" 5
fi
fi
CPPFLAGS="$old_CPPFLAGS"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
if test "$svn_lib_expat" = "yes"; then
as_fn_error $? "--with-expat option requires argument" "$LINENO" 5
elif test "$svn_lib_expat" = "no"; then
as_fn_error $? "Expat is required" "$LINENO" 5
else
as_fn_error $? "Invalid syntax of argument of --with-expat option" "$LINENO" 5
fi
fi
# Berkeley DB on SCO OpenServer needs -lsocket
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for socket in -lsocket" >&5
$as_echo_n "checking for socket in -lsocket... " >&6; }
if ${ac_cv_lib_socket_socket+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lsocket $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char socket ();
int
main ()
{
return socket ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_socket_socket=yes
else
ac_cv_lib_socket_socket=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_socket" >&5
$as_echo "$ac_cv_lib_socket_socket" >&6; }
if test "x$ac_cv_lib_socket_socket" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_LIBSOCKET 1
_ACEOF
LIBS="-lsocket $LIBS"
fi
# Build the BDB filesystem library only if we have an appropriate
# version of Berkeley DB.
case "$host" in
powerpc-apple-darwin*)
# Berkeley DB 4.0 does not work on OS X.
SVN_FS_WANT_DB_MAJOR=4
SVN_FS_WANT_DB_MINOR=1
SVN_FS_WANT_DB_PATCH=25
;;
*)
SVN_FS_WANT_DB_MAJOR=4
SVN_FS_WANT_DB_MINOR=0
SVN_FS_WANT_DB_PATCH=14
;;
esac
db_alt_version="5.x"
# Look for libdb4.so first:
db_version=$SVN_FS_WANT_DB_MAJOR.$SVN_FS_WANT_DB_MINOR.$SVN_FS_WANT_DB_PATCH
# Check whether --with-berkeley-db was given.
if test "${with_berkeley_db+set}" = set; then :
withval=$with_berkeley_db;
if test "$withval" = "no"; then
bdb_status=skip
elif test "$withval" = "yes"; then
apu_db_version="`$apu_config --db-version`"
if test $? -ne 0; then
as_fn_error $? "Can't determine whether apr-util is linked against a
proper version of Berkeley DB." "$LINENO" 5
fi
if test "$withval" = "yes"; then
if test "$apu_db_version" -lt "4"; then
as_fn_error $? "APR-UTIL was linked against Berkeley DB version $apu_db_version,
while version 4 or higher is required. Reinstall
APR-UTIL with the appropriate options." "$LINENO" 5
fi
bdb_status=required
elif test "$apu_found" != "reconfig"; then
if test "$apu_db_version" -lt 4; then
as_fn_error $? "APR-UTIL was installed independently, it won't be
possible to use the specified Berkeley DB: $withval" "$LINENO" 5
fi
bdb_status=required
fi
else
if echo "$withval" | $EGREP ":.*:.*:" > /dev/null; then
svn_berkeley_db_header="`echo "$withval" | $SED -e "s/\([^:]*\):.*/\1/"`"
SVN_DB_INCLUDES=""
for i in `echo "$withval" | $SED -e "s/.*:\([^:]*\):[^:]*:.*/\1/"`; do
SVN_DB_INCLUDES="$SVN_DB_INCLUDES -I$i"
done
SVN_DB_INCLUDES="${SVN_DB_INCLUDES## }"
for l in `echo "$withval" | $SED -e "s/.*:[^:]*:\([^:]*\):.*/\1/"`; do
LDFLAGS="$LDFLAGS `
input_flags="-L$l"
output_flags=""
filtered_dirs="/lib /lib64 /usr/lib /usr/lib64"
for flag in $input_flags; do
filter="no"
for dir in $filtered_dirs; do
if test "$flag" = "-L$dir" || test "$flag" = "-L$dir/"; then
filter="yes"
break
fi
done
if test "$filter" = "no"; then
output_flags="$output_flags $flag"
fi
done
if test -n "$output_flags"; then
printf "%s" "${output_flags# }"
fi
`"
done
SVN_DB_LIBS=""
for l in `echo "$withval" | $SED -e "s/.*:\([^:]*\)/\1/"`; do
SVN_DB_LIBS="$SVN_DB_LIBS -l$l"
done
SVN_DB_LIBS="${SVN_DB_LIBS## }"
bdb_status=required
else
as_fn_error $? "Invalid syntax of argument of --with-berkeley-db option" "$LINENO" 5
fi
fi
else
# No --with-berkeley-db option:
#
# Check if APR-UTIL is providing the correct Berkeley DB version
# for us.
#
apu_db_version="`$apu_config --db-version`"
if test $? -ne 0; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Detected older version of APR-UTIL, trying to determine
whether apr-util is linked against Berkeley DB
$db_version" >&5
$as_echo "$as_me: WARNING: Detected older version of APR-UTIL, trying to determine
whether apr-util is linked against Berkeley DB
$db_version" >&2;}
bdb_status=try-link
elif test "$apu_db_version" -lt "4"; then
bdb_status=skip
else
bdb_status=try-link
fi
fi
if test "$bdb_status" = "skip"; then
svn_lib_berkeley_db=no
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for availability of Berkeley DB" >&5
$as_echo_n "checking for availability of Berkeley DB... " >&6; }
# Check whether --enable-bdb6 was given.
if test "${enable_bdb6+set}" = set; then :
enableval=$enable_bdb6; enable_bdb6=$enableval
else
enable_bdb6=unspecified
fi
svn_lib_berkeley_db_try_save_cppflags="$CPPFLAGS"
svn_lib_berkeley_db_try_save_libs="$LIBS"
svn_check_berkeley_db_major=$SVN_FS_WANT_DB_MAJOR
svn_check_berkeley_db_minor=$SVN_FS_WANT_DB_MINOR
svn_check_berkeley_db_patch=$SVN_FS_WANT_DB_PATCH
enable_bdb6=$enable_bdb6
if test -z "$SVN_DB_LIBS"; then
# We pass --dbm-libs here since Debian has modified apu-config not
# to return -ldb unless --dbm-libs is passed. This may also produce
# extra output beyond -ldb but since we're only filtering for -ldb
# it won't matter to us. However, --dbm-libs was added to apu-config
# in 1.3.8 so it's possible the version we have doesn't support it
# so fallback without it if we get an error.
svn_db_libs_prefiltered="`$apu_config --libs --dbm-libs`"
if test $? -ne 0; then
svn_db_libs_prefiltered="`$apu_config --libs`"
fi
# Extract only the -ldb.* flag from the libs supplied by apu-config
# Otherwise we get bit by the fact that expat might not be built yet
# Or that it resides in a non-standard location which we would have
# to compensate with using something like -R`$apu_config --prefix`/lib.
#
SVN_DB_LIBS="`echo \"$svn_db_libs_prefiltered\" | $SED -e 's/.*\(-ldb[^[:space:]]*\).*/\1/' | $EGREP -- '-ldb[^[:space:]]*'`"
fi
CPPFLAGS="$SVN_DB_INCLUDES $SVN_APRUTIL_INCLUDES $CPPFLAGS"
LIBS="`$apu_config --ldflags` $SVN_DB_LIBS $LIBS"
if test -n "$svn_berkeley_db_header"; then
SVN_DB_HEADER="#include <$svn_berkeley_db_header>"
svn_db_header="#include <$svn_berkeley_db_header>"
else
SVN_DB_HEADER="#include <apu_want.h>"
svn_db_header="#define APU_WANT_DB
#include <apu_want.h>"
fi
if test "$cross_compiling" = yes; then :
svn_have_berkeley_db=yes
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <string.h>
#include <stdlib.h>
$svn_db_header
int main ()
{
int major, minor, patch;
db_version (&major, &minor, &patch);
/* Sanity check: ensure that db.h constants actually match the db library */
if (major != DB_VERSION_MAJOR
|| minor != DB_VERSION_MINOR
|| patch != DB_VERSION_PATCH)
exit (1);
/* Block Berkeley DB 6, because (a) we haven't tested with it, (b) 6.0.20
and newer are under the AGPL, and we want use of AGPL dependencies to be
opt-in. */
if (major >= 6 && strcmp("$enable_bdb6", "yes"))
exit(2);
/* Run-time check: ensure the library claims to be the correct version. */
if (major < $svn_check_berkeley_db_major)
exit (1);
if (major > $svn_check_berkeley_db_major)
exit (0);
if (minor < $svn_check_berkeley_db_minor)
exit (1);
if (minor > $svn_check_berkeley_db_minor)
exit (0);
if (patch >= $svn_check_berkeley_db_patch)
exit (0);
else
exit (1);
}
_ACEOF
if ac_fn_c_try_run "$LINENO"; then :
svn_have_berkeley_db=yes
else
rc=$?
svn_have_berkeley_db=no
if test $rc = 2; then
svn_have_berkeley_db=no6
fi
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
CPPFLAGS="$svn_lib_berkeley_db_try_save_cppflags"
LIBS="$svn_lib_berkeley_db_try_save_libs"
if test "$svn_have_berkeley_db" = "yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
svn_lib_berkeley_db=yes
else
if test "$svn_have_berkeley_db" = "no6"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no (found version 6, but --enable-bdb6 not specified)" >&5
$as_echo "no (found version 6, but --enable-bdb6 not specified)" >&6; }
# A warning will be printed at the end of configure.ac.
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
svn_lib_berkeley_db=no
if test "$bdb_status" = "required"; then
as_fn_error $? "Berkeley DB $db_version or $db_alt_version wasn't found." "$LINENO" 5
fi
fi
fi
cat >>confdefs.h <<_ACEOF
#define SVN_FS_WANT_DB_MAJOR $SVN_FS_WANT_DB_MAJOR
_ACEOF
cat >>confdefs.h <<_ACEOF
#define SVN_FS_WANT_DB_MINOR $SVN_FS_WANT_DB_MINOR
_ACEOF
cat >>confdefs.h <<_ACEOF
#define SVN_FS_WANT_DB_PATCH $SVN_FS_WANT_DB_PATCH
_ACEOF
# Check whether --with-sasl was given.
if test "${with_sasl+set}" = set; then :
withval=$with_sasl;
with_sasl="$withval"
required="yes"
else
with_sasl="yes"
required="no"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to look for SASL" >&5
$as_echo_n "checking whether to look for SASL... " >&6; }
if test "${with_sasl}" = "no"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
svn_lib_sasl=no
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
saved_LDFLAGS="$LDFLAGS"
saved_CPPFLAGS="$CPPFLAGS"
if test "$with_sasl" = "yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: Looking in default locations" >&5
$as_echo "$as_me: Looking in default locations" >&6;}
ac_fn_c_check_header_mongrel "$LINENO" "sasl/sasl.h" "ac_cv_header_sasl_sasl_h" "$ac_includes_default"
if test "x$ac_cv_header_sasl_sasl_h" = xyes; then :
ac_fn_c_check_header_mongrel "$LINENO" "sasl/saslutil.h" "ac_cv_header_sasl_saslutil_h" "$ac_includes_default"
if test "x$ac_cv_header_sasl_saslutil_h" = xyes; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for prop_get in -lsasl2" >&5
$as_echo_n "checking for prop_get in -lsasl2... " >&6; }
if ${ac_cv_lib_sasl2_prop_get+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lsasl2 $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char prop_get ();
int
main ()
{
return prop_get ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_sasl2_prop_get=yes
else
ac_cv_lib_sasl2_prop_get=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sasl2_prop_get" >&5
$as_echo "$ac_cv_lib_sasl2_prop_get" >&6; }
if test "x$ac_cv_lib_sasl2_prop_get" = xyes; then :
svn_lib_sasl=yes
else
svn_lib_sasl=no
fi
else
svn_lib_sasl=no
fi
else
svn_lib_sasl=no
fi
if test "$svn_lib_sasl" = "no"; then
with_sasl="/usr/local"
fi
else
svn_lib_sasl=no
fi
if test "$svn_lib_sasl" = "no"; then
SVN_SASL_INCLUDES="-I${with_sasl}/include"
CPPFLAGS="$CPPFLAGS $SVN_SASL_INCLUDES"
LDFLAGS="$LDFLAGS `
input_flags="-L${with_sasl}/lib"
output_flags=""
filtered_dirs="/lib /lib64 /usr/lib /usr/lib64"
for flag in $input_flags; do
filter="no"
for dir in $filtered_dirs; do
if test "$flag" = "-L$dir" || test "$flag" = "-L$dir/"; then
filter="yes"
break
fi
done
if test "$filter" = "no"; then
output_flags="$output_flags $flag"
fi
done
if test -n "$output_flags"; then
printf "%s" "${output_flags# }"
fi
`"
ac_fn_c_check_header_mongrel "$LINENO" "sasl/sasl.h" "ac_cv_header_sasl_sasl_h" "$ac_includes_default"
if test "x$ac_cv_header_sasl_sasl_h" = xyes; then :
ac_fn_c_check_header_mongrel "$LINENO" "sasl/saslutil.h" "ac_cv_header_sasl_saslutil_h" "$ac_includes_default"
if test "x$ac_cv_header_sasl_saslutil_h" = xyes; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for prop_get in -lsasl2" >&5
$as_echo_n "checking for prop_get in -lsasl2... " >&6; }
if ${ac_cv_lib_sasl2_prop_get+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lsasl2 $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char prop_get ();
int
main ()
{
return prop_get ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_sasl2_prop_get=yes
else
ac_cv_lib_sasl2_prop_get=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sasl2_prop_get" >&5
$as_echo "$ac_cv_lib_sasl2_prop_get" >&6; }
if test "x$ac_cv_lib_sasl2_prop_get" = xyes; then :
svn_lib_sasl=yes
else
svn_lib_sasl=no
fi
else
svn_lib_sasl=no
fi
else
svn_lib_sasl=no
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for availability of Cyrus SASL v2" >&5
$as_echo_n "checking for availability of Cyrus SASL v2... " >&6; }
if test "$svn_lib_sasl" = "yes"; then
SVN_SASL_LIBS="-lsasl2"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
if test "$required" = "yes"; then
as_fn_error $? "Could not find Cyrus SASL v2" "$LINENO" 5
fi
SVN_SASL_INCLUDES=""
LDFLAGS="$saved_LDFLAGS"
fi
CPPFLAGS="$saved_CPPFLAGS"
fi
if test "$svn_lib_sasl" = "yes"; then
$as_echo "#define SVN_HAVE_SASL 1" >>confdefs.h
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Mach-O dynamic module iteration functions" >&5
$as_echo_n "checking for Mach-O dynamic module iteration functions... " >&6; }
if test "$cross_compiling" = yes; then :
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "cannot run test program while cross compiling
See \`config.log' for more details" "$LINENO" 5; }
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <mach-o/dyld.h>
#include <mach-o/loader.h>
int
main ()
{
const struct mach_header *header = _dyld_get_image_header(0);
const char *name = _dyld_get_image_name(0);
if (name && header) return 0;
return 1;
;
return 0;
}
_ACEOF
if ac_fn_c_try_run "$LINENO"; then :
$as_echo "#define SVN_HAVE_MACHO_ITERATE 1" >>confdefs.h
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Mac OS property list utilities" >&5
$as_echo_n "checking for Mac OS property list utilities... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <AvailabilityMacros.h>
#if !defined(MAC_OS_X_VERSION_MAX_ALLOWED) \
|| !defined(MAC_OS_X_VERSION_10_0) \
|| (MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_0)
#error ProperyList API unavailable.
#endif
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
LIBS="$LIBS -framework CoreFoundation"
$as_echo "#define SVN_HAVE_MACOS_PLIST 1" >>confdefs.h
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
# Check whether --enable-keychain was given.
if test "${enable_keychain+set}" = set; then :
enableval=$enable_keychain; enable_keychain=$enableval
else
enable_keychain=yes
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Mac OS KeyChain Services" >&5
$as_echo_n "checking for Mac OS KeyChain Services... " >&6; }
if test "$enable_keychain" = "yes"; then
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <AvailabilityMacros.h>
#if !defined(MAC_OS_X_VERSION_MAX_ALLOWED) \
|| !defined(MAC_OS_X_VERSION_10_2) \
|| (MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_2)
#error KeyChain API unavailable.
#endif
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
LIBS="$LIBS -framework Security"
LIBS="$LIBS -framework CoreServices"
$as_echo "#define SVN_HAVE_KEYCHAIN_SERVICES 1" >>confdefs.h
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
enable_keychain=no
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether APR has support for DSOs" >&5
$as_echo_n "checking whether APR has support for DSOs... " >&6; }
old_CPPFLAGS="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $SVN_APR_INCLUDES"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <apr.h>
#if !APR_HAS_DSO
#error
#endif
_ACEOF
if ac_fn_c_try_cpp "$LINENO"; then :
APR_HAS_DSO="yes"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
APR_HAS_DSO="no"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
rm -f conftest.err conftest.i conftest.$ac_ext
CPPFLAGS="$old_CPPFLAGS"
if test -n "$PKG_CONFIG"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for D-Bus .pc file" >&5
$as_echo_n "checking for D-Bus .pc file... " >&6; }
if $PKG_CONFIG --exists dbus-1; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
old_CPPFLAGS="$CPPFLAGS"
old_LIBS="$LIBS"
DBUS_CPPFLAGS="`$PKG_CONFIG --cflags dbus-1`"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking D-Bus version" >&5
$as_echo_n "checking D-Bus version... " >&6; }
DBUS_VERSION="`$PKG_CONFIG --modversion dbus-1`"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $DBUS_VERSION" >&5
$as_echo "$DBUS_VERSION" >&6; }
# D-Bus 0.* requires DBUS_API_SUBJECT_TO_CHANGE
if test -n "`echo "$DBUS_VERSION" | $EGREP '^0\.[[:digit:]]+'`"; then
DBUS_CPPFLAGS="$DBUS_CPPFLAGS -DDBUS_API_SUBJECT_TO_CHANGE"
fi
DBUS_LIBS="`$PKG_CONFIG --libs dbus-1`"
CPPFLAGS="$CPPFLAGS $DBUS_CPPFLAGS"
LIBS="$LIBS $DBUS_LIBS"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for D-Bus" >&5
$as_echo_n "checking for D-Bus... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <dbus/dbus.h>
int main()
{dbus_bus_get(DBUS_BUS_SESSION, NULL);}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
HAVE_DBUS="yes"
else
HAVE_DBUS="no"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
if test "$HAVE_DBUS" = "yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
CPPFLAGS="$old_CPPFLAGS"
LIBS="$old_LIBS"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
# Check whether --with-gpg_agent was given.
if test "${with_gpg_agent+set}" = set; then :
withval=$with_gpg_agent;
else
with_gpg_agent=yes
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support GPG-Agent" >&5
$as_echo_n "checking whether to support GPG-Agent... " >&6; }
if test "$with_gpg_agent" = "yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
$as_echo "#define SVN_HAVE_GPG_AGENT 1" >>confdefs.h
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
# Check whether --with-gnome_keyring was given.
if test "${with_gnome_keyring+set}" = set; then :
withval=$with_gnome_keyring; with_gnome_keyring="$withval"
else
with_gnome_keyring=auto
fi
found_gnome_keyring=no
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to look for GNOME Keyring" >&5
$as_echo_n "checking whether to look for GNOME Keyring... " >&6; }
if test "$with_gnome_keyring" != "no"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
if test "$svn_enable_shared" = "yes"; then
if test "$APR_HAS_DSO" = "yes"; then
if test -n "$PKG_CONFIG"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GLib and GNOME Keyring .pc files" >&5
$as_echo_n "checking for GLib and GNOME Keyring .pc files... " >&6; }
if $PKG_CONFIG --exists glib-2.0 gnome-keyring-1; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
old_CPPFLAGS="$CPPFLAGS"
SVN_GNOME_KEYRING_INCLUDES="`$PKG_CONFIG --cflags glib-2.0 gnome-keyring-1`"
CPPFLAGS="$CPPFLAGS $SVN_GNOME_KEYRING_INCLUDES"
ac_fn_c_check_header_mongrel "$LINENO" "gnome-keyring.h" "ac_cv_header_gnome_keyring_h" "$ac_includes_default"
if test "x$ac_cv_header_gnome_keyring_h" = xyes; then :
found_gnome_keyring=yes
else
found_gnome_keyring=no
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNOME Keyring" >&5
$as_echo_n "checking for GNOME Keyring... " >&6; }
if test "$found_gnome_keyring" = "yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
$as_echo "#define SVN_HAVE_GNOME_KEYRING 1" >>confdefs.h
CPPFLAGS="$old_CPPFLAGS"
SVN_GNOME_KEYRING_LIBS="`$PKG_CONFIG --libs glib-2.0 gnome-keyring-1`"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
if test "$with_gnome_keyring" = "yes"; then
as_fn_error $? "cannot find GNOME Keyring" "$LINENO" 5
fi
fi
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
if test "$with_gnome_keyring" = "yes"; then
as_fn_error $? "cannot find GLib and GNOME Keyring .pc files." "$LINENO" 5
else
with_gnome_keyring=no
fi
fi
else
if test "$with_gnome_keyring" = "yes"; then
as_fn_error $? "cannot find pkg-config. GNOME Keyring requires this." "$LINENO" 5
else
with_gnome_keyring=no
fi
fi
else
if test "$with_gnome_keyring" = "yes"; then
as_fn_error $? "APR does not have support for DSOs. GNOME Keyring requires this." "$LINENO" 5
else
with_gnome_keyring=no
fi
fi
else
if test "$with_gnome_keyring" = "yes"; then
as_fn_error $? "--with-gnome-keyring conflicts with --disable-shared" "$LINENO" 5
else
with_gnome_keyring=no
fi
fi
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
# Check whether --enable-ev2-impl was given.
if test "${enable_ev2_impl+set}" = set; then :
enableval=$enable_ev2_impl; enable_ev2_impl=$enableval
else
enable_ev2_impl=no
fi
if test "$enable_ev2_impl" = "yes"; then
$as_echo "#define ENABLE_EV2_IMPL 1" >>confdefs.h
fi
# Check whether --enable-nls was given.
if test "${enable_nls+set}" = set; then :
enableval=$enable_nls; enable_nls=$enableval
else
enable_nls=yes
fi
USE_NLS="no"
if test "$enable_nls" = "yes"; then
# Extract the first word of "msgfmt", so it can be a program name with args.
set dummy msgfmt; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_MSGFMT+:} false; then :
$as_echo_n "(cached) " >&6
else
case $MSGFMT in
[\\/]* | ?:[\\/]*)
ac_cv_path_MSGFMT="$MSGFMT" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_MSGFMT="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
test -z "$ac_cv_path_MSGFMT" && ac_cv_path_MSGFMT="none"
;;
esac
fi
MSGFMT=$ac_cv_path_MSGFMT
if test -n "$MSGFMT"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MSGFMT" >&5
$as_echo "$MSGFMT" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
# Extract the first word of "msgmerge", so it can be a program name with args.
set dummy msgmerge; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_MSGMERGE+:} false; then :
$as_echo_n "(cached) " >&6
else
case $MSGMERGE in
[\\/]* | ?:[\\/]*)
ac_cv_path_MSGMERGE="$MSGMERGE" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_MSGMERGE="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
test -z "$ac_cv_path_MSGMERGE" && ac_cv_path_MSGMERGE="none"
;;
esac
fi
MSGMERGE=$ac_cv_path_MSGMERGE
if test -n "$MSGMERGE"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MSGMERGE" >&5
$as_echo "$MSGMERGE" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
# Extract the first word of "xgettext", so it can be a program name with args.
set dummy xgettext; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_XGETTEXT+:} false; then :
$as_echo_n "(cached) " >&6
else
case $XGETTEXT in
[\\/]* | ?:[\\/]*)
ac_cv_path_XGETTEXT="$XGETTEXT" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_XGETTEXT="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
test -z "$ac_cv_path_XGETTEXT" && ac_cv_path_XGETTEXT="none"
;;
esac
fi
XGETTEXT=$ac_cv_path_XGETTEXT
if test -n "$XGETTEXT"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $XGETTEXT" >&5
$as_echo "$XGETTEXT" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "$MSGFMT" != "none"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing bindtextdomain" >&5
$as_echo_n "checking for library containing bindtextdomain... " >&6; }
if ${ac_cv_search_bindtextdomain+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char bindtextdomain ();
int
main ()
{
return bindtextdomain ();
;
return 0;
}
_ACEOF
for ac_lib in '' intl; do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_search_bindtextdomain=$ac_res
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
if ${ac_cv_search_bindtextdomain+:} false; then :
break
fi
done
if ${ac_cv_search_bindtextdomain+:} false; then :
else
ac_cv_search_bindtextdomain=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_bindtextdomain" >&5
$as_echo "$ac_cv_search_bindtextdomain" >&6; }
ac_res=$ac_cv_search_bindtextdomain
if test "$ac_res" != no; then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
else
enable_nls="no"
fi
if test "$enable_nls" = "no"; then
# Destroy the cached result so we can test again
unset ac_cv_search_bindtextdomain
# On some systems, libintl needs libiconv to link properly,
# so try again with -liconv.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing bindtextdomain" >&5
$as_echo_n "checking for library containing bindtextdomain... " >&6; }
if ${ac_cv_search_bindtextdomain+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char bindtextdomain ();
int
main ()
{
return bindtextdomain ();
;
return 0;
}
_ACEOF
for ac_lib in '' intl; do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib -liconv $ac_func_search_save_LIBS"
fi
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_search_bindtextdomain=$ac_res
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
if ${ac_cv_search_bindtextdomain+:} false; then :
break
fi
done
if ${ac_cv_search_bindtextdomain+:} false; then :
else
ac_cv_search_bindtextdomain=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_bindtextdomain" >&5
$as_echo "$ac_cv_search_bindtextdomain" >&6; }
ac_res=$ac_cv_search_bindtextdomain
if test "$ac_res" != no; then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
enable_nls="yes"
# This is here so that -liconv ends up in LIBS
# if it worked with -liconv.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libiconv_open in -liconv" >&5
$as_echo_n "checking for libiconv_open in -liconv... " >&6; }
if ${ac_cv_lib_iconv_libiconv_open+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-liconv $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char libiconv_open ();
int
main ()
{
return libiconv_open ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_iconv_libiconv_open=yes
else
ac_cv_lib_iconv_libiconv_open=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_iconv_libiconv_open" >&5
$as_echo "$ac_cv_lib_iconv_libiconv_open" >&6; }
if test "x$ac_cv_lib_iconv_libiconv_open" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_LIBICONV 1
_ACEOF
LIBS="-liconv $LIBS"
fi
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: bindtextdomain() not found. Disabling NLS." >&5
$as_echo "$as_me: WARNING: bindtextdomain() not found. Disabling NLS." >&2;}
enable_nls="no"
fi
fi
if test "$enable_nls" = "yes"; then
$as_echo "#define ENABLE_NLS 1" >>confdefs.h
USE_NLS="yes"
fi
fi
fi
GETTEXT_CODESET=\#
NO_GETTEXT_CODESET=\#
if test $USE_NLS = "yes"; then
for ac_func in bind_textdomain_codeset
do :
ac_fn_c_check_func "$LINENO" "bind_textdomain_codeset" "ac_cv_func_bind_textdomain_codeset"
if test "x$ac_cv_func_bind_textdomain_codeset" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_BIND_TEXTDOMAIN_CODESET 1
_ACEOF
GETTEXT_CODESET=""
else
NO_GETTEXT_CODESET=""
fi
done
fi
# Check if we are using GNU gettext.
GNU_GETTEXT=no
MSGFMTFLAGS=''
if test $USE_NLS = "yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if we are using GNU gettext" >&5
$as_echo_n "checking if we are using GNU gettext... " >&6; }
if $MSGFMT --version 2>&1 | $EGREP GNU > /dev/null; then
GNU_GETTEXT=yes
MSGFMTFLAGS='-c'
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $GNU_GETTEXT" >&5
$as_echo "$GNU_GETTEXT" >&6; }
fi
libmagic_found=no
# Check whether --with-libmagic was given.
if test "${with_libmagic+set}" = set; then :
withval=$with_libmagic;
if test "$withval" = "yes" ; then
ac_fn_c_check_header_mongrel "$LINENO" "magic.h" "ac_cv_header_magic_h" "$ac_includes_default"
if test "x$ac_cv_header_magic_h" = xyes; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for magic_open in -lmagic" >&5
$as_echo_n "checking for magic_open in -lmagic... " >&6; }
if ${ac_cv_lib_magic_magic_open+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lmagic $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char magic_open ();
int
main ()
{
return magic_open ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_magic_magic_open=yes
else
ac_cv_lib_magic_magic_open=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_magic_magic_open" >&5
$as_echo "$ac_cv_lib_magic_magic_open" >&6; }
if test "x$ac_cv_lib_magic_magic_open" = xyes; then :
libmagic_found="builtin"
fi
fi
libmagic_prefix="the default locations"
elif test "$withval" != "no"; then
libmagic_prefix=$withval
save_cppflags="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS -I$libmagic_prefix/include"
for ac_header in magic.h
do :
ac_fn_c_check_header_mongrel "$LINENO" "magic.h" "ac_cv_header_magic_h" "$ac_includes_default"
if test "x$ac_cv_header_magic_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_MAGIC_H 1
_ACEOF
save_ldflags="$LDFLAGS"
LDFLAGS="-L$libmagic_prefix/lib $LDFLAGS"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for magic_open in -lmagic" >&5
$as_echo_n "checking for magic_open in -lmagic... " >&6; }
if ${ac_cv_lib_magic_magic_open+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lmagic $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char magic_open ();
int
main ()
{
return magic_open ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_magic_magic_open=yes
else
ac_cv_lib_magic_magic_open=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_magic_magic_open" >&5
$as_echo "$ac_cv_lib_magic_magic_open" >&6; }
if test "x$ac_cv_lib_magic_magic_open" = xyes; then :
libmagic_found="yes"
fi
LDFLAGS="$save_ldflags"
fi
done
CPPFLAGS="$save_cppflags"
fi
if test "$withval" != "no" && test "$libmagic_found" = "no"; then
as_fn_error $? "--with-libmagic requested, but libmagic not found at $libmagic_prefix" "$LINENO" 5
fi
else
ac_fn_c_check_header_mongrel "$LINENO" "magic.h" "ac_cv_header_magic_h" "$ac_includes_default"
if test "x$ac_cv_header_magic_h" = xyes; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for magic_open in -lmagic" >&5
$as_echo_n "checking for magic_open in -lmagic... " >&6; }
if ${ac_cv_lib_magic_magic_open+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lmagic $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char magic_open ();
int
main ()
{
return magic_open ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_magic_magic_open=yes
else
ac_cv_lib_magic_magic_open=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_magic_magic_open" >&5
$as_echo "$ac_cv_lib_magic_magic_open" >&6; }
if test "x$ac_cv_lib_magic_magic_open" = xyes; then :
libmagic_found="builtin"
fi
fi
fi
if test "$libmagic_found" != "no"; then
$as_echo "#define SVN_HAVE_LIBMAGIC 1" >>confdefs.h
SVN_MAGIC_LIBS="-lmagic"
fi
if test "$libmagic_found" = "yes"; then
SVN_MAGIC_INCLUDES="-I$libmagic_prefix/include"
LDFLAGS="$LDFLAGS `
input_flags="-L$libmagic_prefix/lib"
output_flags=""
filtered_dirs="/lib /lib64 /usr/lib /usr/lib64"
for flag in $input_flags; do
filter="no"
for dir in $filtered_dirs; do
if test "$flag" = "-L$dir" || test "$flag" = "-L$dir/"; then
filter="yes"
break
fi
done
if test "$filter" = "no"; then
output_flags="$output_flags $flag"
fi
done
if test -n "$output_flags"; then
printf "%s" "${output_flags# }"
fi
`"
fi
# Check whether --with-kwallet was given.
if test "${with_kwallet+set}" = set; then :
withval=$with_kwallet; svn_lib_kwallet="$withval"
else
svn_lib_kwallet=no
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to look for KWallet" >&5
$as_echo_n "checking whether to look for KWallet... " >&6; }
if test "$svn_lib_kwallet" != "no"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
if test "$svn_enable_shared" = "yes"; then
if test "$APR_HAS_DSO" = "yes"; then
if test -n "$PKG_CONFIG"; then
if test "$HAVE_DBUS" = "yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for QtCore, QtDBus, QtGui" >&5
$as_echo_n "checking for QtCore, QtDBus, QtGui... " >&6; }
if $PKG_CONFIG --exists QtCore QtDBus QtGui; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
if test "$svn_lib_kwallet" != "yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for kde4-config" >&5
$as_echo_n "checking for kde4-config... " >&6; }
KDE4_CONFIG="$svn_lib_kwallet/bin/kde4-config"
if test -f "$KDE4_CONFIG" && test -x "$KDE4_CONFIG"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
KDE4_CONFIG=""
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
else
# Extract the first word of "kde4-config", so it can be a program name with args.
set dummy kde4-config; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_KDE4_CONFIG+:} false; then :
$as_echo_n "(cached) " >&6
else
case $KDE4_CONFIG in
[\\/]* | ?:[\\/]*)
ac_cv_path_KDE4_CONFIG="$KDE4_CONFIG" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_KDE4_CONFIG="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
;;
esac
fi
KDE4_CONFIG=$ac_cv_path_KDE4_CONFIG
if test -n "$KDE4_CONFIG"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $KDE4_CONFIG" >&5
$as_echo "$KDE4_CONFIG" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -n "$KDE4_CONFIG"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for KWallet" >&5
$as_echo_n "checking for KWallet... " >&6; }
old_CXXFLAGS="$CXXFLAGS"
old_LDFLAGS="$LDFLAGS"
old_LIBS="$LIBS"
for d in `$PKG_CONFIG --cflags QtCore QtDBus QtGui`; do
if test -n "`echo "$d" | $EGREP -- '^-D[^[:space:]]*'`"; then
CPPFLAGS="$CPPFLAGS $d"
fi
done
qt_include_dirs="`$PKG_CONFIG --cflags-only-I QtCore QtDBus QtGui`"
kde_dir="`$KDE4_CONFIG --prefix`"
SVN_KWALLET_INCLUDES="$DBUS_CPPFLAGS $qt_include_dirs -I$kde_dir/include"
qt_libs_other_options="`$PKG_CONFIG --libs-only-other QtCore QtDBus QtGui`"
SVN_KWALLET_LIBS="$DBUS_LIBS -lQtCore -lQtDBus -lQtGui -lkdecore -lkdeui $qt_libs_other_options"
CXXFLAGS="$CXXFLAGS $SVN_KWALLET_INCLUDES"
LIBS="$LIBS $SVN_KWALLET_LIBS"
qt_lib_dirs="`$PKG_CONFIG --libs-only-L QtCore QtDBus QtGui`"
kde_lib_suffix="`$KDE4_CONFIG --libsuffix`"
LDFLAGS="$old_LDFLAGS `
input_flags="$qt_lib_dirs -L$kde_dir/lib$kde_lib_suffix"
output_flags=""
filtered_dirs="/lib /lib64 /usr/lib /usr/lib64"
for flag in $input_flags; do
filter="no"
for dir in $filtered_dirs; do
if test "$flag" = "-L$dir" || test "$flag" = "-L$dir/"; then
filter="yes"
break
fi
done
if test "$filter" = "no"; then
output_flags="$output_flags $flag"
fi
done
if test -n "$output_flags"; then
printf "%s" "${output_flags# }"
fi
`"
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <kwallet.h>
int main()
{KWallet::Wallet::walletList();}
_ACEOF
if ac_fn_cxx_try_link "$LINENO"; then :
svn_lib_kwallet="yes"
else
svn_lib_kwallet="no"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
if test "$svn_lib_kwallet" = "yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
CXXFLAGS="$old_CXXFLAGS"
LIBS="$old_LIBS"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
as_fn_error $? "cannot find KWallet" "$LINENO" 5
fi
else
as_fn_error $? "cannot find kde4-config" "$LINENO" 5
fi
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
as_fn_error $? "cannot find QtCore, QtDBus, QtGui" "$LINENO" 5
fi
else
as_fn_error $? "cannot find D-Bus" "$LINENO" 5
fi
else
as_fn_error $? "cannot find pkg-config" "$LINENO" 5
fi
else
as_fn_error $? "APR does not have support for DSOs" "$LINENO" 5
fi
else
as_fn_error $? "--with-kwallet conflicts with --disable-shared" "$LINENO" 5
fi
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "$svn_lib_kwallet" = "yes"; then
$as_echo "#define SVN_HAVE_KWALLET 1" >>confdefs.h
fi
# Check whether --enable-plaintext-password-storage was given.
if test "${enable_plaintext_password_storage+set}" = set; then :
enableval=$enable_plaintext_password_storage;
if test "$enableval" = "no"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: Disabling plaintext password/passphrase storage" >&5
$as_echo "$as_me: Disabling plaintext password/passphrase storage" >&6;}
$as_echo "#define SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE 1" >>confdefs.h
fi
fi
INSTALL_STATIC_RULES="install-bin install-docs"
INSTALL_RULES="install-fsmod-lib install-ramod-lib install-lib install-include install-static"
INSTALL_RULES="$INSTALL_RULES $INSTALL_APACHE_RULE"
BUILD_RULES="fsmod-lib ramod-lib lib bin test sub-test $BUILD_APACHE_RULE tools"
if test "$svn_lib_berkeley_db" = "yes"; then
BUILD_RULES="$BUILD_RULES bdb-lib bdb-test"
INSTALL_RULES="`echo $INSTALL_RULES | $SED 's/install-fsmod-lib/install-fsmod-lib install-bdb-lib/'`"
INSTALL_STATIC_RULES="$INSTALL_STATIC_RULES install-bdb-lib"
BDB_TEST_DEPS="\$(BDB_TEST_DEPS)"
BDB_TEST_PROGRAMS="\$(BDB_TEST_PROGRAMS)"
fi
if test "$svn_lib_serf" = "yes"; then
BUILD_RULES="$BUILD_RULES serf-lib"
INSTALL_RULES="`echo $INSTALL_RULES | $SED 's/install-ramod-lib/install-ramod-lib install-serf-lib/'`"
INSTALL_STATIC_RULES="$INSTALL_STATIC_RULES install-serf-lib"
fi
if test "$svn_lib_kwallet" = "yes"; then
BUILD_RULES="$BUILD_RULES kwallet-lib"
INSTALL_RULES="`echo $INSTALL_RULES | $SED 's/install-lib/install-lib install-kwallet-lib/'`"
INSTALL_STATIC_RULES="$INSTALL_STATIC_RULES install-kwallet-lib"
fi
if test "$found_gnome_keyring" = "yes"; then
BUILD_RULES="$BUILD_RULES gnome-keyring-lib"
INSTALL_RULES="`echo $INSTALL_RULES | $SED 's/install-lib/install-lib install-gnome-keyring-lib/'`"
INSTALL_STATIC_RULES="$INSTALL_STATIC_RULES install-gnome-keyring-lib"
fi
if test "$USE_NLS" = "yes"; then
BUILD_RULES="$BUILD_RULES locale"
INSTALL_RULES="$INSTALL_RULES install-locale"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
$as_echo_n "checking for ANSI C header files... " >&6; }
if ${ac_cv_header_stdc+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <float.h>
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ac_cv_header_stdc=yes
else
ac_cv_header_stdc=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
if test $ac_cv_header_stdc = yes; then
# SunOS 4.x string.h does not declare mem*, contrary to ANSI.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <string.h>
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
$EGREP "memchr" >/dev/null 2>&1; then :
else
ac_cv_header_stdc=no
fi
rm -f conftest*
fi
if test $ac_cv_header_stdc = yes; then
# ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdlib.h>
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
$EGREP "free" >/dev/null 2>&1; then :
else
ac_cv_header_stdc=no
fi
rm -f conftest*
fi
if test $ac_cv_header_stdc = yes; then
# /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
if test "$cross_compiling" = yes; then :
:
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <ctype.h>
#include <stdlib.h>
#if ((' ' & 0x0FF) == 0x020)
# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
#else
# define ISLOWER(c) \
(('a' <= (c) && (c) <= 'i') \
|| ('j' <= (c) && (c) <= 'r') \
|| ('s' <= (c) && (c) <= 'z'))
# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
#endif
#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
int
main ()
{
int i;
for (i = 0; i < 256; i++)
if (XOR (islower (i), ISLOWER (i))
|| toupper (i) != TOUPPER (i))
return 2;
return 0;
}
_ACEOF
if ac_fn_c_try_run "$LINENO"; then :
else
ac_cv_header_stdc=no
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
$as_echo "$ac_cv_header_stdc" >&6; }
if test $ac_cv_header_stdc = yes; then
$as_echo "#define STDC_HEADERS 1" >>confdefs.h
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5
$as_echo_n "checking for an ANSI C-conforming const... " >&6; }
if ${ac_cv_c_const+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
#ifndef __cplusplus
/* Ultrix mips cc rejects this sort of thing. */
typedef int charset[2];
const charset cs = { 0, 0 };
/* SunOS 4.1.1 cc rejects this. */
char const *const *pcpcc;
char **ppc;
/* NEC SVR4.0.2 mips cc rejects this. */
struct point {int x, y;};
static struct point const zero = {0,0};
/* AIX XL C 1.02.0.0 rejects this.
It does not let you subtract one const X* pointer from another in
an arm of an if-expression whose if-part is not a constant
expression */
const char *g = "string";
pcpcc = &g + (g ? g-g : 0);
/* HPUX 7.0 cc rejects these. */
++pcpcc;
ppc = (char**) pcpcc;
pcpcc = (char const *const *) ppc;
{ /* SCO 3.2v4 cc rejects this sort of thing. */
char tx;
char *t = &tx;
char const *s = 0 ? (char *) 0 : (char const *) 0;
*t++ = 0;
if (s) return 0;
}
{ /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */
int x[] = {25, 17};
const int *foo = &x[0];
++foo;
}
{ /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
typedef const int *iptr;
iptr p = 0;
++p;
}
{ /* AIX XL C 1.02.0.0 rejects this sort of thing, saying
"k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
struct s { int j; const int *ap[3]; } bx;
struct s *b = &bx; b->j = 5;
}
{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */
const int foo = 10;
if (!foo) return 0;
}
return !cs[0] && !zero.x;
#endif
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ac_cv_c_const=yes
else
ac_cv_c_const=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5
$as_echo "$ac_cv_c_const" >&6; }
if test $ac_cv_c_const = no; then
$as_echo "#define const /**/" >>confdefs.h
fi
ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default"
if test "x$ac_cv_type_size_t" = xyes; then :
else
cat >>confdefs.h <<_ACEOF
#define size_t unsigned int
_ACEOF
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working memcmp" >&5
$as_echo_n "checking for working memcmp... " >&6; }
if ${ac_cv_func_memcmp_working+:} false; then :
$as_echo_n "(cached) " >&6
else
if test "$cross_compiling" = yes; then :
ac_cv_func_memcmp_working=no
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$ac_includes_default
int
main ()
{
/* Some versions of memcmp are not 8-bit clean. */
char c0 = '\100', c1 = '\200', c2 = '\201';
if (memcmp(&c0, &c2, 1) >= 0 || memcmp(&c1, &c2, 1) >= 0)
return 1;
/* The Next x86 OpenStep bug shows up only when comparing 16 bytes
or more and with at least one buffer not starting on a 4-byte boundary.
William Lewis provided this test program. */
{
char foo[21];
char bar[21];
int i;
for (i = 0; i < 4; i++)
{
char *a = foo + i;
char *b = bar + i;
strcpy (a, "--------01111111");
strcpy (b, "--------10000000");
if (memcmp (a, b, 16) >= 0)
return 1;
}
return 0;
}
;
return 0;
}
_ACEOF
if ac_fn_c_try_run "$LINENO"; then :
ac_cv_func_memcmp_working=yes
else
ac_cv_func_memcmp_working=no
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_memcmp_working" >&5
$as_echo "$ac_cv_func_memcmp_working" >&6; }
test $ac_cv_func_memcmp_working = no && case " $LIBOBJS " in
*" memcmp.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS memcmp.$ac_objext"
;;
esac
for ac_func in vprintf
do :
ac_fn_c_check_func "$LINENO" "vprintf" "ac_cv_func_vprintf"
if test "x$ac_cv_func_vprintf" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_VPRINTF 1
_ACEOF
ac_fn_c_check_func "$LINENO" "_doprnt" "ac_cv_func__doprnt"
if test "x$ac_cv_func__doprnt" = xyes; then :
$as_echo "#define HAVE_DOPRNT 1" >>confdefs.h
fi
fi
done
for ac_func in symlink readlink
do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
_ACEOF
fi
done
for ac_header in sys/utsname.h
do :
ac_fn_c_check_header_mongrel "$LINENO" "sys/utsname.h" "ac_cv_header_sys_utsname_h" "$ac_includes_default"
if test "x$ac_cv_header_sys_utsname_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_SYS_UTSNAME_H 1
_ACEOF
for ac_func in uname
do :
ac_fn_c_check_func "$LINENO" "uname" "ac_cv_func_uname"
if test "x$ac_cv_func_uname" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_UNAME 1
_ACEOF
fi
done
fi
done
ac_fn_c_check_header_mongrel "$LINENO" "termios.h" "ac_cv_header_termios_h" "$ac_includes_default"
if test "x$ac_cv_header_termios_h" = xyes; then :
for ac_func in tcgetattr tcsetattr
do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
_ACEOF
$as_echo "#define HAVE_TERMIOS_H 1" >>confdefs.h
fi
done
fi
# Check whether --with-openssl was given.
if test "${with_openssl+set}" = set; then :
withval=$with_openssl;
fi
# Check whether --enable-debug was given.
if test "${enable_debug+set}" = set; then :
enableval=$enable_debug;
if test "$enableval" = "yes" ; then
enable_debugging="yes"
else
enable_debugging="no"
fi
else
# Neither --enable-debug nor --disable-debug was passed.
enable_debugging="maybe"
fi
# Check whether --enable-optimize was given.
if test "${enable_optimize+set}" = set; then :
enableval=$enable_optimize;
if test "$enableval" = "yes" ; then
enable_optimization="yes"
else
enable_optimization="no"
fi
else
# Neither --enable-optimize nor --disable-optimize was passed.
enable_optimization="maybe"
fi
# Check whether --enable-disallowing-of-undefined-references was given.
if test "${enable_disallowing_of_undefined_references+set}" = set; then :
enableval=$enable_disallowing_of_undefined_references;
fi
if test "$enable_disallowing_of_undefined_references" != "yes" && test "`uname`" != "Linux"; then
enable_disallowing_of_undefined_references="no"
fi
if test "$enable_disallowing_of_undefined_references" != "no"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for -Wl,--no-undefined" >&5
$as_echo_n "checking for -Wl,--no-undefined... " >&6; }
old_LDFLAGS="$LDFLAGS"
LDFLAGS="$LDFLAGS -Wl,--no-undefined"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(){;}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
svn_wl_no_undefined="yes"
else
svn_wl_no_undefined="no"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LDFLAGS="$old_LDFLAGS"
if test "$svn_wl_no_undefined" = "yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
for library_dir in "$abs_srcdir/subversion/libsvn_"*; do
eval "`basename $library_dir`_LDFLAGS=-Wl,--no-undefined"
done
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
if test "$enable_disallowing_of_undefined_references" = "yes"; then
as_fn_error $? "--enable-disallowing-of-undefined-references explicitly requested, but -Wl,--no-undefined not supported" "$LINENO" 5
fi
fi
fi
# Check whether --enable-maintainer-mode was given.
if test "${enable_maintainer_mode+set}" = set; then :
enableval=$enable_maintainer_mode;
if test "$enableval" = "yes" ; then
if test "$enable_debugging" = "no" ; then
as_fn_error $? "Can't have --disable-debug and --enable-maintainer-mode" "$LINENO" 5
fi
enable_debugging=yes
if test "$GCC" = "yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: maintainer-mode: adding GCC warning flags" >&5
$as_echo "$as_me: maintainer-mode: adding GCC warning flags" >&6;}
CFLAGS_KEEP="$CFLAGS"
CFLAGS=""
_svn_xxflags__save="$CFLAGS"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC accepts -Werror=implicit-function-declaration" >&5
$as_echo_n "checking if $CC accepts -Werror=implicit-function-declaration... " >&6; }
CFLAGS="-Werror=implicit-function-declaration $CFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(void){return 0;}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
_svn_xxflags__save="$CFLAGS"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC accepts -Werror=declaration-after-statement" >&5
$as_echo_n "checking if $CC accepts -Werror=declaration-after-statement... " >&6; }
CFLAGS="-Werror=declaration-after-statement $CFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(void){return 0;}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
_svn_xxflags__save="$CFLAGS"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC accepts -Wextra-tokens" >&5
$as_echo_n "checking if $CC accepts -Wextra-tokens... " >&6; }
CFLAGS="-Wextra-tokens $CFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(void){return 0;}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
_svn_xxflags__save="$CFLAGS"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC accepts -Wnewline-eof" >&5
$as_echo_n "checking if $CC accepts -Wnewline-eof... " >&6; }
CFLAGS="-Wnewline-eof $CFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(void){return 0;}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
_svn_xxflags__save="$CFLAGS"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC accepts -Wshorten-64-to-32" >&5
$as_echo_n "checking if $CC accepts -Wshorten-64-to-32... " >&6; }
CFLAGS="-Wshorten-64-to-32 $CFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(void){return 0;}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
_svn_xxflags__save="$CFLAGS"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC accepts -Wold-style-definition" >&5
$as_echo_n "checking if $CC accepts -Wold-style-definition... " >&6; }
CFLAGS="-Wold-style-definition $CFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(void){return 0;}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
_svn_xxflags__save="$CFLAGS"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC accepts -Wno-system-headers" >&5
$as_echo_n "checking if $CC accepts -Wno-system-headers... " >&6; }
CFLAGS="-Wno-system-headers $CFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(void){return 0;}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
_svn_xxflags__save="$CFLAGS"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC accepts -Wno-format-nonliteral" >&5
$as_echo_n "checking if $CC accepts -Wno-format-nonliteral... " >&6; }
CFLAGS="-Wno-format-nonliteral $CFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(void){return 0;}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
CMAINTAINERFLAGS="$CFLAGS $CMAINTAINERFLAGS"
CFLAGS="$CFLAGS_KEEP"
CMAINTAINERFLAGS="-Wall -Wpointer-arith -Wwrite-strings -Wshadow -Wformat=2 -Wunused -Waggregate-return -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wno-multichar -Wredundant-decls -Wnested-externs -Winline -Wno-long-long $CMAINTAINERFLAGS"
fi
if test "$GXX" = "yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: maintainer-mode: adding G++ warning flags" >&5
$as_echo "$as_me: maintainer-mode: adding G++ warning flags" >&6;}
CXXFLAGS_KEEP="$CXXFLAGS"
CXXFLAGS=""
_svn_xxflags__save="$CXXFLAGS"
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CXX accepts -Wextra-tokens" >&5
$as_echo_n "checking if $CXX accepts -Wextra-tokens... " >&6; }
CXXFLAGS="-Wextra-tokens $CXXFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(){}
_ACEOF
if ac_fn_cxx_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CXXFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
_svn_xxflags__save="$CXXFLAGS"
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CXX accepts -Wnewline-eof" >&5
$as_echo_n "checking if $CXX accepts -Wnewline-eof... " >&6; }
CXXFLAGS="-Wnewline-eof $CXXFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(){}
_ACEOF
if ac_fn_cxx_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CXXFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
_svn_xxflags__save="$CXXFLAGS"
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CXX accepts -Wshorten-64-to-32" >&5
$as_echo_n "checking if $CXX accepts -Wshorten-64-to-32... " >&6; }
CXXFLAGS="-Wshorten-64-to-32 $CXXFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(){}
_ACEOF
if ac_fn_cxx_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CXXFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
_svn_xxflags__save="$CXXFLAGS"
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CXX accepts -Wno-system-headers" >&5
$as_echo_n "checking if $CXX accepts -Wno-system-headers... " >&6; }
CXXFLAGS="-Wno-system-headers $CXXFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(){}
_ACEOF
if ac_fn_cxx_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CXXFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
CXXMAINTAINERFLAGS="$CXXFLAGS $CXXMAINTAINERFLAGS"
CXXFLAGS="$CXXFLAGS_KEEP"
CXXMAINTAINERFLAGS="-Wall -Wpointer-arith -Wwrite-strings -Wshadow -Wunused -Wunreachable-code $CXXMAINTAINERFLAGS"
fi
fi
fi
if test "$enable_debugging" = "yes" ; then
if test "$enable_optimization" != "yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: Disabling optimizations for debugging" >&5
$as_echo "$as_me: Disabling optimizations for debugging" >&6;}
CFLAGS="`echo $CFLAGS' ' | $SED -e 's/-O[^ ]* //g'`"
CXXFLAGS="`echo $CXXFLAGS' ' | $SED -e 's/-O[^ ]* //g'`"
fi
if test -z "`echo $CUSERFLAGS' ' | $EGREP -- '-g[0-9]? '`"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: Enabling debugging for C" >&5
$as_echo "$as_me: Enabling debugging for C" >&6;}
CFLAGS="`echo $CFLAGS' ' | $SED -e 's/-g[0-9] //g' -e 's/-g //g'`"
_svn_xxflags__save="$CFLAGS"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC accepts -fno-inline" >&5
$as_echo_n "checking if $CC accepts -fno-inline... " >&6; }
CFLAGS="-fno-inline $CFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(void){return 0;}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
_svn_xxflags__save="$CFLAGS"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC accepts -fno-omit-frame-pointer" >&5
$as_echo_n "checking if $CC accepts -fno-omit-frame-pointer... " >&6; }
CFLAGS="-fno-omit-frame-pointer $CFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(void){return 0;}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
_svn_xxflags__save="$CFLAGS"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC accepts -g3" >&5
$as_echo_n "checking if $CC accepts -g3... " >&6; }
CFLAGS="-g3 $CFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(void){return 0;}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CFLAGS="$_svn_xxflags__save"
_svn_xxflags__save="$CFLAGS"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC accepts -g2" >&5
$as_echo_n "checking if $CC accepts -g2... " >&6; }
CFLAGS="-g2 $CFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(void){return 0;}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CFLAGS="$_svn_xxflags__save"
_svn_xxflags__save="$CFLAGS"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC accepts -g" >&5
$as_echo_n "checking if $CC accepts -g... " >&6; }
CFLAGS="-g $CFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(void){return 0;}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
fi
if test -z "`echo $CXXUSERFLAGS' ' | $EGREP -- '-g[0-9]? '`"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: Enabling debugging for C++" >&5
$as_echo "$as_me: Enabling debugging for C++" >&6;}
CXXFLAGS="`echo $CXXFLAGS' ' | $SED -e 's/-g[0-9] //g' -e 's/-g //g'`"
_svn_xxflags__save="$CXXFLAGS"
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CXX accepts -fno-inline" >&5
$as_echo_n "checking if $CXX accepts -fno-inline... " >&6; }
CXXFLAGS="-fno-inline $CXXFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(){}
_ACEOF
if ac_fn_cxx_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CXXFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
_svn_xxflags__save="$CXXFLAGS"
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CXX accepts -fno-omit-frame-pointer" >&5
$as_echo_n "checking if $CXX accepts -fno-omit-frame-pointer... " >&6; }
CXXFLAGS="-fno-omit-frame-pointer $CXXFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(){}
_ACEOF
if ac_fn_cxx_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CXXFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
_svn_xxflags__save="$CXXFLAGS"
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CXX accepts -g3" >&5
$as_echo_n "checking if $CXX accepts -g3... " >&6; }
CXXFLAGS="-g3 $CXXFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(){}
_ACEOF
if ac_fn_cxx_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CXXFLAGS="$_svn_xxflags__save"
_svn_xxflags__save="$CXXFLAGS"
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CXX accepts -g2" >&5
$as_echo_n "checking if $CXX accepts -g2... " >&6; }
CXXFLAGS="-g2 $CXXFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(){}
_ACEOF
if ac_fn_cxx_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CXXFLAGS="$_svn_xxflags__save"
_svn_xxflags__save="$CXXFLAGS"
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CXX accepts -g" >&5
$as_echo_n "checking if $CXX accepts -g... " >&6; }
CXXFLAGS="-g $CXXFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(){}
_ACEOF
if ac_fn_cxx_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CXXFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
fi
CFLAGS="$CFLAGS -DSVN_DEBUG -DAP_DEBUG"
CXXFLAGS="$CXXFLAGS -DSVN_DEBUG -DAP_DEBUG"
elif test "$enable_debugging" = "no" ; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: Disabling debugging" >&5
$as_echo "$as_me: Disabling debugging" >&6;}
CFLAGS="`echo $CFLAGS' ' | $SED -e 's/-g[0-9] //g' -e 's/-g //g'`"
CXXFLAGS="`echo $CXXFLAGS' ' | $SED -e 's/-g[0-9] //g' -e 's/-g //g'`"
CFLAGS="$CFLAGS -DNDEBUG"
CXXFLAGS="$CXXFLAGS -DNDEBUG"
# elif test "$enable_debugging" = "maybe" ; then
# # do nothing
fi
if test "$enable_optimization" = "yes"; then
if test -z "`echo $CUSERFLAGS' ' | $EGREP -- '-O[^ ]* '`"; then
CFLAGS="`echo $CFLAGS' ' | $SED -e 's/-O[^ ]* //g'`"
if test "$enable_debugging" = "yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: Enabling optimizations for C (with debugging enabled)" >&5
$as_echo "$as_me: Enabling optimizations for C (with debugging enabled)" >&6;}
_svn_xxflags__save="$CFLAGS"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC accepts -O1" >&5
$as_echo_n "checking if $CC accepts -O1... " >&6; }
CFLAGS="-O1 $CFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(void){return 0;}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CFLAGS="$_svn_xxflags__save"
_svn_xxflags__save="$CFLAGS"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC accepts -O" >&5
$as_echo_n "checking if $CC accepts -O... " >&6; }
CFLAGS="-O $CFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(void){return 0;}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: Enabling optimizations for C" >&5
$as_echo "$as_me: Enabling optimizations for C" >&6;}
_svn_xxflags__save="$CFLAGS"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC accepts -O3" >&5
$as_echo_n "checking if $CC accepts -O3... " >&6; }
CFLAGS="-O3 $CFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(void){return 0;}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CFLAGS="$_svn_xxflags__save"
_svn_xxflags__save="$CFLAGS"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC accepts -O2" >&5
$as_echo_n "checking if $CC accepts -O2... " >&6; }
CFLAGS="-O2 $CFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(void){return 0;}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CFLAGS="$_svn_xxflags__save"
_svn_xxflags__save="$CFLAGS"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC accepts -O1" >&5
$as_echo_n "checking if $CC accepts -O1... " >&6; }
CFLAGS="-O1 $CFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(void){return 0;}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CFLAGS="$_svn_xxflags__save"
_svn_xxflags__save="$CFLAGS"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC accepts -O" >&5
$as_echo_n "checking if $CC accepts -O... " >&6; }
CFLAGS="-O $CFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(void){return 0;}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
_svn_xxflags__save="$CFLAGS"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC accepts -Wno-clobbered" >&5
$as_echo_n "checking if $CC accepts -Wno-clobbered... " >&6; }
CFLAGS="-Wno-clobbered $CFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(void){return 0;}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
_svn_xxflags__save="$CFLAGS"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC accepts -flto" >&5
$as_echo_n "checking if $CC accepts -flto... " >&6; }
CFLAGS="-flto $CFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(void){return 0;}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
fi
fi
if test -z "`echo $CXXUSERFLAGS' ' | $EGREP -- '-O[^ ]* '`"; then
CXXFLAGS="`echo $CXXFLAGS' ' | $SED -e 's/-O[^ ]* //g'`"
if test "$enable_debugging" = "yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: Enabling optimizations for C++ (with debugging enabled)" >&5
$as_echo "$as_me: Enabling optimizations for C++ (with debugging enabled)" >&6;}
_svn_xxflags__save="$CXXFLAGS"
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CXX accepts -O1" >&5
$as_echo_n "checking if $CXX accepts -O1... " >&6; }
CXXFLAGS="-O1 $CXXFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(){}
_ACEOF
if ac_fn_cxx_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CXXFLAGS="$_svn_xxflags__save"
_svn_xxflags__save="$CXXFLAGS"
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CXX accepts -O" >&5
$as_echo_n "checking if $CXX accepts -O... " >&6; }
CXXFLAGS="-O $CXXFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(){}
_ACEOF
if ac_fn_cxx_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CXXFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: Enabling optimizations for C++" >&5
$as_echo "$as_me: Enabling optimizations for C++" >&6;}
_svn_xxflags__save="$CXXFLAGS"
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CXX accepts -O3" >&5
$as_echo_n "checking if $CXX accepts -O3... " >&6; }
CXXFLAGS="-O3 $CXXFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(){}
_ACEOF
if ac_fn_cxx_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CXXFLAGS="$_svn_xxflags__save"
_svn_xxflags__save="$CXXFLAGS"
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CXX accepts -O2" >&5
$as_echo_n "checking if $CXX accepts -O2... " >&6; }
CXXFLAGS="-O2 $CXXFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(){}
_ACEOF
if ac_fn_cxx_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CXXFLAGS="$_svn_xxflags__save"
_svn_xxflags__save="$CXXFLAGS"
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CXX accepts -O1" >&5
$as_echo_n "checking if $CXX accepts -O1... " >&6; }
CXXFLAGS="-O1 $CXXFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(){}
_ACEOF
if ac_fn_cxx_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CXXFLAGS="$_svn_xxflags__save"
_svn_xxflags__save="$CXXFLAGS"
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CXX accepts -O" >&5
$as_echo_n "checking if $CXX accepts -O... " >&6; }
CXXFLAGS="-O $CXXFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(){}
_ACEOF
if ac_fn_cxx_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CXXFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
_svn_xxflags__save="$CXXFLAGS"
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CXX accepts -Wno-clobbered" >&5
$as_echo_n "checking if $CXX accepts -Wno-clobbered... " >&6; }
CXXFLAGS="-Wno-clobbered $CXXFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(){}
_ACEOF
if ac_fn_cxx_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CXXFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
_svn_xxflags__save="$CXXFLAGS"
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CXX accepts -flto" >&5
$as_echo_n "checking if $CXX accepts -flto... " >&6; }
CXXFLAGS="-flto $CXXFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(){}
_ACEOF
if ac_fn_cxx_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CXXFLAGS="$_svn_xxflags__save"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
fi
fi
elif test "$enable_optimization" = "no"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: Disabling optimizations" >&5
$as_echo "$as_me: Disabling optimizations" >&6;}
CFLAGS="`echo $CFLAGS' ' | $SED -e 's/-O[^ ]* //g'`"
CXXFLAGS="`echo $CXXFLAGS' ' | $SED -e 's/-O[^ ]* //g'`"
# elif test "$enable_optimization" = "maybe" ; then
# # do nothing
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: C compiler flags: $CFLAGS" >&5
$as_echo "$as_me: C compiler flags: $CFLAGS" >&6;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: user-defined: $CUSERFLAGS" >&5
$as_echo "$as_me: user-defined: $CUSERFLAGS" >&6;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: maintainer-mode: $CMAINTAINERFLAGS" >&5
$as_echo "$as_me: maintainer-mode: $CMAINTAINERFLAGS" >&6;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: C++ compiler flags: $CXXFLAGS" >&5
$as_echo "$as_me: C++ compiler flags: $CXXFLAGS" >&6;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: user-defined: $CXXUSERFLAGS" >&5
$as_echo "$as_me: user-defined: $CXXUSERFLAGS" >&6;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: maintainer-mode: $CXXMAINTAINERFLAGS" >&5
$as_echo "$as_me: maintainer-mode: $CXXMAINTAINERFLAGS" >&6;}
# Check whether --enable-full-version-match was given.
if test "${enable_full_version_match+set}" = set; then :
enableval=$enable_full_version_match;
if test "$enableval" = "no" ; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: Disabling svn full version matching" >&5
$as_echo "$as_me: Disabling svn full version matching" >&6;}
$as_echo "#define SVN_DISABLE_FULL_VERSION_MATCH 1" >>confdefs.h
fi
fi
# Check whether --with-editor was given.
if test "${with_editor+set}" = set; then :
withval=$with_editor;
if test "$withval" = "yes" ; then
as_fn_error $? "--with-editor requires an argument." "$LINENO" 5
else
SVN_CLIENT_EDITOR=$withval
cat >>confdefs.h <<_ACEOF
#define SVN_CLIENT_EDITOR "$SVN_CLIENT_EDITOR"
_ACEOF
fi
fi
zlib_found=no
# Check whether --with-zlib was given.
if test "${with_zlib+set}" = set; then :
withval=$with_zlib;
if test "$withval" = "yes" ; then
ac_fn_c_check_header_mongrel "$LINENO" "zlib.h" "ac_cv_header_zlib_h" "$ac_includes_default"
if test "x$ac_cv_header_zlib_h" = xyes; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inflate in -lz" >&5
$as_echo_n "checking for inflate in -lz... " >&6; }
if ${ac_cv_lib_z_inflate+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lz $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char inflate ();
int
main ()
{
return inflate ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_z_inflate=yes
else
ac_cv_lib_z_inflate=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_inflate" >&5
$as_echo "$ac_cv_lib_z_inflate" >&6; }
if test "x$ac_cv_lib_z_inflate" = xyes; then :
zlib_found="builtin"
fi
fi
elif test "$withval" = "no" ; then
as_fn_error $? "cannot compile without zlib." "$LINENO" 5
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: zlib library configuration" >&5
$as_echo "$as_me: zlib library configuration" >&6;}
zlib_prefix=$withval
save_cppflags="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS -I$zlib_prefix/include"
for ac_header in zlib.h
do :
ac_fn_c_check_header_mongrel "$LINENO" "zlib.h" "ac_cv_header_zlib_h" "$ac_includes_default"
if test "x$ac_cv_header_zlib_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_ZLIB_H 1
_ACEOF
save_ldflags="$LDFLAGS"
LDFLAGS="$LDFLAGS -L$zlib_prefix/lib"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inflate in -lz" >&5
$as_echo_n "checking for inflate in -lz... " >&6; }
if ${ac_cv_lib_z_inflate+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lz $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char inflate ();
int
main ()
{
return inflate ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_z_inflate=yes
else
ac_cv_lib_z_inflate=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_inflate" >&5
$as_echo "$ac_cv_lib_z_inflate" >&6; }
if test "x$ac_cv_lib_z_inflate" = xyes; then :
zlib_found="yes"
fi
LDFLAGS="$save_ldflags"
fi
done
CPPFLAGS="$save_cppflags"
fi
else
ac_fn_c_check_header_mongrel "$LINENO" "zlib.h" "ac_cv_header_zlib_h" "$ac_includes_default"
if test "x$ac_cv_header_zlib_h" = xyes; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inflate in -lz" >&5
$as_echo_n "checking for inflate in -lz... " >&6; }
if ${ac_cv_lib_z_inflate+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lz $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char inflate ();
int
main ()
{
return inflate ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_z_inflate=yes
else
ac_cv_lib_z_inflate=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_inflate" >&5
$as_echo "$ac_cv_lib_z_inflate" >&6; }
if test "x$ac_cv_lib_z_inflate" = xyes; then :
zlib_found="builtin"
fi
fi
fi
if test "$zlib_found" = "no"; then
as_fn_error $? "subversion requires zlib" "$LINENO" 5
fi
if test "$zlib_found" = "yes"; then
SVN_ZLIB_INCLUDES="-I$zlib_prefix/include"
LDFLAGS="$LDFLAGS `
input_flags="-L$zlib_prefix/lib"
output_flags=""
filtered_dirs="/lib /lib64 /usr/lib /usr/lib64"
for flag in $input_flags; do
filter="no"
for dir in $filtered_dirs; do
if test "$flag" = "-L$dir" || test "$flag" = "-L$dir/"; then
filter="yes"
break
fi
done
if test "$filter" = "no"; then
output_flags="$output_flags $flag"
fi
done
if test -n "$output_flags"; then
printf "%s" "${output_flags# }"
fi
`"
fi
SVN_ZLIB_LIBS="-lz"
MOD_ACTIVATION=""
# Check whether --enable-mod-activation was given.
if test "${enable_mod_activation+set}" = set; then :
enableval=$enable_mod_activation;
if test "$enableval" = "yes" ; then
MOD_ACTIVATION="-a"
{ $as_echo "$as_me:${as_lineno-$LINENO}: Enabling apache module activation" >&5
$as_echo "$as_me: Enabling apache module activation" >&6;}
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: Disabling apache module activation" >&5
$as_echo "$as_me: Disabling apache module activation" >&6;}
fi
fi
# Check whether --enable-gcov was given.
if test "${enable_gcov+set}" = set; then :
enableval=$enable_gcov;
if test "$enableval" = "yes" ; then
if test "$GCC" = "yes"; then
if test "$svn_enable_shared" = "yes" ; then
as_fn_error $? "Can't have --enable-gcov without --disable-shared (we
recommend also using --enable-all-static)." "$LINENO" 5
fi
if test ! "$enable_all_static" = "yes" ; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: We recommend --enable-all-static with --enable-gcov." >&5
$as_echo "$as_me: WARNING: We recommend --enable-all-static with --enable-gcov." >&2;}
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: Enabling gcov coverage testing." >&5
$as_echo "$as_me: Enabling gcov coverage testing." >&6;}
CFLAGS="$CFLAGS -fprofile-arcs -ftest-coverage"
CXXFLAGS="$CXXFLAGS -fprofile-arcs -ftest-coverage"
else
as_fn_error $? "We only support --enable-gcov with GCC right now." "$LINENO" 5
fi
fi
fi
# Check whether --enable-gprof was given.
if test "${enable_gprof+set}" = set; then :
enableval=$enable_gprof;
if test "$enableval" = "yes" ; then
if test "$GCC" = "yes"; then
if test "$svn_enable_shared" = "yes" ; then
as_fn_error $? "Can't have --enable-gprof without --disable-shared (we
recommend also using --enable-all-static)." "$LINENO" 5
fi
if test ! "$enable_all_static" = "yes" ; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: We recommend --enable-all-static with --enable-gprof." >&5
$as_echo "$as_me: WARNING: We recommend --enable-all-static with --enable-gprof." >&2;}
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: Enabling gprof profiling data (to gmon.out)." >&5
$as_echo "$as_me: Enabling gprof profiling data (to gmon.out)." >&6;}
CFLAGS="$CFLAGS -pg"
CXXFLAGS="$CXXFLAGS -pg"
LT_LDFLAGS="$LT_LDFLAGS -pg"
else
as_fn_error $? "We only support --enable-gprof with GCC right now." "$LINENO" 5
fi
fi
fi
# Scripting and Bindings languages
# Python: Used for testsuite, and bindings
PYTHON="`$abs_srcdir/build/find_python.sh`"
if test -z "$PYTHON"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Python 2.5 or later is required to run the testsuite" >&5
$as_echo "$as_me: WARNING: Python 2.5 or later is required to run the testsuite" >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: or to use the Subversion Python bindings" >&5
$as_echo "$as_me: WARNING: or to use the Subversion Python bindings" >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: " >&5
$as_echo "$as_me: WARNING: " >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: If you have a suitable Python installed, but not on the" >&5
$as_echo "$as_me: WARNING: If you have a suitable Python installed, but not on the" >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: PATH, set the environment variable PYTHON to the full path" >&5
$as_echo "$as_me: WARNING: PATH, set the environment variable PYTHON to the full path" >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: to the Python executable, and re-run configure" >&5
$as_echo "$as_me: WARNING: to the Python executable, and re-run configure" >&2;}
fi
for ac_prog in "$PYTHON"
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_PYTHON+:} false; then :
$as_echo_n "(cached) " >&6
else
case $PYTHON in
[\\/]* | ?:[\\/]*)
ac_cv_path_PYTHON="$PYTHON" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_PYTHON="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
;;
esac
fi
PYTHON=$ac_cv_path_PYTHON
if test -n "$PYTHON"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON" >&5
$as_echo "$PYTHON" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
test -n "$PYTHON" && break
done
test -n "$PYTHON" || PYTHON="none"
# The minimum version for the JVM runtime for our Java bytecode.
JAVA_OLDEST_WORKING_VER='1.5'
# SVN_CHECK_JDK sets $JAVA_CLASSPATH
JAVA_OLDEST_WORKING_VER="$JAVA_OLDEST_WORKING_VER"
# Check whether --with-jdk was given.
if test "${with_jdk+set}" = set; then :
withval=$with_jdk;
case "$withval" in
"no")
JDK_SUITABLE=no
;;
"yes")
where=check
JAVA_OLDEST_WORKING_VER="$JAVA_OLDEST_WORKING_VER"
JDK=none
JAVA_BIN=none
JAVADOC=none
JAVAC=none
JAVAH=none
JAR=none
JNI_INCLUDES=none
JDK_SUITABLE=no
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for JDK" >&5
$as_echo_n "checking for JDK... " >&6; }
if test $where = check; then
if test -x "$JAVA_HOME/bin/java"; then
JDK="$JAVA_HOME"
elif test -x "/Library/Java/Home/bin/java"; then
JDK="/Library/Java/Home"
elif test -x "/usr/bin/java"; then
JDK="/usr"
elif test -x "/usr/local/bin/java"; then
JDK="/usr/local"
fi
else
JDK=$where
fi
os_arch="`uname`"
if test "$os_arch" = "Darwin"; then
OSX_VER=`/usr/bin/sw_vers | grep ProductVersion | cut -f2 | cut -d"." -f1,2`
if test "$OSX_VER" = "10.4"; then
OSX_VER="10.4u"
fi
OSX_SYS_JAVA_FRAMEWORK="/System/Library/Frameworks/JavaVM.framework"
OSX_SDK_JAVA_FRAMEWORK="/Developer/SDKs/MacOSX$OSX_VER.sdk/System/Library"
OSX_SDK_JAVA_FRAMEWORK="$OSX_SDK_JAVA_FRAMEWORK/Frameworks/JavaVM.framework"
fi
if test "$os_arch" = "Darwin" && test "$JDK" = "/usr" &&
test -d "/Library/Java/Home"; then
JDK="/Library/Java/Home"
fi
if test "$os_arch" = "Darwin" && test "$JDK" = "/Library/Java/Home"; then
JRE_LIB_DIR="$OSX_SYS_JAVA_FRAMEWORK/Classes"
else
JRE_LIB_DIR="$JDK/jre/lib"
fi
if test -f "$JDK/include/jni.h"; then
JNI_INCLUDEDIR="$JDK/include"
JDK_SUITABLE=yes
elif test "$os_arch" = "Darwin" && test -e "$JDK/Headers/jni.h"; then
JNI_INCLUDEDIR="$JDK/Headers"
JDK_SUITABLE=yes
elif test "$os_arch" = "Darwin" &&
test -e "$OSX_SYS_JAVA_FRAMEWORK/Headers/jni.h"; then
JNI_INCLUDEDIR="$OSX_SYS_JAVA_FRAMEWORK/Headers"
JDK_SUITABLE=yes
elif test "$os_arch" = "Darwin" &&
test -e "$OSX_SDK_JAVA_FRAMEWORK/Headers/jni.h"; then
JNI_INCLUDEDIR="$OSX_SDK_JAVA_FRAMEWORK/Headers"
JDK_SUITABLE=yes
else
JDK_SUITABLE=no
fi
if test "$JDK_SUITABLE" = "yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $JNI_INCLUDEDIR/jni.h" >&5
$as_echo "$JNI_INCLUDEDIR/jni.h" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
if test "$where" != "check"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: no JNI header files found." >&5
$as_echo "$as_me: WARNING: no JNI header files found." >&2;}
if test "$os_arch" = "Darwin"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: You may need to install the latest Java Development package from http://connect.apple.com/. Apple no longer includes the JNI header files by default on Java updates." >&5
$as_echo "$as_me: WARNING: You may need to install the latest Java Development package from http://connect.apple.com/. Apple no longer includes the JNI header files by default on Java updates." >&2;}
fi
fi
fi
if test "$JDK_SUITABLE" = "yes"; then
JAVA_BIN='$(JDK)/bin'
JAVA="$JAVA_BIN/java"
JAVAC="$JAVA_BIN/javac"
JAVAH="$JAVA_BIN/javah"
JAVADOC="$JAVA_BIN/javadoc"
JAR="$JAVA_BIN/jar"
jikes_options="/usr/local/bin/jikes /usr/bin/jikes"
# Check whether --with-jikes was given.
if test "${with_jikes+set}" = set; then :
withval=$with_jikes;
if test "$withval" != "no" && test "$withval" != "yes"; then
jikes_options="$withval $jikes_options"
fi
requested_jikes="$withval" # will be 'yes' if path unspecified
fi
if test "$requested_jikes" != "no"; then
for jikes in $jikes_options; do
if test -z "$jikes_found" && test -x "$jikes"; then
jikes_found="yes"
JAVAC="$jikes"
JAVA_CLASSPATH="$JRE_LIB_DIR"
for jar in $JRE_LIB_DIR/*.jar; do
JAVA_CLASSPATH="$JAVA_CLASSPATH:$jar"
done
fi
done
fi
if test -n "$requested_jikes" && test "$requested_jikes" != "no"; then
if test -z "$jikes_found"; then
as_fn_error $? "Could not find a usable version of Jikes" "$LINENO" 5
elif test -n "$jikes_found" && test "$requested_jikes" != "yes" &&
test "$JAVAC" != "$requested_jikes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: --with-jikes PATH was invalid, substitute found" >&5
$as_echo "$as_me: WARNING: --with-jikes PATH was invalid, substitute found" >&2;}
fi
fi
# The release for "-source" could actually be greater than that
# of "-target", if we want to cross-compile for lesser JVMs.
if test -z "$JAVAC_FLAGS"; then
JAVAC_FLAGS="-target $JAVA_OLDEST_WORKING_VER -source 1.5"
if test "$enable_debugging" = "yes"; then
JAVAC_FLAGS="-g -Xlint:unchecked $JAVAC_FLAGS"
fi
fi
JNI_INCLUDES="-I$JNI_INCLUDEDIR"
list="`find "$JNI_INCLUDEDIR" -type d -print`"
for dir in $list; do
JNI_INCLUDES="$JNI_INCLUDES -I$dir"
done
fi
;;
*)
where=$withval
JAVA_OLDEST_WORKING_VER="$JAVA_OLDEST_WORKING_VER"
JDK=none
JAVA_BIN=none
JAVADOC=none
JAVAC=none
JAVAH=none
JAR=none
JNI_INCLUDES=none
JDK_SUITABLE=no
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for JDK" >&5
$as_echo_n "checking for JDK... " >&6; }
if test $where = check; then
if test -x "$JAVA_HOME/bin/java"; then
JDK="$JAVA_HOME"
elif test -x "/Library/Java/Home/bin/java"; then
JDK="/Library/Java/Home"
elif test -x "/usr/bin/java"; then
JDK="/usr"
elif test -x "/usr/local/bin/java"; then
JDK="/usr/local"
fi
else
JDK=$where
fi
os_arch="`uname`"
if test "$os_arch" = "Darwin"; then
OSX_VER=`/usr/bin/sw_vers | grep ProductVersion | cut -f2 | cut -d"." -f1,2`
if test "$OSX_VER" = "10.4"; then
OSX_VER="10.4u"
fi
OSX_SYS_JAVA_FRAMEWORK="/System/Library/Frameworks/JavaVM.framework"
OSX_SDK_JAVA_FRAMEWORK="/Developer/SDKs/MacOSX$OSX_VER.sdk/System/Library"
OSX_SDK_JAVA_FRAMEWORK="$OSX_SDK_JAVA_FRAMEWORK/Frameworks/JavaVM.framework"
fi
if test "$os_arch" = "Darwin" && test "$JDK" = "/usr" &&
test -d "/Library/Java/Home"; then
JDK="/Library/Java/Home"
fi
if test "$os_arch" = "Darwin" && test "$JDK" = "/Library/Java/Home"; then
JRE_LIB_DIR="$OSX_SYS_JAVA_FRAMEWORK/Classes"
else
JRE_LIB_DIR="$JDK/jre/lib"
fi
if test -f "$JDK/include/jni.h"; then
JNI_INCLUDEDIR="$JDK/include"
JDK_SUITABLE=yes
elif test "$os_arch" = "Darwin" && test -e "$JDK/Headers/jni.h"; then
JNI_INCLUDEDIR="$JDK/Headers"
JDK_SUITABLE=yes
elif test "$os_arch" = "Darwin" &&
test -e "$OSX_SYS_JAVA_FRAMEWORK/Headers/jni.h"; then
JNI_INCLUDEDIR="$OSX_SYS_JAVA_FRAMEWORK/Headers"
JDK_SUITABLE=yes
elif test "$os_arch" = "Darwin" &&
test -e "$OSX_SDK_JAVA_FRAMEWORK/Headers/jni.h"; then
JNI_INCLUDEDIR="$OSX_SDK_JAVA_FRAMEWORK/Headers"
JDK_SUITABLE=yes
else
JDK_SUITABLE=no
fi
if test "$JDK_SUITABLE" = "yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $JNI_INCLUDEDIR/jni.h" >&5
$as_echo "$JNI_INCLUDEDIR/jni.h" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
if test "$where" != "check"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: no JNI header files found." >&5
$as_echo "$as_me: WARNING: no JNI header files found." >&2;}
if test "$os_arch" = "Darwin"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: You may need to install the latest Java Development package from http://connect.apple.com/. Apple no longer includes the JNI header files by default on Java updates." >&5
$as_echo "$as_me: WARNING: You may need to install the latest Java Development package from http://connect.apple.com/. Apple no longer includes the JNI header files by default on Java updates." >&2;}
fi
fi
fi
if test "$JDK_SUITABLE" = "yes"; then
JAVA_BIN='$(JDK)/bin'
JAVA="$JAVA_BIN/java"
JAVAC="$JAVA_BIN/javac"
JAVAH="$JAVA_BIN/javah"
JAVADOC="$JAVA_BIN/javadoc"
JAR="$JAVA_BIN/jar"
jikes_options="/usr/local/bin/jikes /usr/bin/jikes"
# Check whether --with-jikes was given.
if test "${with_jikes+set}" = set; then :
withval=$with_jikes;
if test "$withval" != "no" && test "$withval" != "yes"; then
jikes_options="$withval $jikes_options"
fi
requested_jikes="$withval" # will be 'yes' if path unspecified
fi
if test "$requested_jikes" != "no"; then
for jikes in $jikes_options; do
if test -z "$jikes_found" && test -x "$jikes"; then
jikes_found="yes"
JAVAC="$jikes"
JAVA_CLASSPATH="$JRE_LIB_DIR"
for jar in $JRE_LIB_DIR/*.jar; do
JAVA_CLASSPATH="$JAVA_CLASSPATH:$jar"
done
fi
done
fi
if test -n "$requested_jikes" && test "$requested_jikes" != "no"; then
if test -z "$jikes_found"; then
as_fn_error $? "Could not find a usable version of Jikes" "$LINENO" 5
elif test -n "$jikes_found" && test "$requested_jikes" != "yes" &&
test "$JAVAC" != "$requested_jikes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: --with-jikes PATH was invalid, substitute found" >&5
$as_echo "$as_me: WARNING: --with-jikes PATH was invalid, substitute found" >&2;}
fi
fi
# The release for "-source" could actually be greater than that
# of "-target", if we want to cross-compile for lesser JVMs.
if test -z "$JAVAC_FLAGS"; then
JAVAC_FLAGS="-target $JAVA_OLDEST_WORKING_VER -source 1.5"
if test "$enable_debugging" = "yes"; then
JAVAC_FLAGS="-g -Xlint:unchecked $JAVAC_FLAGS"
fi
fi
JNI_INCLUDES="-I$JNI_INCLUDEDIR"
list="`find "$JNI_INCLUDEDIR" -type d -print`"
for dir in $list; do
JNI_INCLUDES="$JNI_INCLUDES -I$dir"
done
fi
;;
esac
else
where=check
JAVA_OLDEST_WORKING_VER="$JAVA_OLDEST_WORKING_VER"
JDK=none
JAVA_BIN=none
JAVADOC=none
JAVAC=none
JAVAH=none
JAR=none
JNI_INCLUDES=none
JDK_SUITABLE=no
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for JDK" >&5
$as_echo_n "checking for JDK... " >&6; }
if test $where = check; then
if test -x "$JAVA_HOME/bin/java"; then
JDK="$JAVA_HOME"
elif test -x "/Library/Java/Home/bin/java"; then
JDK="/Library/Java/Home"
elif test -x "/usr/bin/java"; then
JDK="/usr"
elif test -x "/usr/local/bin/java"; then
JDK="/usr/local"
fi
else
JDK=$where
fi
os_arch="`uname`"
if test "$os_arch" = "Darwin"; then
OSX_VER=`/usr/bin/sw_vers | grep ProductVersion | cut -f2 | cut -d"." -f1,2`
if test "$OSX_VER" = "10.4"; then
OSX_VER="10.4u"
fi
OSX_SYS_JAVA_FRAMEWORK="/System/Library/Frameworks/JavaVM.framework"
OSX_SDK_JAVA_FRAMEWORK="/Developer/SDKs/MacOSX$OSX_VER.sdk/System/Library"
OSX_SDK_JAVA_FRAMEWORK="$OSX_SDK_JAVA_FRAMEWORK/Frameworks/JavaVM.framework"
fi
if test "$os_arch" = "Darwin" && test "$JDK" = "/usr" &&
test -d "/Library/Java/Home"; then
JDK="/Library/Java/Home"
fi
if test "$os_arch" = "Darwin" && test "$JDK" = "/Library/Java/Home"; then
JRE_LIB_DIR="$OSX_SYS_JAVA_FRAMEWORK/Classes"
else
JRE_LIB_DIR="$JDK/jre/lib"
fi
if test -f "$JDK/include/jni.h"; then
JNI_INCLUDEDIR="$JDK/include"
JDK_SUITABLE=yes
elif test "$os_arch" = "Darwin" && test -e "$JDK/Headers/jni.h"; then
JNI_INCLUDEDIR="$JDK/Headers"
JDK_SUITABLE=yes
elif test "$os_arch" = "Darwin" &&
test -e "$OSX_SYS_JAVA_FRAMEWORK/Headers/jni.h"; then
JNI_INCLUDEDIR="$OSX_SYS_JAVA_FRAMEWORK/Headers"
JDK_SUITABLE=yes
elif test "$os_arch" = "Darwin" &&
test -e "$OSX_SDK_JAVA_FRAMEWORK/Headers/jni.h"; then
JNI_INCLUDEDIR="$OSX_SDK_JAVA_FRAMEWORK/Headers"
JDK_SUITABLE=yes
else
JDK_SUITABLE=no
fi
if test "$JDK_SUITABLE" = "yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $JNI_INCLUDEDIR/jni.h" >&5
$as_echo "$JNI_INCLUDEDIR/jni.h" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
if test "$where" != "check"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: no JNI header files found." >&5
$as_echo "$as_me: WARNING: no JNI header files found." >&2;}
if test "$os_arch" = "Darwin"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: You may need to install the latest Java Development package from http://connect.apple.com/. Apple no longer includes the JNI header files by default on Java updates." >&5
$as_echo "$as_me: WARNING: You may need to install the latest Java Development package from http://connect.apple.com/. Apple no longer includes the JNI header files by default on Java updates." >&2;}
fi
fi
fi
if test "$JDK_SUITABLE" = "yes"; then
JAVA_BIN='$(JDK)/bin'
JAVA="$JAVA_BIN/java"
JAVAC="$JAVA_BIN/javac"
JAVAH="$JAVA_BIN/javah"
JAVADOC="$JAVA_BIN/javadoc"
JAR="$JAVA_BIN/jar"
jikes_options="/usr/local/bin/jikes /usr/bin/jikes"
# Check whether --with-jikes was given.
if test "${with_jikes+set}" = set; then :
withval=$with_jikes;
if test "$withval" != "no" && test "$withval" != "yes"; then
jikes_options="$withval $jikes_options"
fi
requested_jikes="$withval" # will be 'yes' if path unspecified
fi
if test "$requested_jikes" != "no"; then
for jikes in $jikes_options; do
if test -z "$jikes_found" && test -x "$jikes"; then
jikes_found="yes"
JAVAC="$jikes"
JAVA_CLASSPATH="$JRE_LIB_DIR"
for jar in $JRE_LIB_DIR/*.jar; do
JAVA_CLASSPATH="$JAVA_CLASSPATH:$jar"
done
fi
done
fi
if test -n "$requested_jikes" && test "$requested_jikes" != "no"; then
if test -z "$jikes_found"; then
as_fn_error $? "Could not find a usable version of Jikes" "$LINENO" 5
elif test -n "$jikes_found" && test "$requested_jikes" != "yes" &&
test "$JAVAC" != "$requested_jikes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: --with-jikes PATH was invalid, substitute found" >&5
$as_echo "$as_me: WARNING: --with-jikes PATH was invalid, substitute found" >&2;}
fi
fi
# The release for "-source" could actually be greater than that
# of "-target", if we want to cross-compile for lesser JVMs.
if test -z "$JAVAC_FLAGS"; then
JAVAC_FLAGS="-target $JAVA_OLDEST_WORKING_VER -source 1.5"
if test "$enable_debugging" = "yes"; then
JAVAC_FLAGS="-g -Xlint:unchecked $JAVAC_FLAGS"
fi
fi
JNI_INCLUDES="-I$JNI_INCLUDEDIR"
list="`find "$JNI_INCLUDEDIR" -type d -print`"
for dir in $list; do
JNI_INCLUDES="$JNI_INCLUDES -I$dir"
done
fi
fi
# Extract the first word of "perl", so it can be a program name with args.
set dummy perl; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_PERL+:} false; then :
$as_echo_n "(cached) " >&6
else
case $PERL in
[\\/]* | ?:[\\/]*)
ac_cv_path_PERL="$PERL" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_PERL="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
test -z "$ac_cv_path_PERL" && ac_cv_path_PERL="none"
;;
esac
fi
PERL=$ac_cv_path_PERL
if test -n "$PERL"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $PERL" >&5
$as_echo "$PERL" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test -n "$RUBY"; then
# Extract the first word of ""$RUBY"", so it can be a program name with args.
set dummy "$RUBY"; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_RUBY+:} false; then :
$as_echo_n "(cached) " >&6
else
case $RUBY in
[\\/]* | ?:[\\/]*)
ac_cv_path_RUBY="$RUBY" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_RUBY="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
test -z "$ac_cv_path_RUBY" && ac_cv_path_RUBY="none"
;;
esac
fi
RUBY=$ac_cv_path_RUBY
if test -n "$RUBY"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $RUBY" >&5
$as_echo "$RUBY" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
else
- for ac_prog in ruby ruby1.8 ruby18 ruby1.9 ruby1 ruby1.9.3 ruby193
+ for ac_prog in ruby ruby1.8 ruby18 ruby1.9 ruby1 ruby1.9.3 ruby193 ruby2.0 ruby2.1
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_RUBY+:} false; then :
$as_echo_n "(cached) " >&6
else
case $RUBY in
[\\/]* | ?:[\\/]*)
ac_cv_path_RUBY="$RUBY" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_RUBY="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
;;
esac
fi
RUBY=$ac_cv_path_RUBY
if test -n "$RUBY"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $RUBY" >&5
$as_echo "$RUBY" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
test -n "$RUBY" && break
done
test -n "$RUBY" || RUBY="none"
fi
if test "$RUBY" != "none"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking rb_hash_foreach" >&5
$as_echo_n "checking rb_hash_foreach... " >&6; }
if "$RUBY" -r mkmf -e 'exit(have_func("rb_hash_foreach") ? 0 : 1)' >/dev/null; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
if test -n "$RDOC"; then
# Extract the first word of ""$RDOC"", so it can be a program name with args.
set dummy "$RDOC"; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_RDOC+:} false; then :
$as_echo_n "(cached) " >&6
else
case $RDOC in
[\\/]* | ?:[\\/]*)
ac_cv_path_RDOC="$RDOC" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_RDOC="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
test -z "$ac_cv_path_RDOC" && ac_cv_path_RDOC="none"
;;
esac
fi
RDOC=$ac_cv_path_RDOC
if test -n "$RDOC"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $RDOC" >&5
$as_echo "$RDOC" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
else
- for ac_prog in rdoc rdoc1.8 rdoc18 rdoc1.9 rdoc19 rdoc1.9.3 rdoc193
+ for ac_prog in rdoc rdoc1.8 rdoc18 rdoc1.9 rdoc19 rdoc1.9.3 rdoc193 rdoc2.0 rdoc2.1
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_RDOC+:} false; then :
$as_echo_n "(cached) " >&6
else
case $RDOC in
[\\/]* | ?:[\\/]*)
ac_cv_path_RDOC="$RDOC" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_RDOC="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
;;
esac
fi
RDOC=$ac_cv_path_RDOC
if test -n "$RDOC"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $RDOC" >&5
$as_echo "$RDOC" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
test -n "$RDOC" && break
done
test -n "$RDOC" || RDOC="none"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Ruby major version" >&5
$as_echo_n "checking for Ruby major version... " >&6; }
if ${svn_cv_ruby_major+:} false; then :
$as_echo_n "(cached) " >&6
else
svn_cv_ruby_major="`$RUBY -rrbconfig -e 'print RbConfig::CONFIG.fetch(%q(MAJOR))'`"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $svn_cv_ruby_major" >&5
$as_echo "$svn_cv_ruby_major" >&6; }
RUBY_MAJOR="$svn_cv_ruby_major"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Ruby minor version" >&5
$as_echo_n "checking for Ruby minor version... " >&6; }
if ${svn_cv_ruby_minor+:} false; then :
$as_echo_n "(cached) " >&6
else
svn_cv_ruby_minor="`$RUBY -rrbconfig -e 'print RbConfig::CONFIG.fetch(%q(MINOR))'`"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $svn_cv_ruby_minor" >&5
$as_echo "$svn_cv_ruby_minor" >&6; }
RUBY_MINOR="$svn_cv_ruby_minor"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Ruby teeny version" >&5
$as_echo_n "checking for Ruby teeny version... " >&6; }
if ${svn_cv_ruby_teeny+:} false; then :
$as_echo_n "(cached) " >&6
else
svn_cv_ruby_teeny="`$RUBY -rrbconfig -e 'major, minor, teeny = RUBY_VERSION.split("."); print teeny;'`"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $svn_cv_ruby_teeny" >&5
$as_echo "$svn_cv_ruby_teeny" >&6; }
RUBY_TEENY="$svn_cv_ruby_teeny"
if test \( "$RUBY_MAJOR" -eq "1" -a "$RUBY_MINOR" -gt "8" -a "$RUBY_TEENY" -lt "3" \); then
# Disallow Ruby between 1.8.7 and 1.9.3
RUBY="none"
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: The detected Ruby is between 1.9 and 1.9.3" >&5
$as_echo "$as_me: WARNING: The detected Ruby is between 1.9 and 1.9.3" >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Only 1.8.x and 1.9.3 releases are supported at this time" >&5
$as_echo "$as_me: WARNING: Only 1.8.x and 1.9.3 releases are supported at this time" >&2;}
fi
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
RUBY="none"
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: The detected Ruby is too old for Subversion to use" >&5
$as_echo "$as_me: WARNING: The detected Ruby is too old for Subversion to use" >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: A Ruby which has rb_hash_foreach is required to use the" >&5
$as_echo "$as_me: WARNING: A Ruby which has rb_hash_foreach is required to use the" >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Subversion Ruby bindings" >&5
$as_echo "$as_me: WARNING: Subversion Ruby bindings" >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Upgrade to the official 1.8.2 release, or later" >&5
$as_echo "$as_me: WARNING: Upgrade to the official 1.8.2 release, or later" >&2;}
fi
fi
# Check whether --with-swig was given.
if test "${with_swig+set}" = set; then :
withval=$with_swig;
case "$withval" in
"no")
SWIG_SUITABLE=no
where=no
if test $where = no; then
SWIG=none
elif test $where = check; then
# Extract the first word of "swig", so it can be a program name with args.
set dummy swig; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_SWIG+:} false; then :
$as_echo_n "(cached) " >&6
else
case $SWIG in
[\\/]* | ?:[\\/]*)
ac_cv_path_SWIG="$SWIG" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_SWIG="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
test -z "$ac_cv_path_SWIG" && ac_cv_path_SWIG="none"
;;
esac
fi
SWIG=$ac_cv_path_SWIG
if test -n "$SWIG"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $SWIG" >&5
$as_echo "$SWIG" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
else
if test -f "$where"; then
SWIG="$where"
else
SWIG="$where/bin/swig"
fi
if test ! -f "$SWIG" || test ! -x "$SWIG"; then
as_fn_error $? "Could not find swig binary at $SWIG" "$LINENO" 5
fi
fi
if test "$SWIG" != "none"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking swig version" >&5
$as_echo_n "checking swig version... " >&6; }
SWIG_VERSION_RAW="`$SWIG -version 2>&1 | \
$SED -ne 's/^.*Version \(.*\)$/\1/p'`"
# We want the version as an integer so we can test against
# which version we're using. SWIG doesn't provide this
# to us so we have to come up with it on our own.
# The major is passed straight through,
# the minor is zero padded to two places,
# and the patch level is zero padded to three places.
# e.g. 1.3.24 becomes 103024
SWIG_VERSION="`echo \"$SWIG_VERSION_RAW\" | \
$SED -e 's/[^0-9\.].*$//' \
-e 's/\.\([0-9]\)$/.0\1/' \
-e 's/\.\([0-9][0-9]\)$/.0\1/' \
-e 's/\.\([0-9]\)\./0\1/; s/\.//g;'`"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $SWIG_VERSION_RAW" >&5
$as_echo "$SWIG_VERSION_RAW" >&6; }
# If you change the required swig version number, don't forget to update:
# subversion/bindings/swig/INSTALL
# packages/rpm/redhat-8+/subversion.spec
# packages/rpm/redhat-7.x/subversion.spec
# packages/rpm/rhel-3/subversion.spec
# packages/rpm/rhel-4/subversion.spec
if test -n "$SWIG_VERSION" && test "$SWIG_VERSION" -ge "103024"; then
SWIG_SUITABLE=yes
else
SWIG_SUITABLE=no
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Detected SWIG version $SWIG_VERSION_RAW" >&5
$as_echo "$as_me: WARNING: Detected SWIG version $SWIG_VERSION_RAW" >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Subversion requires SWIG 1.3.24 or later" >&5
$as_echo "$as_me: WARNING: Subversion requires SWIG 1.3.24 or later" >&2;}
fi
fi
SWIG_PY_COMPILE="none"
SWIG_PY_LINK="none"
if test "$PYTHON" != "none"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: Configuring python swig binding" >&5
$as_echo "$as_me: Configuring python swig binding" >&6;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Python includes" >&5
$as_echo_n "checking for Python includes... " >&6; }
if ${ac_cv_python_includes+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_python_includes="`$PYTHON ${abs_srcdir}/build/get-py-info.py --includes`"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_python_includes" >&5
$as_echo "$ac_cv_python_includes" >&6; }
SWIG_PY_INCLUDES="\$(SWIG_INCLUDES) $ac_cv_python_includes"
if test "$ac_cv_python_includes" = "none"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: python bindings cannot be built without distutils module" >&5
$as_echo "$as_me: WARNING: python bindings cannot be built without distutils module" >&2;}
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for compiling Python extensions" >&5
$as_echo_n "checking for compiling Python extensions... " >&6; }
if ${ac_cv_python_compile+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_python_compile="`$PYTHON ${abs_srcdir}/build/get-py-info.py --compile`"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_python_compile" >&5
$as_echo "$ac_cv_python_compile" >&6; }
SWIG_PY_COMPILE="$ac_cv_python_compile $CFLAGS"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for linking Python extensions" >&5
$as_echo_n "checking for linking Python extensions... " >&6; }
if ${ac_cv_python_link+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_python_link="`$PYTHON ${abs_srcdir}/build/get-py-info.py --link`"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_python_link" >&5
$as_echo "$ac_cv_python_link" >&6; }
SWIG_PY_LINK="$ac_cv_python_link"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for linking Python libraries" >&5
$as_echo_n "checking for linking Python libraries... " >&6; }
if ${ac_cv_python_libs+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_python_libs="`$PYTHON ${abs_srcdir}/build/get-py-info.py --libs`"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_python_libs" >&5
$as_echo "$ac_cv_python_libs" >&6; }
SWIG_PY_LIBS="`
input_flags="$ac_cv_python_libs"
output_flags=""
filtered_dirs="/lib /lib64 /usr/lib /usr/lib64"
for flag in $input_flags; do
filter="no"
for dir in $filtered_dirs; do
if test "$flag" = "-L$dir" || test "$flag" = "-L$dir/"; then
filter="yes"
break
fi
done
if test "$filter" = "no"; then
output_flags="$output_flags $flag"
fi
done
if test -n "$output_flags"; then
printf "%s" "${output_flags# }"
fi
`"
SVN_PYCFMT_SAVE_CPPFLAGS="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $SVN_APR_INCLUDES"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for apr_int64_t Python/C API format string" >&5
$as_echo_n "checking for apr_int64_t Python/C API format string... " >&6; }
if ${svn_cv_pycfmt_apr_int64_t+:} false; then :
$as_echo_n "(cached) " >&6
else
if test "x$svn_cv_pycfmt_apr_int64_t" = "x"; then
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <apr.h>
MaTcHtHiS APR_INT64_T_FMT EnDeNd
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
$EGREP "MaTcHtHiS +\"lld\" +EnDeNd" >/dev/null 2>&1; then :
svn_cv_pycfmt_apr_int64_t="L"
fi
rm -f conftest*
fi
if test "x$svn_cv_pycfmt_apr_int64_t" = "x"; then
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
r
#include <apr.h>
MaTcHtHiS APR_INT64_T_FMT EnDeNd
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
$EGREP "MaTcHtHiS +\"ld\" +EnDeNd" >/dev/null 2>&1; then :
svn_cv_pycfmt_apr_int64_t="l"
fi
rm -f conftest*
fi
if test "x$svn_cv_pycfmt_apr_int64_t" = "x"; then
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <apr.h>
MaTcHtHiS APR_INT64_T_FMT EnDeNd
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
$EGREP "MaTcHtHiS +\"d\" +EnDeNd" >/dev/null 2>&1; then :
svn_cv_pycfmt_apr_int64_t="i"
fi
rm -f conftest*
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $svn_cv_pycfmt_apr_int64_t" >&5
$as_echo "$svn_cv_pycfmt_apr_int64_t" >&6; }
CPPFLAGS="$SVN_PYCFMT_SAVE_CPPFLAGS"
if test "x$svn_cv_pycfmt_apr_int64_t" = "x"; then
as_fn_error $? "failed to recognize APR_INT64_T_FMT on this platform" "$LINENO" 5
fi
cat >>confdefs.h <<_ACEOF
#define SVN_APR_INT64_T_PYCFMT "$svn_cv_pycfmt_apr_int64_t"
_ACEOF
fi
if test "$PERL" != "none"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking perl version" >&5
$as_echo_n "checking perl version... " >&6; }
PERL_VERSION="`$PERL -e 'q([); print $] * 1000000,$/;'`"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $PERL_VERSION" >&5
$as_echo "$PERL_VERSION" >&6; }
if test "$PERL_VERSION" -ge "5008000"; then
SWIG_PL_INCLUDES="\$(SWIG_INCLUDES) `$PERL -MExtUtils::Embed -e ccopts`"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: perl bindings require perl 5.8.0 or newer." >&5
$as_echo "$as_me: WARNING: perl bindings require perl 5.8.0 or newer." >&2;}
fi
fi
SWIG_RB_COMPILE="none"
SWIG_RB_LINK="none"
if test "$RUBY" != "none"; then
rbconfig="$RUBY -rrbconfig -e "
for var_name in arch archdir CC LDSHARED DLEXT LIBS LIBRUBYARG \
- rubyhdrdir sitedir sitelibdir sitearchdir libdir
+ rubyhdrdir rubyarchhdrdir sitedir sitelibdir sitearchdir libdir
do
rbconfig_tmp=`$rbconfig "print RbConfig::CONFIG['$var_name']"`
eval "rbconfig_$var_name=\"$rbconfig_tmp\""
done
{ $as_echo "$as_me:${as_lineno-$LINENO}: Configuring Ruby SWIG binding" >&5
$as_echo "$as_me: Configuring Ruby SWIG binding" >&6;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Ruby include path" >&5
$as_echo_n "checking for Ruby include path... " >&6; }
if ${svn_cv_ruby_includes+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -d "$rbconfig_rubyhdrdir"; then
- svn_cv_ruby_includes="-I. -I$rbconfig_rubyhdrdir -I$rbconfig_rubyhdrdir/ruby -I$rbconfig_rubyhdrdir/ruby/backward -I$rbconfig_rubyhdrdir/$rbconfig_arch"
+ svn_cv_ruby_includes="-I. -I$rbconfig_rubyhdrdir -I$rbconfig_rubyhdrdir/ruby -I$rbconfig_rubyhdrdir/ruby/backward"
+ if test -d "$rbconfig_rubyarchhdrdir"; then
+ svn_cv_ruby_includes="$svn_cv_ruby_includes -I$rbconfig_rubyarchhdrdir"
+ else
+ svn_cv_ruby_includes="$svn_cv_ruby_includes -I$rbconfig_rubyhdrdir/$rbconfig_arch"
+ fi
else
svn_cv_ruby_includes="-I. -I$rbconfig_archdir"
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $svn_cv_ruby_includes" >&5
$as_echo "$svn_cv_ruby_includes" >&6; }
SWIG_RB_INCLUDES="\$(SWIG_INCLUDES) $svn_cv_ruby_includes"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to compile Ruby extensions" >&5
$as_echo_n "checking how to compile Ruby extensions... " >&6; }
if ${svn_cv_ruby_compile+:} false; then :
$as_echo_n "(cached) " >&6
else
svn_cv_ruby_compile="$rbconfig_CC $CFLAGS"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $svn_cv_ruby_compile" >&5
$as_echo "$svn_cv_ruby_compile" >&6; }
SWIG_RB_COMPILE="$svn_cv_ruby_compile"
SWIG_RB_COMPILE=`echo "$SWIG_RB_COMPILE" | $SED -e 's/-ansi//'`
SWIG_RB_COMPILE=`echo "$SWIG_RB_COMPILE" | $SED -e 's/-std=c89//'`
SWIG_RB_COMPILE=`echo "$SWIG_RB_COMPILE" | $SED -e 's/-std=c90//'`
SWIG_RB_COMPILE="$SWIG_RB_COMPILE -Wno-int-to-pointer-cast"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link Ruby extensions" >&5
$as_echo_n "checking how to link Ruby extensions... " >&6; }
if ${svn_cv_ruby_link+:} false; then :
$as_echo_n "(cached) " >&6
else
svn_cv_ruby_link="`$RUBY -e 'ARGV.shift; print ARGV.join(%q( ))' \
$rbconfig_LDSHARED`"
svn_cv_ruby_link="$rbconfig_CC $svn_cv_ruby_link"
svn_cv_ruby_link="$svn_cv_ruby_link -shrext .$rbconfig_DLEXT"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $svn_cv_ruby_link" >&5
$as_echo "$svn_cv_ruby_link" >&6; }
SWIG_RB_LINK="$svn_cv_ruby_link"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link Ruby libraries" >&5
$as_echo_n "checking how to link Ruby libraries... " >&6; }
if ${ac_cv_ruby_libs+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_ruby_libs="$rbconfig_LIBRUBYARG $rbconfig_LIBS"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_ruby_libs" >&5
$as_echo "$ac_cv_ruby_libs" >&6; }
SWIG_RB_LIBS="`
input_flags="$ac_cv_ruby_libs"
output_flags=""
filtered_dirs="/lib /lib64 /usr/lib /usr/lib64"
for flag in $input_flags; do
filter="no"
for dir in $filtered_dirs; do
if test "$flag" = "-L$dir" || test "$flag" = "-L$dir/"; then
filter="yes"
break
fi
done
if test "$filter" = "no"; then
output_flags="$output_flags $flag"
fi
done
if test -n "$output_flags"; then
printf "%s" "${output_flags# }"
fi
`"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for rb_errinfo" >&5
$as_echo_n "checking for rb_errinfo... " >&6; }
old_CFLAGS="$CFLAGS"
old_LIBS="$LIBS"
CFLAGS="$CFLAGS $svn_cv_ruby_includes"
CFLAGS=`echo "$CFLAGS" | $SED -e 's/-ansi//'`
CFLAGS=`echo "$CFLAGS" | $SED -e 's/-std=c89//'`
CFLAGS=`echo "$CFLAGS" | $SED -e 's/-std=c90//'`
LIBS="$SWIG_RB_LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <ruby.h>
int main()
{rb_errinfo();}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
have_rb_errinfo="yes"
else
have_rb_errinfo="no"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
if test "$have_rb_errinfo" = "yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
$as_echo "#define HAVE_RB_ERRINFO 1" >>confdefs.h
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
CFLAGS="$old_CFLAGS"
LIBS="$old_LIBS"
if ${svn_cv_ruby_sitedir+:} false; then :
$as_echo_n "(cached) " >&6
else
svn_cv_ruby_sitedir="$rbconfig_sitedir"
fi
# Check whether --with-ruby-sitedir was given.
if test "${with_ruby_sitedir+set}" = set; then :
withval=$with_ruby_sitedir; svn_ruby_installdir="$withval"
else
svn_ruby_installdir="$svn_cv_ruby_sitedir"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking where to install Ruby scripts" >&5
$as_echo_n "checking where to install Ruby scripts... " >&6; }
if ${svn_cv_ruby_sitedir_libsuffix+:} false; then :
$as_echo_n "(cached) " >&6
else
svn_cv_ruby_sitedir_libsuffix="`echo "$rbconfig_sitelibdir" | \
$SED -e "s,^$rbconfig_sitedir,,"`"
fi
SWIG_RB_SITE_LIB_DIR="${svn_ruby_installdir}${svn_cv_ruby_sitedir_libsuffix}"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $SWIG_RB_SITE_LIB_DIR" >&5
$as_echo "$SWIG_RB_SITE_LIB_DIR" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking where to install Ruby extensions" >&5
$as_echo_n "checking where to install Ruby extensions... " >&6; }
if ${svn_cv_ruby_sitedir_archsuffix+:} false; then :
$as_echo_n "(cached) " >&6
else
svn_cv_ruby_sitedir_archsuffix="`echo "$rbconfig_sitearchdir" | \
$SED -e "s,^$rbconfig_sitedir,,"`"
fi
SWIG_RB_SITE_ARCH_DIR="${svn_ruby_installdir}${svn_cv_ruby_sitedir_archsuffix}"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $SWIG_RB_SITE_ARCH_DIR" >&5
$as_echo "$SWIG_RB_SITE_ARCH_DIR" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to use output level for Ruby bindings tests" >&5
$as_echo_n "checking how to use output level for Ruby bindings tests... " >&6; }
if ${svn_cv_ruby_test_verbose+:} false; then :
$as_echo_n "(cached) " >&6
else
svn_cv_ruby_test_verbose="normal"
fi
# Check whether --with-ruby-test-verbose was given.
if test "${with_ruby_test_verbose+set}" = set; then :
withval=$with_ruby_test_verbose; svn_ruby_test_verbose="$withval"
else
svn_ruby_test_verbose="$svn_cv_ruby_test_verbose"
fi
SWIG_RB_TEST_VERBOSE="$svn_ruby_test_verbose"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $SWIG_RB_TEST_VERBOSE" >&5
$as_echo "$SWIG_RB_TEST_VERBOSE" >&6; }
fi
;;
"yes")
where=check
if test $where = no; then
SWIG=none
elif test $where = check; then
# Extract the first word of "swig", so it can be a program name with args.
set dummy swig; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_SWIG+:} false; then :
$as_echo_n "(cached) " >&6
else
case $SWIG in
[\\/]* | ?:[\\/]*)
ac_cv_path_SWIG="$SWIG" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_SWIG="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
test -z "$ac_cv_path_SWIG" && ac_cv_path_SWIG="none"
;;
esac
fi
SWIG=$ac_cv_path_SWIG
if test -n "$SWIG"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $SWIG" >&5
$as_echo "$SWIG" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
else
if test -f "$where"; then
SWIG="$where"
else
SWIG="$where/bin/swig"
fi
if test ! -f "$SWIG" || test ! -x "$SWIG"; then
as_fn_error $? "Could not find swig binary at $SWIG" "$LINENO" 5
fi
fi
if test "$SWIG" != "none"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking swig version" >&5
$as_echo_n "checking swig version... " >&6; }
SWIG_VERSION_RAW="`$SWIG -version 2>&1 | \
$SED -ne 's/^.*Version \(.*\)$/\1/p'`"
# We want the version as an integer so we can test against
# which version we're using. SWIG doesn't provide this
# to us so we have to come up with it on our own.
# The major is passed straight through,
# the minor is zero padded to two places,
# and the patch level is zero padded to three places.
# e.g. 1.3.24 becomes 103024
SWIG_VERSION="`echo \"$SWIG_VERSION_RAW\" | \
$SED -e 's/[^0-9\.].*$//' \
-e 's/\.\([0-9]\)$/.0\1/' \
-e 's/\.\([0-9][0-9]\)$/.0\1/' \
-e 's/\.\([0-9]\)\./0\1/; s/\.//g;'`"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $SWIG_VERSION_RAW" >&5
$as_echo "$SWIG_VERSION_RAW" >&6; }
# If you change the required swig version number, don't forget to update:
# subversion/bindings/swig/INSTALL
# packages/rpm/redhat-8+/subversion.spec
# packages/rpm/redhat-7.x/subversion.spec
# packages/rpm/rhel-3/subversion.spec
# packages/rpm/rhel-4/subversion.spec
if test -n "$SWIG_VERSION" && test "$SWIG_VERSION" -ge "103024"; then
SWIG_SUITABLE=yes
else
SWIG_SUITABLE=no
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Detected SWIG version $SWIG_VERSION_RAW" >&5
$as_echo "$as_me: WARNING: Detected SWIG version $SWIG_VERSION_RAW" >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Subversion requires SWIG 1.3.24 or later" >&5
$as_echo "$as_me: WARNING: Subversion requires SWIG 1.3.24 or later" >&2;}
fi
fi
SWIG_PY_COMPILE="none"
SWIG_PY_LINK="none"
if test "$PYTHON" != "none"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: Configuring python swig binding" >&5
$as_echo "$as_me: Configuring python swig binding" >&6;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Python includes" >&5
$as_echo_n "checking for Python includes... " >&6; }
if ${ac_cv_python_includes+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_python_includes="`$PYTHON ${abs_srcdir}/build/get-py-info.py --includes`"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_python_includes" >&5
$as_echo "$ac_cv_python_includes" >&6; }
SWIG_PY_INCLUDES="\$(SWIG_INCLUDES) $ac_cv_python_includes"
if test "$ac_cv_python_includes" = "none"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: python bindings cannot be built without distutils module" >&5
$as_echo "$as_me: WARNING: python bindings cannot be built without distutils module" >&2;}
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for compiling Python extensions" >&5
$as_echo_n "checking for compiling Python extensions... " >&6; }
if ${ac_cv_python_compile+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_python_compile="`$PYTHON ${abs_srcdir}/build/get-py-info.py --compile`"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_python_compile" >&5
$as_echo "$ac_cv_python_compile" >&6; }
SWIG_PY_COMPILE="$ac_cv_python_compile $CFLAGS"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for linking Python extensions" >&5
$as_echo_n "checking for linking Python extensions... " >&6; }
if ${ac_cv_python_link+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_python_link="`$PYTHON ${abs_srcdir}/build/get-py-info.py --link`"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_python_link" >&5
$as_echo "$ac_cv_python_link" >&6; }
SWIG_PY_LINK="$ac_cv_python_link"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for linking Python libraries" >&5
$as_echo_n "checking for linking Python libraries... " >&6; }
if ${ac_cv_python_libs+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_python_libs="`$PYTHON ${abs_srcdir}/build/get-py-info.py --libs`"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_python_libs" >&5
$as_echo "$ac_cv_python_libs" >&6; }
SWIG_PY_LIBS="`
input_flags="$ac_cv_python_libs"
output_flags=""
filtered_dirs="/lib /lib64 /usr/lib /usr/lib64"
for flag in $input_flags; do
filter="no"
for dir in $filtered_dirs; do
if test "$flag" = "-L$dir" || test "$flag" = "-L$dir/"; then
filter="yes"
break
fi
done
if test "$filter" = "no"; then
output_flags="$output_flags $flag"
fi
done
if test -n "$output_flags"; then
printf "%s" "${output_flags# }"
fi
`"
SVN_PYCFMT_SAVE_CPPFLAGS="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $SVN_APR_INCLUDES"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for apr_int64_t Python/C API format string" >&5
$as_echo_n "checking for apr_int64_t Python/C API format string... " >&6; }
if ${svn_cv_pycfmt_apr_int64_t+:} false; then :
$as_echo_n "(cached) " >&6
else
if test "x$svn_cv_pycfmt_apr_int64_t" = "x"; then
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <apr.h>
MaTcHtHiS APR_INT64_T_FMT EnDeNd
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
$EGREP "MaTcHtHiS +\"lld\" +EnDeNd" >/dev/null 2>&1; then :
svn_cv_pycfmt_apr_int64_t="L"
fi
rm -f conftest*
fi
if test "x$svn_cv_pycfmt_apr_int64_t" = "x"; then
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
r
#include <apr.h>
MaTcHtHiS APR_INT64_T_FMT EnDeNd
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
$EGREP "MaTcHtHiS +\"ld\" +EnDeNd" >/dev/null 2>&1; then :
svn_cv_pycfmt_apr_int64_t="l"
fi
rm -f conftest*
fi
if test "x$svn_cv_pycfmt_apr_int64_t" = "x"; then
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <apr.h>
MaTcHtHiS APR_INT64_T_FMT EnDeNd
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
$EGREP "MaTcHtHiS +\"d\" +EnDeNd" >/dev/null 2>&1; then :
svn_cv_pycfmt_apr_int64_t="i"
fi
rm -f conftest*
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $svn_cv_pycfmt_apr_int64_t" >&5
$as_echo "$svn_cv_pycfmt_apr_int64_t" >&6; }
CPPFLAGS="$SVN_PYCFMT_SAVE_CPPFLAGS"
if test "x$svn_cv_pycfmt_apr_int64_t" = "x"; then
as_fn_error $? "failed to recognize APR_INT64_T_FMT on this platform" "$LINENO" 5
fi
cat >>confdefs.h <<_ACEOF
#define SVN_APR_INT64_T_PYCFMT "$svn_cv_pycfmt_apr_int64_t"
_ACEOF
fi
if test "$PERL" != "none"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking perl version" >&5
$as_echo_n "checking perl version... " >&6; }
PERL_VERSION="`$PERL -e 'q([); print $] * 1000000,$/;'`"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $PERL_VERSION" >&5
$as_echo "$PERL_VERSION" >&6; }
if test "$PERL_VERSION" -ge "5008000"; then
SWIG_PL_INCLUDES="\$(SWIG_INCLUDES) `$PERL -MExtUtils::Embed -e ccopts`"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: perl bindings require perl 5.8.0 or newer." >&5
$as_echo "$as_me: WARNING: perl bindings require perl 5.8.0 or newer." >&2;}
fi
fi
SWIG_RB_COMPILE="none"
SWIG_RB_LINK="none"
if test "$RUBY" != "none"; then
rbconfig="$RUBY -rrbconfig -e "
for var_name in arch archdir CC LDSHARED DLEXT LIBS LIBRUBYARG \
- rubyhdrdir sitedir sitelibdir sitearchdir libdir
+ rubyhdrdir rubyarchhdrdir sitedir sitelibdir sitearchdir libdir
do
rbconfig_tmp=`$rbconfig "print RbConfig::CONFIG['$var_name']"`
eval "rbconfig_$var_name=\"$rbconfig_tmp\""
done
{ $as_echo "$as_me:${as_lineno-$LINENO}: Configuring Ruby SWIG binding" >&5
$as_echo "$as_me: Configuring Ruby SWIG binding" >&6;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Ruby include path" >&5
$as_echo_n "checking for Ruby include path... " >&6; }
if ${svn_cv_ruby_includes+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -d "$rbconfig_rubyhdrdir"; then
- svn_cv_ruby_includes="-I. -I$rbconfig_rubyhdrdir -I$rbconfig_rubyhdrdir/ruby -I$rbconfig_rubyhdrdir/ruby/backward -I$rbconfig_rubyhdrdir/$rbconfig_arch"
+ svn_cv_ruby_includes="-I. -I$rbconfig_rubyhdrdir -I$rbconfig_rubyhdrdir/ruby -I$rbconfig_rubyhdrdir/ruby/backward"
+ if test -d "$rbconfig_rubyarchhdrdir"; then
+ svn_cv_ruby_includes="$svn_cv_ruby_includes -I$rbconfig_rubyarchhdrdir"
+ else
+ svn_cv_ruby_includes="$svn_cv_ruby_includes -I$rbconfig_rubyhdrdir/$rbconfig_arch"
+ fi
else
svn_cv_ruby_includes="-I. -I$rbconfig_archdir"
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $svn_cv_ruby_includes" >&5
$as_echo "$svn_cv_ruby_includes" >&6; }
SWIG_RB_INCLUDES="\$(SWIG_INCLUDES) $svn_cv_ruby_includes"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to compile Ruby extensions" >&5
$as_echo_n "checking how to compile Ruby extensions... " >&6; }
if ${svn_cv_ruby_compile+:} false; then :
$as_echo_n "(cached) " >&6
else
svn_cv_ruby_compile="$rbconfig_CC $CFLAGS"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $svn_cv_ruby_compile" >&5
$as_echo "$svn_cv_ruby_compile" >&6; }
SWIG_RB_COMPILE="$svn_cv_ruby_compile"
SWIG_RB_COMPILE=`echo "$SWIG_RB_COMPILE" | $SED -e 's/-ansi//'`
SWIG_RB_COMPILE=`echo "$SWIG_RB_COMPILE" | $SED -e 's/-std=c89//'`
SWIG_RB_COMPILE=`echo "$SWIG_RB_COMPILE" | $SED -e 's/-std=c90//'`
SWIG_RB_COMPILE="$SWIG_RB_COMPILE -Wno-int-to-pointer-cast"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link Ruby extensions" >&5
$as_echo_n "checking how to link Ruby extensions... " >&6; }
if ${svn_cv_ruby_link+:} false; then :
$as_echo_n "(cached) " >&6
else
svn_cv_ruby_link="`$RUBY -e 'ARGV.shift; print ARGV.join(%q( ))' \
$rbconfig_LDSHARED`"
svn_cv_ruby_link="$rbconfig_CC $svn_cv_ruby_link"
svn_cv_ruby_link="$svn_cv_ruby_link -shrext .$rbconfig_DLEXT"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $svn_cv_ruby_link" >&5
$as_echo "$svn_cv_ruby_link" >&6; }
SWIG_RB_LINK="$svn_cv_ruby_link"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link Ruby libraries" >&5
$as_echo_n "checking how to link Ruby libraries... " >&6; }
if ${ac_cv_ruby_libs+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_ruby_libs="$rbconfig_LIBRUBYARG $rbconfig_LIBS"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_ruby_libs" >&5
$as_echo "$ac_cv_ruby_libs" >&6; }
SWIG_RB_LIBS="`
input_flags="$ac_cv_ruby_libs"
output_flags=""
filtered_dirs="/lib /lib64 /usr/lib /usr/lib64"
for flag in $input_flags; do
filter="no"
for dir in $filtered_dirs; do
if test "$flag" = "-L$dir" || test "$flag" = "-L$dir/"; then
filter="yes"
break
fi
done
if test "$filter" = "no"; then
output_flags="$output_flags $flag"
fi
done
if test -n "$output_flags"; then
printf "%s" "${output_flags# }"
fi
`"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for rb_errinfo" >&5
$as_echo_n "checking for rb_errinfo... " >&6; }
old_CFLAGS="$CFLAGS"
old_LIBS="$LIBS"
CFLAGS="$CFLAGS $svn_cv_ruby_includes"
CFLAGS=`echo "$CFLAGS" | $SED -e 's/-ansi//'`
CFLAGS=`echo "$CFLAGS" | $SED -e 's/-std=c89//'`
CFLAGS=`echo "$CFLAGS" | $SED -e 's/-std=c90//'`
LIBS="$SWIG_RB_LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <ruby.h>
int main()
{rb_errinfo();}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
have_rb_errinfo="yes"
else
have_rb_errinfo="no"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
if test "$have_rb_errinfo" = "yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
$as_echo "#define HAVE_RB_ERRINFO 1" >>confdefs.h
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
CFLAGS="$old_CFLAGS"
LIBS="$old_LIBS"
if ${svn_cv_ruby_sitedir+:} false; then :
$as_echo_n "(cached) " >&6
else
svn_cv_ruby_sitedir="$rbconfig_sitedir"
fi
# Check whether --with-ruby-sitedir was given.
if test "${with_ruby_sitedir+set}" = set; then :
withval=$with_ruby_sitedir; svn_ruby_installdir="$withval"
else
svn_ruby_installdir="$svn_cv_ruby_sitedir"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking where to install Ruby scripts" >&5
$as_echo_n "checking where to install Ruby scripts... " >&6; }
if ${svn_cv_ruby_sitedir_libsuffix+:} false; then :
$as_echo_n "(cached) " >&6
else
svn_cv_ruby_sitedir_libsuffix="`echo "$rbconfig_sitelibdir" | \
$SED -e "s,^$rbconfig_sitedir,,"`"
fi
SWIG_RB_SITE_LIB_DIR="${svn_ruby_installdir}${svn_cv_ruby_sitedir_libsuffix}"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $SWIG_RB_SITE_LIB_DIR" >&5
$as_echo "$SWIG_RB_SITE_LIB_DIR" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking where to install Ruby extensions" >&5
$as_echo_n "checking where to install Ruby extensions... " >&6; }
if ${svn_cv_ruby_sitedir_archsuffix+:} false; then :
$as_echo_n "(cached) " >&6
else
svn_cv_ruby_sitedir_archsuffix="`echo "$rbconfig_sitearchdir" | \
$SED -e "s,^$rbconfig_sitedir,,"`"
fi
SWIG_RB_SITE_ARCH_DIR="${svn_ruby_installdir}${svn_cv_ruby_sitedir_archsuffix}"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $SWIG_RB_SITE_ARCH_DIR" >&5
$as_echo "$SWIG_RB_SITE_ARCH_DIR" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to use output level for Ruby bindings tests" >&5
$as_echo_n "checking how to use output level for Ruby bindings tests... " >&6; }
if ${svn_cv_ruby_test_verbose+:} false; then :
$as_echo_n "(cached) " >&6
else
svn_cv_ruby_test_verbose="normal"
fi
# Check whether --with-ruby-test-verbose was given.
if test "${with_ruby_test_verbose+set}" = set; then :
withval=$with_ruby_test_verbose; svn_ruby_test_verbose="$withval"
else
svn_ruby_test_verbose="$svn_cv_ruby_test_verbose"
fi
SWIG_RB_TEST_VERBOSE="$svn_ruby_test_verbose"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $SWIG_RB_TEST_VERBOSE" >&5
$as_echo "$SWIG_RB_TEST_VERBOSE" >&6; }
fi
;;
*)
where=$withval
if test $where = no; then
SWIG=none
elif test $where = check; then
# Extract the first word of "swig", so it can be a program name with args.
set dummy swig; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_SWIG+:} false; then :
$as_echo_n "(cached) " >&6
else
case $SWIG in
[\\/]* | ?:[\\/]*)
ac_cv_path_SWIG="$SWIG" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_SWIG="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
test -z "$ac_cv_path_SWIG" && ac_cv_path_SWIG="none"
;;
esac
fi
SWIG=$ac_cv_path_SWIG
if test -n "$SWIG"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $SWIG" >&5
$as_echo "$SWIG" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
else
if test -f "$where"; then
SWIG="$where"
else
SWIG="$where/bin/swig"
fi
if test ! -f "$SWIG" || test ! -x "$SWIG"; then
as_fn_error $? "Could not find swig binary at $SWIG" "$LINENO" 5
fi
fi
if test "$SWIG" != "none"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking swig version" >&5
$as_echo_n "checking swig version... " >&6; }
SWIG_VERSION_RAW="`$SWIG -version 2>&1 | \
$SED -ne 's/^.*Version \(.*\)$/\1/p'`"
# We want the version as an integer so we can test against
# which version we're using. SWIG doesn't provide this
# to us so we have to come up with it on our own.
# The major is passed straight through,
# the minor is zero padded to two places,
# and the patch level is zero padded to three places.
# e.g. 1.3.24 becomes 103024
SWIG_VERSION="`echo \"$SWIG_VERSION_RAW\" | \
$SED -e 's/[^0-9\.].*$//' \
-e 's/\.\([0-9]\)$/.0\1/' \
-e 's/\.\([0-9][0-9]\)$/.0\1/' \
-e 's/\.\([0-9]\)\./0\1/; s/\.//g;'`"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $SWIG_VERSION_RAW" >&5
$as_echo "$SWIG_VERSION_RAW" >&6; }
# If you change the required swig version number, don't forget to update:
# subversion/bindings/swig/INSTALL
# packages/rpm/redhat-8+/subversion.spec
# packages/rpm/redhat-7.x/subversion.spec
# packages/rpm/rhel-3/subversion.spec
# packages/rpm/rhel-4/subversion.spec
if test -n "$SWIG_VERSION" && test "$SWIG_VERSION" -ge "103024"; then
SWIG_SUITABLE=yes
else
SWIG_SUITABLE=no
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Detected SWIG version $SWIG_VERSION_RAW" >&5
$as_echo "$as_me: WARNING: Detected SWIG version $SWIG_VERSION_RAW" >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Subversion requires SWIG 1.3.24 or later" >&5
$as_echo "$as_me: WARNING: Subversion requires SWIG 1.3.24 or later" >&2;}
fi
fi
SWIG_PY_COMPILE="none"
SWIG_PY_LINK="none"
if test "$PYTHON" != "none"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: Configuring python swig binding" >&5
$as_echo "$as_me: Configuring python swig binding" >&6;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Python includes" >&5
$as_echo_n "checking for Python includes... " >&6; }
if ${ac_cv_python_includes+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_python_includes="`$PYTHON ${abs_srcdir}/build/get-py-info.py --includes`"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_python_includes" >&5
$as_echo "$ac_cv_python_includes" >&6; }
SWIG_PY_INCLUDES="\$(SWIG_INCLUDES) $ac_cv_python_includes"
if test "$ac_cv_python_includes" = "none"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: python bindings cannot be built without distutils module" >&5
$as_echo "$as_me: WARNING: python bindings cannot be built without distutils module" >&2;}
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for compiling Python extensions" >&5
$as_echo_n "checking for compiling Python extensions... " >&6; }
if ${ac_cv_python_compile+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_python_compile="`$PYTHON ${abs_srcdir}/build/get-py-info.py --compile`"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_python_compile" >&5
$as_echo "$ac_cv_python_compile" >&6; }
SWIG_PY_COMPILE="$ac_cv_python_compile $CFLAGS"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for linking Python extensions" >&5
$as_echo_n "checking for linking Python extensions... " >&6; }
if ${ac_cv_python_link+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_python_link="`$PYTHON ${abs_srcdir}/build/get-py-info.py --link`"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_python_link" >&5
$as_echo "$ac_cv_python_link" >&6; }
SWIG_PY_LINK="$ac_cv_python_link"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for linking Python libraries" >&5
$as_echo_n "checking for linking Python libraries... " >&6; }
if ${ac_cv_python_libs+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_python_libs="`$PYTHON ${abs_srcdir}/build/get-py-info.py --libs`"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_python_libs" >&5
$as_echo "$ac_cv_python_libs" >&6; }
SWIG_PY_LIBS="`
input_flags="$ac_cv_python_libs"
output_flags=""
filtered_dirs="/lib /lib64 /usr/lib /usr/lib64"
for flag in $input_flags; do
filter="no"
for dir in $filtered_dirs; do
if test "$flag" = "-L$dir" || test "$flag" = "-L$dir/"; then
filter="yes"
break
fi
done
if test "$filter" = "no"; then
output_flags="$output_flags $flag"
fi
done
if test -n "$output_flags"; then
printf "%s" "${output_flags# }"
fi
`"
SVN_PYCFMT_SAVE_CPPFLAGS="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $SVN_APR_INCLUDES"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for apr_int64_t Python/C API format string" >&5
$as_echo_n "checking for apr_int64_t Python/C API format string... " >&6; }
if ${svn_cv_pycfmt_apr_int64_t+:} false; then :
$as_echo_n "(cached) " >&6
else
if test "x$svn_cv_pycfmt_apr_int64_t" = "x"; then
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <apr.h>
MaTcHtHiS APR_INT64_T_FMT EnDeNd
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
$EGREP "MaTcHtHiS +\"lld\" +EnDeNd" >/dev/null 2>&1; then :
svn_cv_pycfmt_apr_int64_t="L"
fi
rm -f conftest*
fi
if test "x$svn_cv_pycfmt_apr_int64_t" = "x"; then
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
r
#include <apr.h>
MaTcHtHiS APR_INT64_T_FMT EnDeNd
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
$EGREP "MaTcHtHiS +\"ld\" +EnDeNd" >/dev/null 2>&1; then :
svn_cv_pycfmt_apr_int64_t="l"
fi
rm -f conftest*
fi
if test "x$svn_cv_pycfmt_apr_int64_t" = "x"; then
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <apr.h>
MaTcHtHiS APR_INT64_T_FMT EnDeNd
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
$EGREP "MaTcHtHiS +\"d\" +EnDeNd" >/dev/null 2>&1; then :
svn_cv_pycfmt_apr_int64_t="i"
fi
rm -f conftest*
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $svn_cv_pycfmt_apr_int64_t" >&5
$as_echo "$svn_cv_pycfmt_apr_int64_t" >&6; }
CPPFLAGS="$SVN_PYCFMT_SAVE_CPPFLAGS"
if test "x$svn_cv_pycfmt_apr_int64_t" = "x"; then
as_fn_error $? "failed to recognize APR_INT64_T_FMT on this platform" "$LINENO" 5
fi
cat >>confdefs.h <<_ACEOF
#define SVN_APR_INT64_T_PYCFMT "$svn_cv_pycfmt_apr_int64_t"
_ACEOF
fi
if test "$PERL" != "none"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking perl version" >&5
$as_echo_n "checking perl version... " >&6; }
PERL_VERSION="`$PERL -e 'q([); print $] * 1000000,$/;'`"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $PERL_VERSION" >&5
$as_echo "$PERL_VERSION" >&6; }
if test "$PERL_VERSION" -ge "5008000"; then
SWIG_PL_INCLUDES="\$(SWIG_INCLUDES) `$PERL -MExtUtils::Embed -e ccopts`"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: perl bindings require perl 5.8.0 or newer." >&5
$as_echo "$as_me: WARNING: perl bindings require perl 5.8.0 or newer." >&2;}
fi
fi
SWIG_RB_COMPILE="none"
SWIG_RB_LINK="none"
if test "$RUBY" != "none"; then
rbconfig="$RUBY -rrbconfig -e "
for var_name in arch archdir CC LDSHARED DLEXT LIBS LIBRUBYARG \
- rubyhdrdir sitedir sitelibdir sitearchdir libdir
+ rubyhdrdir rubyarchhdrdir sitedir sitelibdir sitearchdir libdir
do
rbconfig_tmp=`$rbconfig "print RbConfig::CONFIG['$var_name']"`
eval "rbconfig_$var_name=\"$rbconfig_tmp\""
done
{ $as_echo "$as_me:${as_lineno-$LINENO}: Configuring Ruby SWIG binding" >&5
$as_echo "$as_me: Configuring Ruby SWIG binding" >&6;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Ruby include path" >&5
$as_echo_n "checking for Ruby include path... " >&6; }
if ${svn_cv_ruby_includes+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -d "$rbconfig_rubyhdrdir"; then
- svn_cv_ruby_includes="-I. -I$rbconfig_rubyhdrdir -I$rbconfig_rubyhdrdir/ruby -I$rbconfig_rubyhdrdir/ruby/backward -I$rbconfig_rubyhdrdir/$rbconfig_arch"
+ svn_cv_ruby_includes="-I. -I$rbconfig_rubyhdrdir -I$rbconfig_rubyhdrdir/ruby -I$rbconfig_rubyhdrdir/ruby/backward"
+ if test -d "$rbconfig_rubyarchhdrdir"; then
+ svn_cv_ruby_includes="$svn_cv_ruby_includes -I$rbconfig_rubyarchhdrdir"
+ else
+ svn_cv_ruby_includes="$svn_cv_ruby_includes -I$rbconfig_rubyhdrdir/$rbconfig_arch"
+ fi
else
svn_cv_ruby_includes="-I. -I$rbconfig_archdir"
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $svn_cv_ruby_includes" >&5
$as_echo "$svn_cv_ruby_includes" >&6; }
SWIG_RB_INCLUDES="\$(SWIG_INCLUDES) $svn_cv_ruby_includes"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to compile Ruby extensions" >&5
$as_echo_n "checking how to compile Ruby extensions... " >&6; }
if ${svn_cv_ruby_compile+:} false; then :
$as_echo_n "(cached) " >&6
else
svn_cv_ruby_compile="$rbconfig_CC $CFLAGS"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $svn_cv_ruby_compile" >&5
$as_echo "$svn_cv_ruby_compile" >&6; }
SWIG_RB_COMPILE="$svn_cv_ruby_compile"
SWIG_RB_COMPILE=`echo "$SWIG_RB_COMPILE" | $SED -e 's/-ansi//'`
SWIG_RB_COMPILE=`echo "$SWIG_RB_COMPILE" | $SED -e 's/-std=c89//'`
SWIG_RB_COMPILE=`echo "$SWIG_RB_COMPILE" | $SED -e 's/-std=c90//'`
SWIG_RB_COMPILE="$SWIG_RB_COMPILE -Wno-int-to-pointer-cast"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link Ruby extensions" >&5
$as_echo_n "checking how to link Ruby extensions... " >&6; }
if ${svn_cv_ruby_link+:} false; then :
$as_echo_n "(cached) " >&6
else
svn_cv_ruby_link="`$RUBY -e 'ARGV.shift; print ARGV.join(%q( ))' \
$rbconfig_LDSHARED`"
svn_cv_ruby_link="$rbconfig_CC $svn_cv_ruby_link"
svn_cv_ruby_link="$svn_cv_ruby_link -shrext .$rbconfig_DLEXT"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $svn_cv_ruby_link" >&5
$as_echo "$svn_cv_ruby_link" >&6; }
SWIG_RB_LINK="$svn_cv_ruby_link"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link Ruby libraries" >&5
$as_echo_n "checking how to link Ruby libraries... " >&6; }
if ${ac_cv_ruby_libs+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_ruby_libs="$rbconfig_LIBRUBYARG $rbconfig_LIBS"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_ruby_libs" >&5
$as_echo "$ac_cv_ruby_libs" >&6; }
SWIG_RB_LIBS="`
input_flags="$ac_cv_ruby_libs"
output_flags=""
filtered_dirs="/lib /lib64 /usr/lib /usr/lib64"
for flag in $input_flags; do
filter="no"
for dir in $filtered_dirs; do
if test "$flag" = "-L$dir" || test "$flag" = "-L$dir/"; then
filter="yes"
break
fi
done
if test "$filter" = "no"; then
output_flags="$output_flags $flag"
fi
done
if test -n "$output_flags"; then
printf "%s" "${output_flags# }"
fi
`"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for rb_errinfo" >&5
$as_echo_n "checking for rb_errinfo... " >&6; }
old_CFLAGS="$CFLAGS"
old_LIBS="$LIBS"
CFLAGS="$CFLAGS $svn_cv_ruby_includes"
CFLAGS=`echo "$CFLAGS" | $SED -e 's/-ansi//'`
CFLAGS=`echo "$CFLAGS" | $SED -e 's/-std=c89//'`
CFLAGS=`echo "$CFLAGS" | $SED -e 's/-std=c90//'`
LIBS="$SWIG_RB_LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <ruby.h>
int main()
{rb_errinfo();}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
have_rb_errinfo="yes"
else
have_rb_errinfo="no"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
if test "$have_rb_errinfo" = "yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
$as_echo "#define HAVE_RB_ERRINFO 1" >>confdefs.h
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
CFLAGS="$old_CFLAGS"
LIBS="$old_LIBS"
if ${svn_cv_ruby_sitedir+:} false; then :
$as_echo_n "(cached) " >&6
else
svn_cv_ruby_sitedir="$rbconfig_sitedir"
fi
# Check whether --with-ruby-sitedir was given.
if test "${with_ruby_sitedir+set}" = set; then :
withval=$with_ruby_sitedir; svn_ruby_installdir="$withval"
else
svn_ruby_installdir="$svn_cv_ruby_sitedir"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking where to install Ruby scripts" >&5
$as_echo_n "checking where to install Ruby scripts... " >&6; }
if ${svn_cv_ruby_sitedir_libsuffix+:} false; then :
$as_echo_n "(cached) " >&6
else
svn_cv_ruby_sitedir_libsuffix="`echo "$rbconfig_sitelibdir" | \
$SED -e "s,^$rbconfig_sitedir,,"`"
fi
SWIG_RB_SITE_LIB_DIR="${svn_ruby_installdir}${svn_cv_ruby_sitedir_libsuffix}"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $SWIG_RB_SITE_LIB_DIR" >&5
$as_echo "$SWIG_RB_SITE_LIB_DIR" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking where to install Ruby extensions" >&5
$as_echo_n "checking where to install Ruby extensions... " >&6; }
if ${svn_cv_ruby_sitedir_archsuffix+:} false; then :
$as_echo_n "(cached) " >&6
else
svn_cv_ruby_sitedir_archsuffix="`echo "$rbconfig_sitearchdir" | \
$SED -e "s,^$rbconfig_sitedir,,"`"
fi
SWIG_RB_SITE_ARCH_DIR="${svn_ruby_installdir}${svn_cv_ruby_sitedir_archsuffix}"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $SWIG_RB_SITE_ARCH_DIR" >&5
$as_echo "$SWIG_RB_SITE_ARCH_DIR" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to use output level for Ruby bindings tests" >&5
$as_echo_n "checking how to use output level for Ruby bindings tests... " >&6; }
if ${svn_cv_ruby_test_verbose+:} false; then :
$as_echo_n "(cached) " >&6
else
svn_cv_ruby_test_verbose="normal"
fi
# Check whether --with-ruby-test-verbose was given.
if test "${with_ruby_test_verbose+set}" = set; then :
withval=$with_ruby_test_verbose; svn_ruby_test_verbose="$withval"
else
svn_ruby_test_verbose="$svn_cv_ruby_test_verbose"
fi
SWIG_RB_TEST_VERBOSE="$svn_ruby_test_verbose"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $SWIG_RB_TEST_VERBOSE" >&5
$as_echo "$SWIG_RB_TEST_VERBOSE" >&6; }
fi
;;
esac
else
where=check
if test $where = no; then
SWIG=none
elif test $where = check; then
# Extract the first word of "swig", so it can be a program name with args.
set dummy swig; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_SWIG+:} false; then :
$as_echo_n "(cached) " >&6
else
case $SWIG in
[\\/]* | ?:[\\/]*)
ac_cv_path_SWIG="$SWIG" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_SWIG="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
test -z "$ac_cv_path_SWIG" && ac_cv_path_SWIG="none"
;;
esac
fi
SWIG=$ac_cv_path_SWIG
if test -n "$SWIG"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $SWIG" >&5
$as_echo "$SWIG" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
else
if test -f "$where"; then
SWIG="$where"
else
SWIG="$where/bin/swig"
fi
if test ! -f "$SWIG" || test ! -x "$SWIG"; then
as_fn_error $? "Could not find swig binary at $SWIG" "$LINENO" 5
fi
fi
if test "$SWIG" != "none"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking swig version" >&5
$as_echo_n "checking swig version... " >&6; }
SWIG_VERSION_RAW="`$SWIG -version 2>&1 | \
$SED -ne 's/^.*Version \(.*\)$/\1/p'`"
# We want the version as an integer so we can test against
# which version we're using. SWIG doesn't provide this
# to us so we have to come up with it on our own.
# The major is passed straight through,
# the minor is zero padded to two places,
# and the patch level is zero padded to three places.
# e.g. 1.3.24 becomes 103024
SWIG_VERSION="`echo \"$SWIG_VERSION_RAW\" | \
$SED -e 's/[^0-9\.].*$//' \
-e 's/\.\([0-9]\)$/.0\1/' \
-e 's/\.\([0-9][0-9]\)$/.0\1/' \
-e 's/\.\([0-9]\)\./0\1/; s/\.//g;'`"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $SWIG_VERSION_RAW" >&5
$as_echo "$SWIG_VERSION_RAW" >&6; }
# If you change the required swig version number, don't forget to update:
# subversion/bindings/swig/INSTALL
# packages/rpm/redhat-8+/subversion.spec
# packages/rpm/redhat-7.x/subversion.spec
# packages/rpm/rhel-3/subversion.spec
# packages/rpm/rhel-4/subversion.spec
if test -n "$SWIG_VERSION" && test "$SWIG_VERSION" -ge "103024"; then
SWIG_SUITABLE=yes
else
SWIG_SUITABLE=no
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Detected SWIG version $SWIG_VERSION_RAW" >&5
$as_echo "$as_me: WARNING: Detected SWIG version $SWIG_VERSION_RAW" >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Subversion requires SWIG 1.3.24 or later" >&5
$as_echo "$as_me: WARNING: Subversion requires SWIG 1.3.24 or later" >&2;}
fi
fi
SWIG_PY_COMPILE="none"
SWIG_PY_LINK="none"
if test "$PYTHON" != "none"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: Configuring python swig binding" >&5
$as_echo "$as_me: Configuring python swig binding" >&6;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Python includes" >&5
$as_echo_n "checking for Python includes... " >&6; }
if ${ac_cv_python_includes+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_python_includes="`$PYTHON ${abs_srcdir}/build/get-py-info.py --includes`"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_python_includes" >&5
$as_echo "$ac_cv_python_includes" >&6; }
SWIG_PY_INCLUDES="\$(SWIG_INCLUDES) $ac_cv_python_includes"
if test "$ac_cv_python_includes" = "none"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: python bindings cannot be built without distutils module" >&5
$as_echo "$as_me: WARNING: python bindings cannot be built without distutils module" >&2;}
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for compiling Python extensions" >&5
$as_echo_n "checking for compiling Python extensions... " >&6; }
if ${ac_cv_python_compile+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_python_compile="`$PYTHON ${abs_srcdir}/build/get-py-info.py --compile`"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_python_compile" >&5
$as_echo "$ac_cv_python_compile" >&6; }
SWIG_PY_COMPILE="$ac_cv_python_compile $CFLAGS"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for linking Python extensions" >&5
$as_echo_n "checking for linking Python extensions... " >&6; }
if ${ac_cv_python_link+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_python_link="`$PYTHON ${abs_srcdir}/build/get-py-info.py --link`"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_python_link" >&5
$as_echo "$ac_cv_python_link" >&6; }
SWIG_PY_LINK="$ac_cv_python_link"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for linking Python libraries" >&5
$as_echo_n "checking for linking Python libraries... " >&6; }
if ${ac_cv_python_libs+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_python_libs="`$PYTHON ${abs_srcdir}/build/get-py-info.py --libs`"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_python_libs" >&5
$as_echo "$ac_cv_python_libs" >&6; }
SWIG_PY_LIBS="`
input_flags="$ac_cv_python_libs"
output_flags=""
filtered_dirs="/lib /lib64 /usr/lib /usr/lib64"
for flag in $input_flags; do
filter="no"
for dir in $filtered_dirs; do
if test "$flag" = "-L$dir" || test "$flag" = "-L$dir/"; then
filter="yes"
break
fi
done
if test "$filter" = "no"; then
output_flags="$output_flags $flag"
fi
done
if test -n "$output_flags"; then
printf "%s" "${output_flags# }"
fi
`"
SVN_PYCFMT_SAVE_CPPFLAGS="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $SVN_APR_INCLUDES"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for apr_int64_t Python/C API format string" >&5
$as_echo_n "checking for apr_int64_t Python/C API format string... " >&6; }
if ${svn_cv_pycfmt_apr_int64_t+:} false; then :
$as_echo_n "(cached) " >&6
else
if test "x$svn_cv_pycfmt_apr_int64_t" = "x"; then
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <apr.h>
MaTcHtHiS APR_INT64_T_FMT EnDeNd
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
$EGREP "MaTcHtHiS +\"lld\" +EnDeNd" >/dev/null 2>&1; then :
svn_cv_pycfmt_apr_int64_t="L"
fi
rm -f conftest*
fi
if test "x$svn_cv_pycfmt_apr_int64_t" = "x"; then
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
r
#include <apr.h>
MaTcHtHiS APR_INT64_T_FMT EnDeNd
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
$EGREP "MaTcHtHiS +\"ld\" +EnDeNd" >/dev/null 2>&1; then :
svn_cv_pycfmt_apr_int64_t="l"
fi
rm -f conftest*
fi
if test "x$svn_cv_pycfmt_apr_int64_t" = "x"; then
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <apr.h>
MaTcHtHiS APR_INT64_T_FMT EnDeNd
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
$EGREP "MaTcHtHiS +\"d\" +EnDeNd" >/dev/null 2>&1; then :
svn_cv_pycfmt_apr_int64_t="i"
fi
rm -f conftest*
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $svn_cv_pycfmt_apr_int64_t" >&5
$as_echo "$svn_cv_pycfmt_apr_int64_t" >&6; }
CPPFLAGS="$SVN_PYCFMT_SAVE_CPPFLAGS"
if test "x$svn_cv_pycfmt_apr_int64_t" = "x"; then
as_fn_error $? "failed to recognize APR_INT64_T_FMT on this platform" "$LINENO" 5
fi
cat >>confdefs.h <<_ACEOF
#define SVN_APR_INT64_T_PYCFMT "$svn_cv_pycfmt_apr_int64_t"
_ACEOF
fi
if test "$PERL" != "none"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking perl version" >&5
$as_echo_n "checking perl version... " >&6; }
PERL_VERSION="`$PERL -e 'q([); print $] * 1000000,$/;'`"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $PERL_VERSION" >&5
$as_echo "$PERL_VERSION" >&6; }
if test "$PERL_VERSION" -ge "5008000"; then
SWIG_PL_INCLUDES="\$(SWIG_INCLUDES) `$PERL -MExtUtils::Embed -e ccopts`"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: perl bindings require perl 5.8.0 or newer." >&5
$as_echo "$as_me: WARNING: perl bindings require perl 5.8.0 or newer." >&2;}
fi
fi
SWIG_RB_COMPILE="none"
SWIG_RB_LINK="none"
if test "$RUBY" != "none"; then
rbconfig="$RUBY -rrbconfig -e "
for var_name in arch archdir CC LDSHARED DLEXT LIBS LIBRUBYARG \
- rubyhdrdir sitedir sitelibdir sitearchdir libdir
+ rubyhdrdir rubyarchhdrdir sitedir sitelibdir sitearchdir libdir
do
rbconfig_tmp=`$rbconfig "print RbConfig::CONFIG['$var_name']"`
eval "rbconfig_$var_name=\"$rbconfig_tmp\""
done
{ $as_echo "$as_me:${as_lineno-$LINENO}: Configuring Ruby SWIG binding" >&5
$as_echo "$as_me: Configuring Ruby SWIG binding" >&6;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Ruby include path" >&5
$as_echo_n "checking for Ruby include path... " >&6; }
if ${svn_cv_ruby_includes+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -d "$rbconfig_rubyhdrdir"; then
- svn_cv_ruby_includes="-I. -I$rbconfig_rubyhdrdir -I$rbconfig_rubyhdrdir/ruby -I$rbconfig_rubyhdrdir/ruby/backward -I$rbconfig_rubyhdrdir/$rbconfig_arch"
+ svn_cv_ruby_includes="-I. -I$rbconfig_rubyhdrdir -I$rbconfig_rubyhdrdir/ruby -I$rbconfig_rubyhdrdir/ruby/backward"
+ if test -d "$rbconfig_rubyarchhdrdir"; then
+ svn_cv_ruby_includes="$svn_cv_ruby_includes -I$rbconfig_rubyarchhdrdir"
+ else
+ svn_cv_ruby_includes="$svn_cv_ruby_includes -I$rbconfig_rubyhdrdir/$rbconfig_arch"
+ fi
else
svn_cv_ruby_includes="-I. -I$rbconfig_archdir"
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $svn_cv_ruby_includes" >&5
$as_echo "$svn_cv_ruby_includes" >&6; }
SWIG_RB_INCLUDES="\$(SWIG_INCLUDES) $svn_cv_ruby_includes"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to compile Ruby extensions" >&5
$as_echo_n "checking how to compile Ruby extensions... " >&6; }
if ${svn_cv_ruby_compile+:} false; then :
$as_echo_n "(cached) " >&6
else
svn_cv_ruby_compile="$rbconfig_CC $CFLAGS"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $svn_cv_ruby_compile" >&5
$as_echo "$svn_cv_ruby_compile" >&6; }
SWIG_RB_COMPILE="$svn_cv_ruby_compile"
SWIG_RB_COMPILE=`echo "$SWIG_RB_COMPILE" | $SED -e 's/-ansi//'`
SWIG_RB_COMPILE=`echo "$SWIG_RB_COMPILE" | $SED -e 's/-std=c89//'`
SWIG_RB_COMPILE=`echo "$SWIG_RB_COMPILE" | $SED -e 's/-std=c90//'`
SWIG_RB_COMPILE="$SWIG_RB_COMPILE -Wno-int-to-pointer-cast"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link Ruby extensions" >&5
$as_echo_n "checking how to link Ruby extensions... " >&6; }
if ${svn_cv_ruby_link+:} false; then :
$as_echo_n "(cached) " >&6
else
svn_cv_ruby_link="`$RUBY -e 'ARGV.shift; print ARGV.join(%q( ))' \
$rbconfig_LDSHARED`"
svn_cv_ruby_link="$rbconfig_CC $svn_cv_ruby_link"
svn_cv_ruby_link="$svn_cv_ruby_link -shrext .$rbconfig_DLEXT"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $svn_cv_ruby_link" >&5
$as_echo "$svn_cv_ruby_link" >&6; }
SWIG_RB_LINK="$svn_cv_ruby_link"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link Ruby libraries" >&5
$as_echo_n "checking how to link Ruby libraries... " >&6; }
if ${ac_cv_ruby_libs+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_ruby_libs="$rbconfig_LIBRUBYARG $rbconfig_LIBS"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_ruby_libs" >&5
$as_echo "$ac_cv_ruby_libs" >&6; }
SWIG_RB_LIBS="`
input_flags="$ac_cv_ruby_libs"
output_flags=""
filtered_dirs="/lib /lib64 /usr/lib /usr/lib64"
for flag in $input_flags; do
filter="no"
for dir in $filtered_dirs; do
if test "$flag" = "-L$dir" || test "$flag" = "-L$dir/"; then
filter="yes"
break
fi
done
if test "$filter" = "no"; then
output_flags="$output_flags $flag"
fi
done
if test -n "$output_flags"; then
printf "%s" "${output_flags# }"
fi
`"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for rb_errinfo" >&5
$as_echo_n "checking for rb_errinfo... " >&6; }
old_CFLAGS="$CFLAGS"
old_LIBS="$LIBS"
CFLAGS="$CFLAGS $svn_cv_ruby_includes"
CFLAGS=`echo "$CFLAGS" | $SED -e 's/-ansi//'`
CFLAGS=`echo "$CFLAGS" | $SED -e 's/-std=c89//'`
CFLAGS=`echo "$CFLAGS" | $SED -e 's/-std=c90//'`
LIBS="$SWIG_RB_LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <ruby.h>
int main()
{rb_errinfo();}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
have_rb_errinfo="yes"
else
have_rb_errinfo="no"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
if test "$have_rb_errinfo" = "yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
$as_echo "#define HAVE_RB_ERRINFO 1" >>confdefs.h
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
CFLAGS="$old_CFLAGS"
LIBS="$old_LIBS"
if ${svn_cv_ruby_sitedir+:} false; then :
$as_echo_n "(cached) " >&6
else
svn_cv_ruby_sitedir="$rbconfig_sitedir"
fi
# Check whether --with-ruby-sitedir was given.
if test "${with_ruby_sitedir+set}" = set; then :
withval=$with_ruby_sitedir; svn_ruby_installdir="$withval"
else
svn_ruby_installdir="$svn_cv_ruby_sitedir"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking where to install Ruby scripts" >&5
$as_echo_n "checking where to install Ruby scripts... " >&6; }
if ${svn_cv_ruby_sitedir_libsuffix+:} false; then :
$as_echo_n "(cached) " >&6
else
svn_cv_ruby_sitedir_libsuffix="`echo "$rbconfig_sitelibdir" | \
$SED -e "s,^$rbconfig_sitedir,,"`"
fi
SWIG_RB_SITE_LIB_DIR="${svn_ruby_installdir}${svn_cv_ruby_sitedir_libsuffix}"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $SWIG_RB_SITE_LIB_DIR" >&5
$as_echo "$SWIG_RB_SITE_LIB_DIR" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking where to install Ruby extensions" >&5
$as_echo_n "checking where to install Ruby extensions... " >&6; }
if ${svn_cv_ruby_sitedir_archsuffix+:} false; then :
$as_echo_n "(cached) " >&6
else
svn_cv_ruby_sitedir_archsuffix="`echo "$rbconfig_sitearchdir" | \
$SED -e "s,^$rbconfig_sitedir,,"`"
fi
SWIG_RB_SITE_ARCH_DIR="${svn_ruby_installdir}${svn_cv_ruby_sitedir_archsuffix}"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $SWIG_RB_SITE_ARCH_DIR" >&5
$as_echo "$SWIG_RB_SITE_ARCH_DIR" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to use output level for Ruby bindings tests" >&5
$as_echo_n "checking how to use output level for Ruby bindings tests... " >&6; }
if ${svn_cv_ruby_test_verbose+:} false; then :
$as_echo_n "(cached) " >&6
else
svn_cv_ruby_test_verbose="normal"
fi
# Check whether --with-ruby-test-verbose was given.
if test "${with_ruby_test_verbose+set}" = set; then :
withval=$with_ruby_test_verbose; svn_ruby_test_verbose="$withval"
else
svn_ruby_test_verbose="$svn_cv_ruby_test_verbose"
fi
SWIG_RB_TEST_VERBOSE="$svn_ruby_test_verbose"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $SWIG_RB_TEST_VERBOSE" >&5
$as_echo "$SWIG_RB_TEST_VERBOSE" >&6; }
fi
fi
# Check whether --with-ctypesgen was given.
if test "${with_ctypesgen+set}" = set; then :
withval=$with_ctypesgen;
case "$withval" in
"no")
where=no
CTYPESGEN=none
if test $where = check; then
# Extract the first word of ""ctypesgen.py"", so it can be a program name with args.
set dummy "ctypesgen.py"; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_CTYPESGEN+:} false; then :
$as_echo_n "(cached) " >&6
else
case $CTYPESGEN in
[\\/]* | ?:[\\/]*)
ac_cv_path_CTYPESGEN="$CTYPESGEN" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_CTYPESGEN="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
test -z "$ac_cv_path_CTYPESGEN" && ac_cv_path_CTYPESGEN="none"
;;
esac
fi
CTYPESGEN=$ac_cv_path_CTYPESGEN
if test -n "$CTYPESGEN"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CTYPESGEN" >&5
$as_echo "$CTYPESGEN" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
elif test $where != no; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ctypesgen.py" >&5
$as_echo_n "checking for ctypesgen.py... " >&6; }
if test -f "$where"; then
CTYPESGEN="$where"
elif test -f "$where/bin/ctypesgen.py"; then
CTYPESGEN="$where/bin/ctypesgen.py"
else
CTYPESGEN="$where/ctypesgen.py"
fi
if test ! -f "$CTYPESGEN" || test ! -x "$CTYPESGEN"; then
as_fn_error $? "Could not find ctypesgen at $where/ctypesgen.py or at
$where/bin/ctypesgen.py" "$LINENO" 5
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CTYPESGEN" >&5
$as_echo "$CTYPESGEN" >&6; }
fi
fi
;;
"yes")
where=check
CTYPESGEN=none
if test $where = check; then
# Extract the first word of ""ctypesgen.py"", so it can be a program name with args.
set dummy "ctypesgen.py"; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_CTYPESGEN+:} false; then :
$as_echo_n "(cached) " >&6
else
case $CTYPESGEN in
[\\/]* | ?:[\\/]*)
ac_cv_path_CTYPESGEN="$CTYPESGEN" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_CTYPESGEN="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
test -z "$ac_cv_path_CTYPESGEN" && ac_cv_path_CTYPESGEN="none"
;;
esac
fi
CTYPESGEN=$ac_cv_path_CTYPESGEN
if test -n "$CTYPESGEN"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CTYPESGEN" >&5
$as_echo "$CTYPESGEN" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
elif test $where != no; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ctypesgen.py" >&5
$as_echo_n "checking for ctypesgen.py... " >&6; }
if test -f "$where"; then
CTYPESGEN="$where"
elif test -f "$where/bin/ctypesgen.py"; then
CTYPESGEN="$where/bin/ctypesgen.py"
else
CTYPESGEN="$where/ctypesgen.py"
fi
if test ! -f "$CTYPESGEN" || test ! -x "$CTYPESGEN"; then
as_fn_error $? "Could not find ctypesgen at $where/ctypesgen.py or at
$where/bin/ctypesgen.py" "$LINENO" 5
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CTYPESGEN" >&5
$as_echo "$CTYPESGEN" >&6; }
fi
fi
;;
*)
where=$withval
CTYPESGEN=none
if test $where = check; then
# Extract the first word of ""ctypesgen.py"", so it can be a program name with args.
set dummy "ctypesgen.py"; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_CTYPESGEN+:} false; then :
$as_echo_n "(cached) " >&6
else
case $CTYPESGEN in
[\\/]* | ?:[\\/]*)
ac_cv_path_CTYPESGEN="$CTYPESGEN" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_CTYPESGEN="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
test -z "$ac_cv_path_CTYPESGEN" && ac_cv_path_CTYPESGEN="none"
;;
esac
fi
CTYPESGEN=$ac_cv_path_CTYPESGEN
if test -n "$CTYPESGEN"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CTYPESGEN" >&5
$as_echo "$CTYPESGEN" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
elif test $where != no; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ctypesgen.py" >&5
$as_echo_n "checking for ctypesgen.py... " >&6; }
if test -f "$where"; then
CTYPESGEN="$where"
elif test -f "$where/bin/ctypesgen.py"; then
CTYPESGEN="$where/bin/ctypesgen.py"
else
CTYPESGEN="$where/ctypesgen.py"
fi
if test ! -f "$CTYPESGEN" || test ! -x "$CTYPESGEN"; then
as_fn_error $? "Could not find ctypesgen at $where/ctypesgen.py or at
$where/bin/ctypesgen.py" "$LINENO" 5
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CTYPESGEN" >&5
$as_echo "$CTYPESGEN" >&6; }
fi
fi
;;
esac
else
where=check
CTYPESGEN=none
if test $where = check; then
# Extract the first word of ""ctypesgen.py"", so it can be a program name with args.
set dummy "ctypesgen.py"; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_CTYPESGEN+:} false; then :
$as_echo_n "(cached) " >&6
else
case $CTYPESGEN in
[\\/]* | ?:[\\/]*)
ac_cv_path_CTYPESGEN="$CTYPESGEN" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_CTYPESGEN="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
test -z "$ac_cv_path_CTYPESGEN" && ac_cv_path_CTYPESGEN="none"
;;
esac
fi
CTYPESGEN=$ac_cv_path_CTYPESGEN
if test -n "$CTYPESGEN"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CTYPESGEN" >&5
$as_echo "$CTYPESGEN" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
elif test $where != no; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ctypesgen.py" >&5
$as_echo_n "checking for ctypesgen.py... " >&6; }
if test -f "$where"; then
CTYPESGEN="$where"
elif test -f "$where/bin/ctypesgen.py"; then
CTYPESGEN="$where/bin/ctypesgen.py"
else
CTYPESGEN="$where/ctypesgen.py"
fi
if test ! -f "$CTYPESGEN" || test ! -x "$CTYPESGEN"; then
as_fn_error $? "Could not find ctypesgen at $where/ctypesgen.py or at
$where/bin/ctypesgen.py" "$LINENO" 5
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CTYPESGEN" >&5
$as_echo "$CTYPESGEN" >&6; }
fi
fi
fi
# Check whether --enable-runtime-module-search was given.
if test "${enable_runtime_module_search+set}" = set; then :
enableval=$enable_runtime_module_search;
if test "$enableval" = "yes"; then
use_dso=yes
if test "$svn_enable_shared" = "no"; then
as_fn_error $? "--enable-runtime-module-search conflicts with --disable-shared" "$LINENO" 5
fi
$as_echo "#define SVN_USE_DSO 1" >>confdefs.h
fi
fi
if test "$svn_enable_shared" = "no" || test "$use_dso" != "yes"; then
$as_echo "#define SVN_LIBSVN_CLIENT_LINKS_RA_LOCAL 1" >>confdefs.h
svn_ra_lib_deps="\$(RA_LOCAL_DEPS)"
svn_ra_lib_install_deps="install-ramod-lib"
svn_ra_lib_link="\$(RA_LOCAL_LINK)"
$as_echo "#define SVN_LIBSVN_CLIENT_LINKS_RA_SVN 1" >>confdefs.h
svn_ra_lib_deps="$svn_ra_lib_deps \$(RA_SVN_DEPS)"
svn_ra_lib_link="$svn_ra_lib_link \$(RA_SVN_LINK)"
if test "$svn_lib_serf" = "yes"; then
$as_echo "#define SVN_LIBSVN_CLIENT_LINKS_RA_SERF 1" >>confdefs.h
svn_ra_lib_deps="$svn_ra_lib_deps \$(RA_SERF_DEPS)"
svn_ra_lib_install_deps="$svn_ra_lib_install_deps install-serf-lib"
svn_ra_lib_link="$svn_ra_lib_link \$(RA_SERF_LINK)"
fi
SVN_RA_LIB_DEPS=$svn_ra_lib_deps
SVN_RA_LIB_INSTALL_DEPS=$svn_ra_lib_install_deps
SVN_RA_LIB_LINK=$svn_ra_lib_link
$as_echo "#define SVN_LIBSVN_FS_LINKS_FS_FS 1" >>confdefs.h
svn_fs_lib_deps="\$(FS_FS_DEPS)"
svn_fs_lib_install_deps="install-fsmod-lib"
svn_fs_lib_link="\$(FS_FS_LINK)"
if test "$svn_lib_berkeley_db" = "yes"; then
$as_echo "#define SVN_LIBSVN_FS_LINKS_FS_BASE 1" >>confdefs.h
svn_fs_lib_deps="$svn_fs_lib_deps \$(FS_BASE_DEPS)"
svn_fs_lib_install_deps="$svn_fs_lib_install_deps install-bdb-lib"
svn_fs_lib_link="$svn_fs_lib_link \$(FS_BASE_LINK)"
fi
SVN_FS_LIB_DEPS=$svn_fs_lib_deps
SVN_FS_LIB_INSTALL_DEPS=$svn_fs_lib_install_deps
SVN_FS_LIB_LINK=$svn_fs_lib_link
fi
# ==== JavaHL ================================================================
do_javahl_build=no
# Check whether --enable-javahl was given.
if test "${enable_javahl+set}" = set; then :
enableval=$enable_javahl; if test "$enableval" = "yes" ; then
do_javahl_build="yes"
fi
fi
JAVAHL_OBJDIR=""
INSTALL_EXTRA_JAVAHL_LIB=""
FIX_JAVAHL_LIB=""
JAVAHL_TESTS_TARGET=""
JAVAHL_COMPAT_TESTS_TARGET=""
LT_CXX_LIBADD=""
if test "$do_javahl_build" = "yes"; then
if test "$JDK_SUITABLE" = "no"; then
as_fn_error $? "Cannot compile JavaHL without a suitable JDK.
Please specify a suitable JDK using the --with-jdk option." "$LINENO" 5
fi
JAVAHL_OBJDIR='$(libsvnjavahl_PATH)/.libs'
os_arch=`uname`
if test "$os_arch" = "Darwin"; then
INSTALL_EXTRA_JAVAHL_LIB='ln -sf $(libdir)/libsvnjavahl-1.dylib $(libdir)/libsvnjavahl-1.jnilib'
FIX_JAVAHL_LIB="ln -sf libsvnjavahl-1.dylib $JAVAHL_OBJDIR/libsvnjavahl-1.jnilib"
fi
# This segment (and the rest of r10800) is very likely unnecessary
# with libtool 1.5, which automatically adds libstdc++ as a
# dependency to the C++ libraries it builds. So at some future time
# when autogen.sh requires libtool 1.5 or higher, we can get rid of
# it.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for additional flags to link C++ libraries" >&5
$as_echo_n "checking for additional flags to link C++ libraries... " >&6; }
if test "x$ac_compiler_gnu" = "xyes"; then
LT_CXX_LIBADD="-lstdc++"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $LT_CXX_LIBADD" >&5
$as_echo "$LT_CXX_LIBADD" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
$as_echo "none needed" >&6; }
fi
fi
# Check whether --with-junit was given.
if test "${with_junit+set}" = set; then :
withval=$with_junit;
if test "$withval" != "no"; then
if test -n "$JAVA_CLASSPATH"; then
JAVA_CLASSPATH="$withval:$JAVA_CLASSPATH"
else
JAVA_CLASSPATH="$withval"
fi
JAVAHL_TESTS_TARGET="javahl-tests"
JAVAHL_COMPAT_TESTS_TARGET="javahl-compat-tests"
fi
fi
# ==== Miscellaneous bits ====================================================
# Strip '-no-cpp-precomp' from CPPFLAGS for the clang compiler
### I think we get this flag from APR, so the fix probably belongs there
if test "$CC" = "clang"; then
CPPFLAGS=`echo "$CPPFLAGS" | $SED -e 's/-no-cpp-precomp //'`
fi
# Need to strip '-no-cpp-precomp' from CPPFLAGS for SWIG as well.
SWIG_CPPFLAGS="$CPPFLAGS"
SWIG_CPPFLAGS=`echo "$SWIG_CPPFLAGS" | $SED -e 's/-no-cpp-precomp //'`
cat >>confdefs.h <<_ACEOF
#define SVN_PATH_LOCAL_SEPARATOR '/'
_ACEOF
cat >>confdefs.h <<_ACEOF
#define SVN_NULL_DEVICE_NAME "/dev/null"
_ACEOF
DEFAULT_FS_TYPE="fsfs"
cat >>confdefs.h <<_ACEOF
#define DEFAULT_FS_TYPE "$DEFAULT_FS_TYPE"
_ACEOF
DEFAULT_HTTP_LIBRARY="serf"
cat >>confdefs.h <<_ACEOF
#define DEFAULT_HTTP_LIBRARY "$DEFAULT_HTTP_LIBRARY"
_ACEOF
# BSD/OS (BSDi) needs to use a different include syntax in Makefile
INCLUDE_OUTPUTS="include \$(top_srcdir)/build-outputs.mk"
case "$host" in
*bsdi*)
# Check whether they've installed GNU make
if ! make --version > /dev/null 2>&1; then
# BSDi make
INCLUDE_OUTPUTS=".include \"\$(top_srcdir)/build-outputs.mk\""
fi
;;
esac
# ==== Detection complete - output and run config.status =====================
ac_config_headers="$ac_config_headers subversion/svn_private_config.h.tmp:subversion/svn_private_config.h.in"
ac_config_commands="$ac_config_commands svn_private_config.h.tmp"
ac_config_files="$ac_config_files Makefile"
SVN_CONFIG_SCRIPT_FILES="$SVN_CONFIG_SCRIPT_FILES tools/backup/hot-backup.py"
ac_config_files="$ac_config_files tools/backup/hot-backup.py"
SVN_CONFIG_SCRIPT_FILES="$SVN_CONFIG_SCRIPT_FILES tools/hook-scripts/commit-access-control.pl"
ac_config_files="$ac_config_files tools/hook-scripts/commit-access-control.pl"
SVN_CONFIG_SCRIPT_FILES="$SVN_CONFIG_SCRIPT_FILES subversion/bindings/swig/perl/native/Makefile.PL"
ac_config_files="$ac_config_files subversion/bindings/swig/perl/native/Makefile.PL"
if test -e packages/solaris/pkginfo.in; then
SVN_CONFIG_SCRIPT_FILES="$SVN_CONFIG_SCRIPT_FILES packages/solaris/pkginfo"
ac_config_files="$ac_config_files packages/solaris/pkginfo"
fi
# Ensure that SWIG is checked after reconfiguration.
rm -f .swig_checked
cat >>confdefs.h <<_ACEOF
#define SVN_BUILD_HOST "${host}"
_ACEOF
cat >>confdefs.h <<_ACEOF
#define SVN_BUILD_TARGET "${target}"
_ACEOF
cat >confcache <<\_ACEOF
# This file is a shell script that caches the results of configure
# tests run on this system so they can be shared between configure
# scripts and configure runs, see configure's option --config-cache.
# It is not useful on other systems. If it contains results you don't
# want to keep, you may remove or edit it.
#
# config.status only pays attention to the cache file if you give it
# the --recheck option to rerun configure.
#
# `ac_cv_env_foo' variables (set or unset) will be overridden when
# loading this file, other *unset* `ac_cv_foo' will be assigned the
# following values.
_ACEOF
# The following way of writing the cache mishandles newlines in values,
# but we know of no workaround that is simple, portable, and efficient.
# So, we kill variables containing newlines.
# Ultrix sh set writes to stderr and can't be redirected directly,
# and sets the high bit in the cache file unless we assign to the vars.
(
for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
eval ac_val=\$$ac_var
case $ac_val in #(
*${as_nl}*)
case $ac_var in #(
*_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
esac
case $ac_var in #(
_ | IFS | as_nl) ;; #(
BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
*) { eval $ac_var=; unset $ac_var;} ;;
esac ;;
esac
done
(set) 2>&1 |
case $as_nl`(ac_space=' '; set) 2>&1` in #(
*${as_nl}ac_space=\ *)
# `set' does not quote correctly, so add quotes: double-quote
# substitution turns \\\\ into \\, and sed turns \\ into \.
sed -n \
"s/'/'\\\\''/g;
s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
;; #(
*)
# `set' quotes correctly as required by POSIX, so do not add quotes.
sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
;;
esac |
sort
) |
sed '
/^ac_cv_env_/b end
t clear
:clear
s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
t end
s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
:end' >>confcache
if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
if test -w "$cache_file"; then
if test "x$cache_file" != "x/dev/null"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
$as_echo "$as_me: updating cache $cache_file" >&6;}
if test ! -f "$cache_file" || test -h "$cache_file"; then
cat confcache >"$cache_file"
else
case $cache_file in #(
*/* | ?:*)
mv -f confcache "$cache_file"$$ &&
mv -f "$cache_file"$$ "$cache_file" ;; #(
*)
mv -f confcache "$cache_file" ;;
esac
fi
fi
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
fi
fi
rm -f confcache
test "x$prefix" = xNONE && prefix=$ac_default_prefix
# Let make expand exec_prefix.
test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
DEFS=-DHAVE_CONFIG_H
ac_libobjs=
ac_ltlibobjs=
U=
for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
# 1. Remove the extension, and $U if already installed.
ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
# 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR
# will be set to the directory where LIBOBJS objects are built.
as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
done
LIBOBJS=$ac_libobjs
LTLIBOBJS=$ac_ltlibobjs
: "${CONFIG_STATUS=./config.status}"
ac_write_fail=0
ac_clean_files_save=$ac_clean_files
ac_clean_files="$ac_clean_files $CONFIG_STATUS"
{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
as_write_fail=0
cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
#! $SHELL
# Generated by $as_me.
# Run this file to recreate the current configuration.
# Compiler output produced by configure, useful for debugging
# configure, is in config.log if it exists.
debug=false
ac_cs_recheck=false
ac_cs_silent=false
SHELL=\${CONFIG_SHELL-$SHELL}
export SHELL
_ASEOF
cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
## -------------------- ##
## M4sh Initialization. ##
## -------------------- ##
# Be more Bourne compatible
DUALCASE=1; export DUALCASE # for MKS sh
if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
emulate sh
NULLCMD=:
# Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
# is contrary to our usage. Disable this feature.
alias -g '${1+"$@"}'='"$@"'
setopt NO_GLOB_SUBST
else
case `(set -o) 2>/dev/null` in #(
*posix*) :
set -o posix ;; #(
*) :
;;
esac
fi
as_nl='
'
export as_nl
# Printing a long string crashes Solaris 7 /usr/bin/printf.
as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
# Prefer a ksh shell builtin over an external printf program on Solaris,
# but without wasting forks for bash or zsh.
if test -z "$BASH_VERSION$ZSH_VERSION" \
&& (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
as_echo='print -r --'
as_echo_n='print -rn --'
elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
as_echo='printf %s\n'
as_echo_n='printf %s'
else
if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
as_echo_n='/usr/ucb/echo -n'
else
as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
as_echo_n_body='eval
arg=$1;
case $arg in #(
*"$as_nl"*)
expr "X$arg" : "X\\(.*\\)$as_nl";
arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
esac;
expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
'
export as_echo_n_body
as_echo_n='sh -c $as_echo_n_body as_echo'
fi
export as_echo_body
as_echo='sh -c $as_echo_body as_echo'
fi
# The user is always right.
if test "${PATH_SEPARATOR+set}" != set; then
PATH_SEPARATOR=:
(PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
(PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
PATH_SEPARATOR=';'
}
fi
# IFS
# We need space, tab and new line, in precisely that order. Quoting is
# there to prevent editors from complaining about space-tab.
# (If _AS_PATH_WALK were called with IFS unset, it would disable word
# splitting by setting IFS to empty value.)
IFS=" "" $as_nl"
# Find who we are. Look in the path if we contain no directory separator.
as_myself=
case $0 in #((
*[\\/]* ) as_myself=$0 ;;
*) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
done
IFS=$as_save_IFS
;;
esac
# We did not find ourselves, most probably we were run as `sh COMMAND'
# in which case we are not to be found in the path.
if test "x$as_myself" = x; then
as_myself=$0
fi
if test ! -f "$as_myself"; then
$as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
exit 1
fi
# Unset variables that we do not need and which cause bugs (e.g. in
# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
# suppresses any "Segmentation fault" message there. '((' could
# trigger a bug in pdksh 5.2.14.
for as_var in BASH_ENV ENV MAIL MAILPATH
do eval test x\${$as_var+set} = xset \
&& ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
done
PS1='$ '
PS2='> '
PS4='+ '
# NLS nuisances.
LC_ALL=C
export LC_ALL
LANGUAGE=C
export LANGUAGE
# CDPATH.
(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
# as_fn_error STATUS ERROR [LINENO LOG_FD]
# ----------------------------------------
# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
# script with STATUS, using 1 if that was 0.
as_fn_error ()
{
as_status=$1; test $as_status -eq 0 && as_status=1
if test "$4"; then
as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
$as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
fi
$as_echo "$as_me: error: $2" >&2
as_fn_exit $as_status
} # as_fn_error
# as_fn_set_status STATUS
# -----------------------
# Set $? to STATUS, without forking.
as_fn_set_status ()
{
return $1
} # as_fn_set_status
# as_fn_exit STATUS
# -----------------
# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
as_fn_exit ()
{
set +e
as_fn_set_status $1
exit $1
} # as_fn_exit
# as_fn_unset VAR
# ---------------
# Portably unset VAR.
as_fn_unset ()
{
{ eval $1=; unset $1;}
}
as_unset=as_fn_unset
# as_fn_append VAR VALUE
# ----------------------
# Append the text in VALUE to the end of the definition contained in VAR. Take
# advantage of any shell optimizations that allow amortized linear growth over
# repeated appends, instead of the typical quadratic growth present in naive
# implementations.
if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
eval 'as_fn_append ()
{
eval $1+=\$2
}'
else
as_fn_append ()
{
eval $1=\$$1\$2
}
fi # as_fn_append
# as_fn_arith ARG...
# ------------------
# Perform arithmetic evaluation on the ARGs, and store the result in the
# global $as_val. Take advantage of shells that can avoid forks. The arguments
# must be portable across $(()) and expr.
if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
eval 'as_fn_arith ()
{
as_val=$(( $* ))
}'
else
as_fn_arith ()
{
as_val=`expr "$@" || test $? -eq 1`
}
fi # as_fn_arith
if expr a : '\(a\)' >/dev/null 2>&1 &&
test "X`expr 00001 : '.*\(...\)'`" = X001; then
as_expr=expr
else
as_expr=false
fi
if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
as_basename=basename
else
as_basename=false
fi
if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
as_dirname=dirname
else
as_dirname=false
fi
as_me=`$as_basename -- "$0" ||
$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
X"$0" : 'X\(//\)$' \| \
X"$0" : 'X\(/\)' \| . 2>/dev/null ||
$as_echo X/"$0" |
sed '/^.*\/\([^/][^/]*\)\/*$/{
s//\1/
q
}
/^X\/\(\/\/\)$/{
s//\1/
q
}
/^X\/\(\/\).*/{
s//\1/
q
}
s/.*/./; q'`
# Avoid depending upon Character Ranges.
as_cr_letters='abcdefghijklmnopqrstuvwxyz'
as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
as_cr_Letters=$as_cr_letters$as_cr_LETTERS
as_cr_digits='0123456789'
as_cr_alnum=$as_cr_Letters$as_cr_digits
ECHO_C= ECHO_N= ECHO_T=
case `echo -n x` in #(((((
-n*)
case `echo 'xy\c'` in
*c*) ECHO_T=' ';; # ECHO_T is single tab character.
xy) ECHO_C='\c';;
*) echo `echo ksh88 bug on AIX 6.1` > /dev/null
ECHO_T=' ';;
esac;;
*)
ECHO_N='-n';;
esac
rm -f conf$$ conf$$.exe conf$$.file
if test -d conf$$.dir; then
rm -f conf$$.dir/conf$$.file
else
rm -f conf$$.dir
mkdir conf$$.dir 2>/dev/null
fi
if (echo >conf$$.file) 2>/dev/null; then
if ln -s conf$$.file conf$$ 2>/dev/null; then
as_ln_s='ln -s'
# ... but there are two gotchas:
# 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
# 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
# In both cases, we have to default to `cp -pR'.
ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
as_ln_s='cp -pR'
elif ln conf$$.file conf$$ 2>/dev/null; then
as_ln_s=ln
else
as_ln_s='cp -pR'
fi
else
as_ln_s='cp -pR'
fi
rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
rmdir conf$$.dir 2>/dev/null
# as_fn_mkdir_p
# -------------
# Create "$as_dir" as a directory, including parents if necessary.
as_fn_mkdir_p ()
{
case $as_dir in #(
-*) as_dir=./$as_dir;;
esac
test -d "$as_dir" || eval $as_mkdir_p || {
as_dirs=
while :; do
case $as_dir in #(
*\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
*) as_qdir=$as_dir;;
esac
as_dirs="'$as_qdir' $as_dirs"
as_dir=`$as_dirname -- "$as_dir" ||
$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
X"$as_dir" : 'X\(//\)[^/]' \| \
X"$as_dir" : 'X\(//\)$' \| \
X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
$as_echo X"$as_dir" |
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
s//\1/
q
}
/^X\(\/\/\)[^/].*/{
s//\1/
q
}
/^X\(\/\/\)$/{
s//\1/
q
}
/^X\(\/\).*/{
s//\1/
q
}
s/.*/./; q'`
test -d "$as_dir" && break
done
test -z "$as_dirs" || eval "mkdir $as_dirs"
} || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
} # as_fn_mkdir_p
if mkdir -p . 2>/dev/null; then
as_mkdir_p='mkdir -p "$as_dir"'
else
test -d ./-p && rmdir ./-p
as_mkdir_p=false
fi
# as_fn_executable_p FILE
# -----------------------
# Test if FILE is an executable regular file.
as_fn_executable_p ()
{
test -f "$1" && test -x "$1"
} # as_fn_executable_p
as_test_x='test -x'
as_executable_p=as_fn_executable_p
# Sed expression to map a string onto a valid CPP name.
as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
# Sed expression to map a string onto a valid variable name.
as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
exec 6>&1
## ----------------------------------- ##
## Main body of $CONFIG_STATUS script. ##
## ----------------------------------- ##
_ASEOF
test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# Save the log message, to keep $0 and so on meaningful, and to
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by subversion $as_me 1.8.10, which was
+This file was extended by subversion $as_me 1.8.14, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
CONFIG_HEADERS = $CONFIG_HEADERS
CONFIG_LINKS = $CONFIG_LINKS
CONFIG_COMMANDS = $CONFIG_COMMANDS
$ $0 $@
on `(hostname || uname -n) 2>/dev/null | sed 1q`
"
_ACEOF
case $ac_config_files in *"
"*) set x $ac_config_files; shift; ac_config_files=$*;;
esac
case $ac_config_headers in *"
"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
esac
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
# Files that config.status was made for.
config_files="$ac_config_files"
config_headers="$ac_config_headers"
config_commands="$ac_config_commands"
_ACEOF
cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
ac_cs_usage="\
\`$as_me' instantiates files and other configuration actions
from templates according to the current configuration. Unless the files
and actions are specified as TAGs, all are instantiated by default.
Usage: $0 [OPTION]... [TAG]...
-h, --help print this help, then exit
-V, --version print version number and configuration settings, then exit
--config print configuration, then exit
-q, --quiet, --silent
do not print progress messages
-d, --debug don't remove temporary files
--recheck update $as_me by reconfiguring in the same conditions
--file=FILE[:TEMPLATE]
instantiate the configuration file FILE
--header=FILE[:TEMPLATE]
instantiate the configuration header FILE
Configuration files:
$config_files
Configuration headers:
$config_headers
Configuration commands:
$config_commands
Report bugs to <http://subversion.apache.org/>."
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-subversion config.status 1.8.10
+subversion config.status 1.8.14
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
Copyright (C) 2012 Free Software Foundation, Inc.
This config.status script is free software; the Free Software Foundation
gives unlimited permission to copy, distribute and modify it."
ac_pwd='$ac_pwd'
srcdir='$srcdir'
INSTALL='$INSTALL'
AWK='$AWK'
test -n "\$AWK" || AWK=awk
_ACEOF
cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# The default lists apply if the user does not specify any file.
ac_need_defaults=:
while test $# != 0
do
case $1 in
--*=?*)
ac_option=`expr "X$1" : 'X\([^=]*\)='`
ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
ac_shift=:
;;
--*=)
ac_option=`expr "X$1" : 'X\([^=]*\)='`
ac_optarg=
ac_shift=:
;;
*)
ac_option=$1
ac_optarg=$2
ac_shift=shift
;;
esac
case $ac_option in
# Handling of the options.
-recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
ac_cs_recheck=: ;;
--version | --versio | --versi | --vers | --ver | --ve | --v | -V )
$as_echo "$ac_cs_version"; exit ;;
--config | --confi | --conf | --con | --co | --c )
$as_echo "$ac_cs_config"; exit ;;
--debug | --debu | --deb | --de | --d | -d )
debug=: ;;
--file | --fil | --fi | --f )
$ac_shift
case $ac_optarg in
*\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
'') as_fn_error $? "missing file argument" ;;
esac
as_fn_append CONFIG_FILES " '$ac_optarg'"
ac_need_defaults=false;;
--header | --heade | --head | --hea )
$ac_shift
case $ac_optarg in
*\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
esac
as_fn_append CONFIG_HEADERS " '$ac_optarg'"
ac_need_defaults=false;;
--he | --h)
# Conflict between --help and --header
as_fn_error $? "ambiguous option: \`$1'
Try \`$0 --help' for more information.";;
--help | --hel | -h )
$as_echo "$ac_cs_usage"; exit ;;
-q | -quiet | --quiet | --quie | --qui | --qu | --q \
| -silent | --silent | --silen | --sile | --sil | --si | --s)
ac_cs_silent=: ;;
# This is an error.
-*) as_fn_error $? "unrecognized option: \`$1'
Try \`$0 --help' for more information." ;;
*) as_fn_append ac_config_targets " $1"
ac_need_defaults=false ;;
esac
shift
done
ac_configure_extra_args=
if $ac_cs_silent; then
exec 6>/dev/null
ac_configure_extra_args="$ac_configure_extra_args --silent"
fi
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
if \$ac_cs_recheck; then
set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
shift
\$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
CONFIG_SHELL='$SHELL'
export CONFIG_SHELL
exec "\$@"
fi
_ACEOF
cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
exec 5>>config.log
{
echo
sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
## Running $as_me. ##
_ASBOX
$as_echo "$ac_log"
} >&5
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
#
# INIT-COMMANDS
#
# The HP-UX ksh and POSIX shell print the target directory to stdout
# if CDPATH is set.
(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
sed_quote_subst='$sed_quote_subst'
double_quote_subst='$double_quote_subst'
delay_variable_subst='$delay_variable_subst'
macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`'
macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`'
enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`'
enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`'
pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`'
enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`'
SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`'
ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`'
PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`'
host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`'
host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`'
host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`'
build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`'
build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`'
build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`'
SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`'
Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`'
GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`'
EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`'
FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`'
LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`'
NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`'
LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`'
max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`'
ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`'
exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`'
lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`'
lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`'
lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`'
lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_to_host_file_cmd" | $SED "$delay_single_quote_subst"`'
lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`'
reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`'
reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`'
OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`'
deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`'
file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`'
file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`'
want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`'
DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`'
sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`'
AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`'
AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`'
archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`'
STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`'
RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`'
old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`'
old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`'
old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`'
lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`'
CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`'
CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`'
compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`'
GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`'
lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`'
lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_import='`$ECHO "$lt_cv_sys_global_symbol_to_import" | $SED "$delay_single_quote_subst"`'
lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`'
lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`'
+lt_cv_nm_interface='`$ECHO "$lt_cv_nm_interface" | $SED "$delay_single_quote_subst"`'
nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`'
lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`'
+lt_cv_truncate_bin='`$ECHO "$lt_cv_truncate_bin" | $SED "$delay_single_quote_subst"`'
objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`'
MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`'
lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`'
lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`'
lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`'
lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`'
lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`'
need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`'
MANIFEST_TOOL='`$ECHO "$MANIFEST_TOOL" | $SED "$delay_single_quote_subst"`'
DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`'
NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`'
LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`'
OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`'
OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`'
libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`'
shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`'
extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`'
archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`'
enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`'
export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`'
whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`'
compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`'
old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`'
old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`'
archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`'
archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`'
module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`'
module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`'
with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`'
allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`'
no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`'
hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`'
hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`'
hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`'
hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`'
hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`'
hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`'
hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`'
inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`'
link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`'
always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`'
export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`'
exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`'
include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`'
prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`'
postlink_cmds='`$ECHO "$postlink_cmds" | $SED "$delay_single_quote_subst"`'
file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`'
variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`'
need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`'
need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`'
version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`'
runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`'
shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`'
shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`'
libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`'
library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`'
soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`'
install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`'
postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`'
postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`'
finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`'
finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`'
hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`'
sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`'
sys_lib_dlsearch_path_spec='`$ECHO "$sys_lib_dlsearch_path_spec" | $SED "$delay_single_quote_subst"`'
hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`'
enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`'
enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`'
enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`'
old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`'
striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`'
compiler_lib_search_dirs='`$ECHO "$compiler_lib_search_dirs" | $SED "$delay_single_quote_subst"`'
predep_objects='`$ECHO "$predep_objects" | $SED "$delay_single_quote_subst"`'
postdep_objects='`$ECHO "$postdep_objects" | $SED "$delay_single_quote_subst"`'
predeps='`$ECHO "$predeps" | $SED "$delay_single_quote_subst"`'
postdeps='`$ECHO "$postdeps" | $SED "$delay_single_quote_subst"`'
compiler_lib_search_path='`$ECHO "$compiler_lib_search_path" | $SED "$delay_single_quote_subst"`'
LD_CXX='`$ECHO "$LD_CXX" | $SED "$delay_single_quote_subst"`'
reload_flag_CXX='`$ECHO "$reload_flag_CXX" | $SED "$delay_single_quote_subst"`'
reload_cmds_CXX='`$ECHO "$reload_cmds_CXX" | $SED "$delay_single_quote_subst"`'
old_archive_cmds_CXX='`$ECHO "$old_archive_cmds_CXX" | $SED "$delay_single_quote_subst"`'
compiler_CXX='`$ECHO "$compiler_CXX" | $SED "$delay_single_quote_subst"`'
GCC_CXX='`$ECHO "$GCC_CXX" | $SED "$delay_single_quote_subst"`'
lt_prog_compiler_no_builtin_flag_CXX='`$ECHO "$lt_prog_compiler_no_builtin_flag_CXX" | $SED "$delay_single_quote_subst"`'
lt_prog_compiler_pic_CXX='`$ECHO "$lt_prog_compiler_pic_CXX" | $SED "$delay_single_quote_subst"`'
lt_prog_compiler_wl_CXX='`$ECHO "$lt_prog_compiler_wl_CXX" | $SED "$delay_single_quote_subst"`'
lt_prog_compiler_static_CXX='`$ECHO "$lt_prog_compiler_static_CXX" | $SED "$delay_single_quote_subst"`'
lt_cv_prog_compiler_c_o_CXX='`$ECHO "$lt_cv_prog_compiler_c_o_CXX" | $SED "$delay_single_quote_subst"`'
archive_cmds_need_lc_CXX='`$ECHO "$archive_cmds_need_lc_CXX" | $SED "$delay_single_quote_subst"`'
enable_shared_with_static_runtimes_CXX='`$ECHO "$enable_shared_with_static_runtimes_CXX" | $SED "$delay_single_quote_subst"`'
export_dynamic_flag_spec_CXX='`$ECHO "$export_dynamic_flag_spec_CXX" | $SED "$delay_single_quote_subst"`'
whole_archive_flag_spec_CXX='`$ECHO "$whole_archive_flag_spec_CXX" | $SED "$delay_single_quote_subst"`'
compiler_needs_object_CXX='`$ECHO "$compiler_needs_object_CXX" | $SED "$delay_single_quote_subst"`'
old_archive_from_new_cmds_CXX='`$ECHO "$old_archive_from_new_cmds_CXX" | $SED "$delay_single_quote_subst"`'
old_archive_from_expsyms_cmds_CXX='`$ECHO "$old_archive_from_expsyms_cmds_CXX" | $SED "$delay_single_quote_subst"`'
archive_cmds_CXX='`$ECHO "$archive_cmds_CXX" | $SED "$delay_single_quote_subst"`'
archive_expsym_cmds_CXX='`$ECHO "$archive_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`'
module_cmds_CXX='`$ECHO "$module_cmds_CXX" | $SED "$delay_single_quote_subst"`'
module_expsym_cmds_CXX='`$ECHO "$module_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`'
with_gnu_ld_CXX='`$ECHO "$with_gnu_ld_CXX" | $SED "$delay_single_quote_subst"`'
allow_undefined_flag_CXX='`$ECHO "$allow_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`'
no_undefined_flag_CXX='`$ECHO "$no_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`'
hardcode_libdir_flag_spec_CXX='`$ECHO "$hardcode_libdir_flag_spec_CXX" | $SED "$delay_single_quote_subst"`'
hardcode_libdir_separator_CXX='`$ECHO "$hardcode_libdir_separator_CXX" | $SED "$delay_single_quote_subst"`'
hardcode_direct_CXX='`$ECHO "$hardcode_direct_CXX" | $SED "$delay_single_quote_subst"`'
hardcode_direct_absolute_CXX='`$ECHO "$hardcode_direct_absolute_CXX" | $SED "$delay_single_quote_subst"`'
hardcode_minus_L_CXX='`$ECHO "$hardcode_minus_L_CXX" | $SED "$delay_single_quote_subst"`'
hardcode_shlibpath_var_CXX='`$ECHO "$hardcode_shlibpath_var_CXX" | $SED "$delay_single_quote_subst"`'
hardcode_automatic_CXX='`$ECHO "$hardcode_automatic_CXX" | $SED "$delay_single_quote_subst"`'
inherit_rpath_CXX='`$ECHO "$inherit_rpath_CXX" | $SED "$delay_single_quote_subst"`'
link_all_deplibs_CXX='`$ECHO "$link_all_deplibs_CXX" | $SED "$delay_single_quote_subst"`'
always_export_symbols_CXX='`$ECHO "$always_export_symbols_CXX" | $SED "$delay_single_quote_subst"`'
export_symbols_cmds_CXX='`$ECHO "$export_symbols_cmds_CXX" | $SED "$delay_single_quote_subst"`'
exclude_expsyms_CXX='`$ECHO "$exclude_expsyms_CXX" | $SED "$delay_single_quote_subst"`'
include_expsyms_CXX='`$ECHO "$include_expsyms_CXX" | $SED "$delay_single_quote_subst"`'
prelink_cmds_CXX='`$ECHO "$prelink_cmds_CXX" | $SED "$delay_single_quote_subst"`'
postlink_cmds_CXX='`$ECHO "$postlink_cmds_CXX" | $SED "$delay_single_quote_subst"`'
file_list_spec_CXX='`$ECHO "$file_list_spec_CXX" | $SED "$delay_single_quote_subst"`'
hardcode_action_CXX='`$ECHO "$hardcode_action_CXX" | $SED "$delay_single_quote_subst"`'
compiler_lib_search_dirs_CXX='`$ECHO "$compiler_lib_search_dirs_CXX" | $SED "$delay_single_quote_subst"`'
predep_objects_CXX='`$ECHO "$predep_objects_CXX" | $SED "$delay_single_quote_subst"`'
postdep_objects_CXX='`$ECHO "$postdep_objects_CXX" | $SED "$delay_single_quote_subst"`'
predeps_CXX='`$ECHO "$predeps_CXX" | $SED "$delay_single_quote_subst"`'
postdeps_CXX='`$ECHO "$postdeps_CXX" | $SED "$delay_single_quote_subst"`'
compiler_lib_search_path_CXX='`$ECHO "$compiler_lib_search_path_CXX" | $SED "$delay_single_quote_subst"`'
LTCC='$LTCC'
LTCFLAGS='$LTCFLAGS'
compiler='$compiler_DEFAULT'
# A function that is used when there is no print builtin or printf.
func_fallback_echo ()
{
eval 'cat <<_LTECHO_EOF
\$1
_LTECHO_EOF'
}
# Quote evaled strings.
for var in SHELL \
ECHO \
PATH_SEPARATOR \
SED \
GREP \
EGREP \
FGREP \
LD \
NM \
LN_S \
lt_SP2NL \
lt_NL2SP \
reload_flag \
OBJDUMP \
deplibs_check_method \
file_magic_cmd \
file_magic_glob \
want_nocaseglob \
DLLTOOL \
sharedlib_from_linklib_cmd \
AR \
AR_FLAGS \
archiver_list_spec \
STRIP \
RANLIB \
CC \
CFLAGS \
compiler \
lt_cv_sys_global_symbol_pipe \
lt_cv_sys_global_symbol_to_cdecl \
+lt_cv_sys_global_symbol_to_import \
lt_cv_sys_global_symbol_to_c_name_address \
lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \
+lt_cv_nm_interface \
nm_file_list_spec \
+lt_cv_truncate_bin \
lt_prog_compiler_no_builtin_flag \
lt_prog_compiler_pic \
lt_prog_compiler_wl \
lt_prog_compiler_static \
lt_cv_prog_compiler_c_o \
need_locks \
MANIFEST_TOOL \
DSYMUTIL \
NMEDIT \
LIPO \
OTOOL \
OTOOL64 \
shrext_cmds \
export_dynamic_flag_spec \
whole_archive_flag_spec \
compiler_needs_object \
with_gnu_ld \
allow_undefined_flag \
no_undefined_flag \
hardcode_libdir_flag_spec \
hardcode_libdir_separator \
exclude_expsyms \
include_expsyms \
file_list_spec \
variables_saved_for_relink \
libname_spec \
library_names_spec \
soname_spec \
install_override_mode \
finish_eval \
old_striplib \
striplib \
compiler_lib_search_dirs \
predep_objects \
postdep_objects \
predeps \
postdeps \
compiler_lib_search_path \
LD_CXX \
reload_flag_CXX \
compiler_CXX \
lt_prog_compiler_no_builtin_flag_CXX \
lt_prog_compiler_pic_CXX \
lt_prog_compiler_wl_CXX \
lt_prog_compiler_static_CXX \
lt_cv_prog_compiler_c_o_CXX \
export_dynamic_flag_spec_CXX \
whole_archive_flag_spec_CXX \
compiler_needs_object_CXX \
with_gnu_ld_CXX \
allow_undefined_flag_CXX \
no_undefined_flag_CXX \
hardcode_libdir_flag_spec_CXX \
hardcode_libdir_separator_CXX \
exclude_expsyms_CXX \
include_expsyms_CXX \
file_list_spec_CXX \
compiler_lib_search_dirs_CXX \
predep_objects_CXX \
postdep_objects_CXX \
predeps_CXX \
postdeps_CXX \
compiler_lib_search_path_CXX; do
case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
*[\\\\\\\`\\"\\\$]*)
- eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\""
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes
;;
*)
eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
;;
esac
done
# Double-quote double-evaled strings.
for var in reload_cmds \
old_postinstall_cmds \
old_postuninstall_cmds \
old_archive_cmds \
extract_expsyms_cmds \
old_archive_from_new_cmds \
old_archive_from_expsyms_cmds \
archive_cmds \
archive_expsym_cmds \
module_cmds \
module_expsym_cmds \
export_symbols_cmds \
prelink_cmds \
postlink_cmds \
postinstall_cmds \
postuninstall_cmds \
finish_cmds \
sys_lib_search_path_spec \
sys_lib_dlsearch_path_spec \
reload_cmds_CXX \
old_archive_cmds_CXX \
old_archive_from_new_cmds_CXX \
old_archive_from_expsyms_cmds_CXX \
archive_cmds_CXX \
archive_expsym_cmds_CXX \
module_cmds_CXX \
module_expsym_cmds_CXX \
export_symbols_cmds_CXX \
prelink_cmds_CXX \
postlink_cmds_CXX; do
case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
*[\\\\\\\`\\"\\\$]*)
- eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\""
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes
;;
*)
eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
;;
esac
done
ac_aux_dir='$ac_aux_dir'
-xsi_shell='$xsi_shell'
-lt_shell_append='$lt_shell_append'
-# See if we are running on zsh, and set the options which allow our
+# See if we are running on zsh, and set the options that allow our
# commands through without removal of \ escapes INIT.
-if test -n "\${ZSH_VERSION+set}" ; then
+if test -n "\${ZSH_VERSION+set}"; then
setopt NO_GLOB_SUBST
fi
PACKAGE='$PACKAGE'
VERSION='$VERSION'
- TIMESTAMP='$TIMESTAMP'
RM='$RM'
ofile='$ofile'
SED="$SED"
SVN_DB_HEADER="$SVN_DB_HEADER"
_ACEOF
cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# Handling of arguments.
for ac_config_target in $ac_config_targets
do
case $ac_config_target in
"libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;;
"subversion/svn_private_config.h.tmp") CONFIG_HEADERS="$CONFIG_HEADERS subversion/svn_private_config.h.tmp:subversion/svn_private_config.h.in" ;;
"svn_private_config.h.tmp") CONFIG_COMMANDS="$CONFIG_COMMANDS svn_private_config.h.tmp" ;;
"Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
"tools/backup/hot-backup.py") CONFIG_FILES="$CONFIG_FILES tools/backup/hot-backup.py" ;;
"tools/hook-scripts/commit-access-control.pl") CONFIG_FILES="$CONFIG_FILES tools/hook-scripts/commit-access-control.pl" ;;
"subversion/bindings/swig/perl/native/Makefile.PL") CONFIG_FILES="$CONFIG_FILES subversion/bindings/swig/perl/native/Makefile.PL" ;;
"packages/solaris/pkginfo") CONFIG_FILES="$CONFIG_FILES packages/solaris/pkginfo" ;;
*) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
esac
done
# If the user did not use the arguments to specify the items to instantiate,
# then the envvar interface is used. Set only those that are not.
# We use the long form for the default assignment because of an extremely
# bizarre bug on SunOS 4.1.3.
if $ac_need_defaults; then
test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
fi
# Have a temporary directory for convenience. Make it in the build tree
# simply because there is no reason against having it here, and in addition,
# creating and moving files from /tmp can sometimes cause problems.
# Hook for its removal unless debugging.
# Note that there is a small window in which the directory will not be cleaned:
# after its creation but before its name has been assigned to `$tmp'.
$debug ||
{
tmp= ac_tmp=
trap 'exit_status=$?
: "${ac_tmp:=$tmp}"
{ test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
' 0
trap 'as_fn_exit 1' 1 2 13 15
}
# Create a (secure) tmp directory for tmp files.
{
tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
test -d "$tmp"
} ||
{
tmp=./conf$$-$RANDOM
(umask 077 && mkdir "$tmp")
} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
ac_tmp=$tmp
# Set up the scripts for CONFIG_FILES section.
# No need to generate them if there are no CONFIG_FILES.
# This happens for instance with `./config.status config.h'.
if test -n "$CONFIG_FILES"; then
ac_cr=`echo X | tr X '\015'`
# On cygwin, bash can eat \r inside `` if the user requested igncr.
# But we know of no other shell where ac_cr would be empty at this
# point, so we can use a bashism as a fallback.
if test "x$ac_cr" = x; then
eval ac_cr=\$\'\\r\'
fi
ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
ac_cs_awk_cr='\\r'
else
ac_cs_awk_cr=$ac_cr
fi
echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
_ACEOF
{
echo "cat >conf$$subs.awk <<_ACEOF" &&
echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
echo "_ACEOF"
} >conf$$subs.sh ||
as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
ac_delim='%!_!# '
for ac_last_try in false false false false false :; do
. ./conf$$subs.sh ||
as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
if test $ac_delim_n = $ac_delim_num; then
break
elif $ac_last_try; then
as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
else
ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
fi
done
rm -f conf$$subs.sh
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
_ACEOF
sed -n '
h
s/^/S["/; s/!.*/"]=/
p
g
s/^[^!]*!//
:repl
t repl
s/'"$ac_delim"'$//
t delim
:nl
h
s/\(.\{148\}\)..*/\1/
t more1
s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
p
n
b repl
:more1
s/["\\]/\\&/g; s/^/"/; s/$/"\\/
p
g
s/.\{148\}//
t nl
:delim
h
s/\(.\{148\}\)..*/\1/
t more2
s/["\\]/\\&/g; s/^/"/; s/$/"/
p
b
:more2
s/["\\]/\\&/g; s/^/"/; s/$/"\\/
p
g
s/.\{148\}//
t delim
' <conf$$subs.awk | sed '
/^[^""]/{
N
s/\n//
}
' >>$CONFIG_STATUS || ac_write_fail=1
rm -f conf$$subs.awk
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
_ACAWK
cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
for (key in S) S_is_set[key] = 1
FS = ""
}
{
line = $ 0
nfields = split(line, field, "@")
substed = 0
len = length(field[1])
for (i = 2; i < nfields; i++) {
key = field[i]
keylen = length(key)
if (S_is_set[key]) {
value = S[key]
line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
len += length(value) + length(field[++i])
substed = 1
} else
len += 1 + keylen
}
print line
}
_ACAWK
_ACEOF
cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
else
cat
fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
|| as_fn_error $? "could not setup config files machinery" "$LINENO" 5
_ACEOF
# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
# trailing colons and then remove the whole line if VPATH becomes empty
# (actually we leave an empty line to preserve line numbers).
if test "x$srcdir" = x.; then
ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{
h
s///
s/^/:/
s/[ ]*$/:/
s/:\$(srcdir):/:/g
s/:\${srcdir}:/:/g
s/:@srcdir@:/:/g
s/^:*//
s/:*$//
x
s/\(=[ ]*\).*/\1/
G
s/\n//
s/^[^=]*=[ ]*$//
}'
fi
cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
fi # test -n "$CONFIG_FILES"
# Set up the scripts for CONFIG_HEADERS section.
# No need to generate them if there are no CONFIG_HEADERS.
# This happens for instance with `./config.status Makefile'.
if test -n "$CONFIG_HEADERS"; then
cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
BEGIN {
_ACEOF
# Transform confdefs.h into an awk script `defines.awk', embedded as
# here-document in config.status, that substitutes the proper values into
# config.h.in to produce config.h.
# Create a delimiter string that does not exist in confdefs.h, to ease
# handling of long lines.
ac_delim='%!_!# '
for ac_last_try in false false :; do
ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
if test -z "$ac_tt"; then
break
elif $ac_last_try; then
as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
else
ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
fi
done
# For the awk script, D is an array of macro values keyed by name,
# likewise P contains macro parameters if any. Preserve backslash
# newline sequences.
ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
sed -n '
s/.\{148\}/&'"$ac_delim"'/g
t rset
:rset
s/^[ ]*#[ ]*define[ ][ ]*/ /
t def
d
:def
s/\\$//
t bsnl
s/["\\]/\\&/g
s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
D["\1"]=" \3"/p
s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p
d
:bsnl
s/["\\]/\\&/g
s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
D["\1"]=" \3\\\\\\n"\\/p
t cont
s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
t cont
d
:cont
n
s/.\{148\}/&'"$ac_delim"'/g
t clear
:clear
s/\\$//
t bsnlc
s/["\\]/\\&/g; s/^/"/; s/$/"/p
d
:bsnlc
s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
b cont
' <confdefs.h | sed '
s/'"$ac_delim"'/"\\\
"/g' >>$CONFIG_STATUS || ac_write_fail=1
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
for (key in D) D_is_set[key] = 1
FS = ""
}
/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
line = \$ 0
split(line, arg, " ")
if (arg[1] == "#") {
defundef = arg[2]
mac1 = arg[3]
} else {
defundef = substr(arg[1], 2)
mac1 = arg[2]
}
split(mac1, mac2, "(") #)
macro = mac2[1]
prefix = substr(line, 1, index(line, defundef) - 1)
if (D_is_set[macro]) {
# Preserve the white space surrounding the "#".
print prefix "define", macro P[macro] D[macro]
next
} else {
# Replace #undef with comments. This is necessary, for example,
# in the case of _POSIX_SOURCE, which is predefined and required
# on some systems where configure will not decide to define it.
if (defundef == "undef") {
print "/*", prefix defundef, macro, "*/"
next
}
}
}
{ print }
_ACAWK
_ACEOF
cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
fi # test -n "$CONFIG_HEADERS"
eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS"
shift
for ac_tag
do
case $ac_tag in
:[FHLC]) ac_mode=$ac_tag; continue;;
esac
case $ac_mode$ac_tag in
:[FHL]*:*);;
:L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
:[FH]-) ac_tag=-:-;;
:[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
esac
ac_save_IFS=$IFS
IFS=:
set x $ac_tag
IFS=$ac_save_IFS
shift
ac_file=$1
shift
case $ac_mode in
:L) ac_source=$1;;
:[FH])
ac_file_inputs=
for ac_f
do
case $ac_f in
-) ac_f="$ac_tmp/stdin";;
*) # Look for the file first in the build tree, then in the source tree
# (if the path is not absolute). The absolute path cannot be DOS-style,
# because $ac_f cannot contain `:'.
test -f "$ac_f" ||
case $ac_f in
[\\/$]*) false;;
*) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
esac ||
as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
esac
case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
as_fn_append ac_file_inputs " '$ac_f'"
done
# Let's still pretend it is `configure' which instantiates (i.e., don't
# use $as_me), people would be surprised to read:
# /* config.h. Generated by config.status. */
configure_input='Generated from '`
$as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
`' by configure.'
if test x"$ac_file" != x-; then
configure_input="$ac_file. $configure_input"
{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
$as_echo "$as_me: creating $ac_file" >&6;}
fi
# Neutralize special characters interpreted by sed in replacement strings.
case $configure_input in #(
*\&* | *\|* | *\\* )
ac_sed_conf_input=`$as_echo "$configure_input" |
sed 's/[\\\\&|]/\\\\&/g'`;; #(
*) ac_sed_conf_input=$configure_input;;
esac
case $ac_tag in
*:-:* | *:-) cat >"$ac_tmp/stdin" \
|| as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
esac
;;
esac
ac_dir=`$as_dirname -- "$ac_file" ||
$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
X"$ac_file" : 'X\(//\)[^/]' \| \
X"$ac_file" : 'X\(//\)$' \| \
X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
$as_echo X"$ac_file" |
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
s//\1/
q
}
/^X\(\/\/\)[^/].*/{
s//\1/
q
}
/^X\(\/\/\)$/{
s//\1/
q
}
/^X\(\/\).*/{
s//\1/
q
}
s/.*/./; q'`
as_dir="$ac_dir"; as_fn_mkdir_p
ac_builddir=.
case "$ac_dir" in
.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
*)
ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
# A ".." for each directory in $ac_dir_suffix.
ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
case $ac_top_builddir_sub in
"") ac_top_builddir_sub=. ac_top_build_prefix= ;;
*) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
esac ;;
esac
ac_abs_top_builddir=$ac_pwd
ac_abs_builddir=$ac_pwd$ac_dir_suffix
# for backward compatibility:
ac_top_builddir=$ac_top_build_prefix
case $srcdir in
.) # We are building in place.
ac_srcdir=.
ac_top_srcdir=$ac_top_builddir_sub
ac_abs_top_srcdir=$ac_pwd ;;
[\\/]* | ?:[\\/]* ) # Absolute name.
ac_srcdir=$srcdir$ac_dir_suffix;
ac_top_srcdir=$srcdir
ac_abs_top_srcdir=$srcdir ;;
*) # Relative name.
ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
ac_top_srcdir=$ac_top_build_prefix$srcdir
ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
esac
ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
case $ac_mode in
:F)
#
# CONFIG_FILE
#
case $INSTALL in
[\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
*) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
esac
_ACEOF
cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# If the template does not know about datarootdir, expand it.
# FIXME: This hack should be removed a few years after 2.60.
ac_datarootdir_hack=; ac_datarootdir_seen=
ac_sed_dataroot='
/datarootdir/ {
p
q
}
/@datadir@/p
/@docdir@/p
/@infodir@/p
/@localedir@/p
/@mandir@/p'
case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
*datarootdir*) ac_datarootdir_seen=yes;;
*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_datarootdir_hack='
s&@datadir@&$datadir&g
s&@docdir@&$docdir&g
s&@infodir@&$infodir&g
s&@localedir@&$localedir&g
s&@mandir@&$mandir&g
s&\\\${datarootdir}&$datarootdir&g' ;;
esac
_ACEOF
# Neutralize VPATH when `$srcdir' = `.'.
# Shell code in configure.ac might set extrasub.
# FIXME: do we really want to maintain this feature?
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_sed_extra="$ac_vpsub
$extrasub
_ACEOF
cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
:t
/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
s|@configure_input@|$ac_sed_conf_input|;t t
s&@top_builddir@&$ac_top_builddir_sub&;t t
s&@top_build_prefix@&$ac_top_build_prefix&;t t
s&@srcdir@&$ac_srcdir&;t t
s&@abs_srcdir@&$ac_abs_srcdir&;t t
s&@top_srcdir@&$ac_top_srcdir&;t t
s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
s&@builddir@&$ac_builddir&;t t
s&@abs_builddir@&$ac_abs_builddir&;t t
s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
s&@INSTALL@&$ac_INSTALL&;t t
$ac_datarootdir_hack
"
eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
>$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
{ ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
{ ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \
"$ac_tmp/out"`; test -z "$ac_out"; } &&
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
which seems to be undefined. Please make sure it is defined" >&5
$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
which seems to be undefined. Please make sure it is defined" >&2;}
rm -f "$ac_tmp/stdin"
case $ac_file in
-) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
*) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
esac \
|| as_fn_error $? "could not create $ac_file" "$LINENO" 5
;;
:H)
#
# CONFIG_HEADER
#
if test x"$ac_file" != x-; then
{
$as_echo "/* $configure_input */" \
&& eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
} >"$ac_tmp/config.h" \
|| as_fn_error $? "could not create $ac_file" "$LINENO" 5
if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
$as_echo "$as_me: $ac_file is unchanged" >&6;}
else
rm -f "$ac_file"
mv "$ac_tmp/config.h" "$ac_file" \
|| as_fn_error $? "could not create $ac_file" "$LINENO" 5
fi
else
$as_echo "/* $configure_input */" \
&& eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
|| as_fn_error $? "could not create -" "$LINENO" 5
fi
;;
:C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5
$as_echo "$as_me: executing $ac_file commands" >&6;}
;;
esac
case $ac_file$ac_mode in
"libtool":C)
- # See if we are running on zsh, and set the options which allow our
+ # See if we are running on zsh, and set the options that allow our
# commands through without removal of \ escapes.
- if test -n "${ZSH_VERSION+set}" ; then
+ if test -n "${ZSH_VERSION+set}"; then
setopt NO_GLOB_SUBST
fi
- cfgfile="${ofile}T"
+ cfgfile=${ofile}T
trap "$RM \"$cfgfile\"; exit 1" 1 2 15
$RM "$cfgfile"
cat <<_LT_EOF >> "$cfgfile"
#! $SHELL
-
-# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
-# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION
+# Generated automatically by $as_me ($PACKAGE) $VERSION
# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+
+# Provide generalized library-building support services.
+# Written by Gordon Matzigkeit, 1996
+
+# Copyright (C) 2014 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions. There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# GNU Libtool 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 of the License, or
+# (at your option) any later version.
#
-# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
-# 2006, 2007, 2008, 2009, 2010, 2011 Free Software
-# Foundation, Inc.
-# Written by Gordon Matzigkeit, 1996
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program or library that is built
+# using GNU Libtool, you may include this file under the same
+# distribution terms that you use for the rest of that program.
#
-# This file is part of GNU Libtool.
-#
-# GNU Libtool 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.
-#
-# As a special exception to the GNU General Public License,
-# if you distribute this file as part of a program or library that
-# is built using GNU Libtool, you may include this file under the
-# same distribution terms that you use for the rest of that program.
-#
-# GNU Libtool is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy
-# can be downloaded from http://www.gnu.org/licenses/gpl.html, or
-# obtained by writing to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
# The names of the tagged configurations supported by this script.
-available_tags="CXX "
+available_tags='CXX '
# ### BEGIN LIBTOOL CONFIG
# Which release of libtool.m4 was used?
macro_version=$macro_version
macro_revision=$macro_revision
# Whether or not to build shared libraries.
build_libtool_libs=$enable_shared
# Whether or not to build static libraries.
build_old_libs=$enable_static
# What type of objects to build.
pic_mode=$pic_mode
# Whether or not to optimize for fast installation.
fast_install=$enable_fast_install
# Shell to use when invoking shell scripts.
SHELL=$lt_SHELL
# An echo program that protects backslashes.
ECHO=$lt_ECHO
# The PATH separator for the build system.
PATH_SEPARATOR=$lt_PATH_SEPARATOR
# The host system.
host_alias=$host_alias
host=$host
host_os=$host_os
# The build system.
build_alias=$build_alias
build=$build
build_os=$build_os
# A sed program that does not truncate output.
SED=$lt_SED
# Sed that helps us avoid accidentally triggering echo(1) options like -n.
Xsed="\$SED -e 1s/^X//"
# A grep program that handles long lines.
GREP=$lt_GREP
# An ERE matcher.
EGREP=$lt_EGREP
# A literal string matcher.
FGREP=$lt_FGREP
# A BSD- or MS-compatible name lister.
NM=$lt_NM
# Whether we need soft or hard links.
LN_S=$lt_LN_S
# What is the maximum length of a command?
max_cmd_len=$max_cmd_len
# Object file suffix (normally "o").
objext=$ac_objext
# Executable file suffix (normally "").
exeext=$exeext
# whether the shell understands "unset".
lt_unset=$lt_unset
# turn spaces into newlines.
SP2NL=$lt_lt_SP2NL
# turn newlines into spaces.
NL2SP=$lt_lt_NL2SP
# convert \$build file names to \$host format.
to_host_file_cmd=$lt_cv_to_host_file_cmd
# convert \$build files to toolchain format.
to_tool_file_cmd=$lt_cv_to_tool_file_cmd
# An object symbol dumper.
OBJDUMP=$lt_OBJDUMP
# Method to check whether dependent libraries are shared objects.
deplibs_check_method=$lt_deplibs_check_method
# Command to use when deplibs_check_method = "file_magic".
file_magic_cmd=$lt_file_magic_cmd
# How to find potential files when deplibs_check_method = "file_magic".
file_magic_glob=$lt_file_magic_glob
# Find potential files using nocaseglob when deplibs_check_method = "file_magic".
want_nocaseglob=$lt_want_nocaseglob
# DLL creation program.
DLLTOOL=$lt_DLLTOOL
# Command to associate shared and link libraries.
sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd
# The archiver.
AR=$lt_AR
# Flags to create an archive.
AR_FLAGS=$lt_AR_FLAGS
# How to feed a file listing to the archiver.
archiver_list_spec=$lt_archiver_list_spec
# A symbol stripping program.
STRIP=$lt_STRIP
# Commands used to install an old-style archive.
RANLIB=$lt_RANLIB
old_postinstall_cmds=$lt_old_postinstall_cmds
old_postuninstall_cmds=$lt_old_postuninstall_cmds
# Whether to use a lock for old archive extraction.
lock_old_archive_extraction=$lock_old_archive_extraction
# A C compiler.
LTCC=$lt_CC
# LTCC compiler flags.
LTCFLAGS=$lt_CFLAGS
# Take the output of nm and produce a listing of raw symbols and C names.
global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe
# Transform the output of nm in a proper C declaration.
global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl
+# Transform the output of nm into a list of symbols to manually relocate.
+global_symbol_to_import=$lt_lt_cv_sys_global_symbol_to_import
+
# Transform the output of nm in a C name address pair.
global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address
# Transform the output of nm in a C name address pair when lib prefix is needed.
global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix
+# The name lister interface.
+nm_interface=$lt_lt_cv_nm_interface
+
# Specify filename containing input files for \$NM.
nm_file_list_spec=$lt_nm_file_list_spec
-# The root where to search for dependent libraries,and in which our libraries should be installed.
+# The root where to search for dependent libraries,and where our libraries should be installed.
lt_sysroot=$lt_sysroot
+# Command to truncate a binary pipe.
+lt_truncate_bin=$lt_lt_cv_truncate_bin
+
# The name of the directory that contains temporary libtool files.
objdir=$objdir
# Used to examine libraries when file_magic_cmd begins with "file".
MAGIC_CMD=$MAGIC_CMD
# Must we lock files when doing compilation?
need_locks=$lt_need_locks
# Manifest tool.
MANIFEST_TOOL=$lt_MANIFEST_TOOL
# Tool to manipulate archived DWARF debug symbol files on Mac OS X.
DSYMUTIL=$lt_DSYMUTIL
# Tool to change global to local symbols on Mac OS X.
NMEDIT=$lt_NMEDIT
# Tool to manipulate fat objects and archives on Mac OS X.
LIPO=$lt_LIPO
# ldd/readelf like tool for Mach-O binaries on Mac OS X.
OTOOL=$lt_OTOOL
# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4.
OTOOL64=$lt_OTOOL64
# Old archive suffix (normally "a").
libext=$libext
# Shared library suffix (normally ".so").
shrext_cmds=$lt_shrext_cmds
# The commands to extract the exported symbol list from a shared archive.
extract_expsyms_cmds=$lt_extract_expsyms_cmds
# Variables whose values should be saved in libtool wrapper scripts and
# restored at link time.
variables_saved_for_relink=$lt_variables_saved_for_relink
# Do we need the "lib" prefix for modules?
need_lib_prefix=$need_lib_prefix
# Do we need a version for libraries?
need_version=$need_version
# Library versioning type.
version_type=$version_type
# Shared library runtime path variable.
runpath_var=$runpath_var
# Shared library path variable.
shlibpath_var=$shlibpath_var
# Is shlibpath searched before the hard-coded library search path?
shlibpath_overrides_runpath=$shlibpath_overrides_runpath
# Format of library name prefix.
libname_spec=$lt_libname_spec
# List of archive names. First name is the real one, the rest are links.
# The last name is the one that the linker finds with -lNAME
library_names_spec=$lt_library_names_spec
# The coded name of the library, if different from the real name.
soname_spec=$lt_soname_spec
# Permission mode override for installation of shared libraries.
install_override_mode=$lt_install_override_mode
# Command to use after installation of a shared archive.
postinstall_cmds=$lt_postinstall_cmds
# Command to use after uninstallation of a shared archive.
postuninstall_cmds=$lt_postuninstall_cmds
# Commands used to finish a libtool library installation in a directory.
finish_cmds=$lt_finish_cmds
# As "finish_cmds", except a single script fragment to be evaled but
# not shown.
finish_eval=$lt_finish_eval
# Whether we should hardcode library paths into libraries.
hardcode_into_libs=$hardcode_into_libs
# Compile-time system search path for libraries.
sys_lib_search_path_spec=$lt_sys_lib_search_path_spec
# Run-time system search path for libraries.
sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec
# Whether dlopen is supported.
dlopen_support=$enable_dlopen
# Whether dlopen of programs is supported.
dlopen_self=$enable_dlopen_self
# Whether dlopen of statically linked programs is supported.
dlopen_self_static=$enable_dlopen_self_static
# Commands to strip libraries.
old_striplib=$lt_old_striplib
striplib=$lt_striplib
# The linker used to build libraries.
LD=$lt_LD
# How to create reloadable object files.
reload_flag=$lt_reload_flag
reload_cmds=$lt_reload_cmds
# Commands used to build an old-style archive.
old_archive_cmds=$lt_old_archive_cmds
# A language specific compiler.
CC=$lt_compiler
# Is the compiler the GNU compiler?
with_gcc=$GCC
# Compiler flag to turn off builtin functions.
no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag
# Additional compiler flags for building library objects.
pic_flag=$lt_lt_prog_compiler_pic
# How to pass a linker flag through the compiler.
wl=$lt_lt_prog_compiler_wl
# Compiler flag to prevent dynamic linking.
link_static_flag=$lt_lt_prog_compiler_static
# Does compiler simultaneously support -c and -o options?
compiler_c_o=$lt_lt_cv_prog_compiler_c_o
# Whether or not to add -lc for building shared libraries.
build_libtool_need_lc=$archive_cmds_need_lc
# Whether or not to disallow shared libs when runtime libs are static.
allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes
# Compiler flag to allow reflexive dlopens.
export_dynamic_flag_spec=$lt_export_dynamic_flag_spec
# Compiler flag to generate shared objects directly from archives.
whole_archive_flag_spec=$lt_whole_archive_flag_spec
# Whether the compiler copes with passing no objects directly.
compiler_needs_object=$lt_compiler_needs_object
# Create an old-style archive from a shared archive.
old_archive_from_new_cmds=$lt_old_archive_from_new_cmds
# Create a temporary old-style archive to link instead of a shared archive.
old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds
# Commands used to build a shared archive.
archive_cmds=$lt_archive_cmds
archive_expsym_cmds=$lt_archive_expsym_cmds
# Commands used to build a loadable module if different from building
# a shared archive.
module_cmds=$lt_module_cmds
module_expsym_cmds=$lt_module_expsym_cmds
# Whether we are building with GNU ld or not.
with_gnu_ld=$lt_with_gnu_ld
# Flag that allows shared libraries with undefined symbols to be built.
allow_undefined_flag=$lt_allow_undefined_flag
# Flag that enforces no undefined symbols.
no_undefined_flag=$lt_no_undefined_flag
# Flag to hardcode \$libdir into a binary during linking.
# This must work even if \$libdir does not exist
hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec
# Whether we need a single "-rpath" flag with a separated argument.
hardcode_libdir_separator=$lt_hardcode_libdir_separator
-# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes
# DIR into the resulting binary.
hardcode_direct=$hardcode_direct
-# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes
# DIR into the resulting binary and the resulting library dependency is
-# "absolute",i.e impossible to change by setting \${shlibpath_var} if the
+# "absolute",i.e impossible to change by setting \$shlibpath_var if the
# library is relocated.
hardcode_direct_absolute=$hardcode_direct_absolute
# Set to "yes" if using the -LDIR flag during linking hardcodes DIR
# into the resulting binary.
hardcode_minus_L=$hardcode_minus_L
# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
# into the resulting binary.
hardcode_shlibpath_var=$hardcode_shlibpath_var
# Set to "yes" if building a shared library automatically hardcodes DIR
# into the library and all subsequent libraries and executables linked
# against it.
hardcode_automatic=$hardcode_automatic
# Set to yes if linker adds runtime paths of dependent libraries
# to runtime path list.
inherit_rpath=$inherit_rpath
# Whether libtool must link a program against all its dependency libraries.
link_all_deplibs=$link_all_deplibs
# Set to "yes" if exported symbols are required.
always_export_symbols=$always_export_symbols
# The commands to list exported symbols.
export_symbols_cmds=$lt_export_symbols_cmds
# Symbols that should not be listed in the preloaded symbols.
exclude_expsyms=$lt_exclude_expsyms
# Symbols that must always be exported.
include_expsyms=$lt_include_expsyms
# Commands necessary for linking programs (against libraries) with templates.
prelink_cmds=$lt_prelink_cmds
# Commands necessary for finishing linking programs.
postlink_cmds=$lt_postlink_cmds
# Specify filename containing input files.
file_list_spec=$lt_file_list_spec
# How to hardcode a shared library path into an executable.
hardcode_action=$hardcode_action
# The directories searched by this compiler when creating a shared library.
compiler_lib_search_dirs=$lt_compiler_lib_search_dirs
# Dependencies to place before and after the objects being linked to
# create a shared library.
predep_objects=$lt_predep_objects
postdep_objects=$lt_postdep_objects
predeps=$lt_predeps
postdeps=$lt_postdeps
# The library search path used internally by the compiler when linking
# a shared library.
compiler_lib_search_path=$lt_compiler_lib_search_path
# ### END LIBTOOL CONFIG
_LT_EOF
case $host_os in
aix3*)
cat <<\_LT_EOF >> "$cfgfile"
# AIX sometimes has problems with the GCC collect2 program. For some
# reason, if we set the COLLECT_NAMES environment variable, the problems
# vanish in a puff of smoke.
-if test "X${COLLECT_NAMES+set}" != Xset; then
+if test set != "${COLLECT_NAMES+set}"; then
COLLECT_NAMES=
export COLLECT_NAMES
fi
_LT_EOF
;;
esac
-ltmain="$ac_aux_dir/ltmain.sh"
+ltmain=$ac_aux_dir/ltmain.sh
# We use sed instead of cat because bash on DJGPP gets confused if
# if finds mixed CR/LF and LF-only lines. Since sed operates in
# text mode, it properly converts lines to CR/LF. This bash problem
# is reportedly fixed, but why not run on old versions too?
sed '$q' "$ltmain" >> "$cfgfile" \
|| (rm -f "$cfgfile"; exit 1)
- if test x"$xsi_shell" = xyes; then
- sed -e '/^func_dirname ()$/,/^} # func_dirname /c\
-func_dirname ()\
-{\
-\ case ${1} in\
-\ */*) func_dirname_result="${1%/*}${2}" ;;\
-\ * ) func_dirname_result="${3}" ;;\
-\ esac\
-} # Extended-shell func_dirname implementation' "$cfgfile" > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
-test 0 -eq $? || _lt_function_replace_fail=:
-
-
- sed -e '/^func_basename ()$/,/^} # func_basename /c\
-func_basename ()\
-{\
-\ func_basename_result="${1##*/}"\
-} # Extended-shell func_basename implementation' "$cfgfile" > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
-test 0 -eq $? || _lt_function_replace_fail=:
-
-
- sed -e '/^func_dirname_and_basename ()$/,/^} # func_dirname_and_basename /c\
-func_dirname_and_basename ()\
-{\
-\ case ${1} in\
-\ */*) func_dirname_result="${1%/*}${2}" ;;\
-\ * ) func_dirname_result="${3}" ;;\
-\ esac\
-\ func_basename_result="${1##*/}"\
-} # Extended-shell func_dirname_and_basename implementation' "$cfgfile" > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
-test 0 -eq $? || _lt_function_replace_fail=:
-
-
- sed -e '/^func_stripname ()$/,/^} # func_stripname /c\
-func_stripname ()\
-{\
-\ # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are\
-\ # positional parameters, so assign one to ordinary parameter first.\
-\ func_stripname_result=${3}\
-\ func_stripname_result=${func_stripname_result#"${1}"}\
-\ func_stripname_result=${func_stripname_result%"${2}"}\
-} # Extended-shell func_stripname implementation' "$cfgfile" > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
-test 0 -eq $? || _lt_function_replace_fail=:
-
-
- sed -e '/^func_split_long_opt ()$/,/^} # func_split_long_opt /c\
-func_split_long_opt ()\
-{\
-\ func_split_long_opt_name=${1%%=*}\
-\ func_split_long_opt_arg=${1#*=}\
-} # Extended-shell func_split_long_opt implementation' "$cfgfile" > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
-test 0 -eq $? || _lt_function_replace_fail=:
-
-
- sed -e '/^func_split_short_opt ()$/,/^} # func_split_short_opt /c\
-func_split_short_opt ()\
-{\
-\ func_split_short_opt_arg=${1#??}\
-\ func_split_short_opt_name=${1%"$func_split_short_opt_arg"}\
-} # Extended-shell func_split_short_opt implementation' "$cfgfile" > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
-test 0 -eq $? || _lt_function_replace_fail=:
-
-
- sed -e '/^func_lo2o ()$/,/^} # func_lo2o /c\
-func_lo2o ()\
-{\
-\ case ${1} in\
-\ *.lo) func_lo2o_result=${1%.lo}.${objext} ;;\
-\ *) func_lo2o_result=${1} ;;\
-\ esac\
-} # Extended-shell func_lo2o implementation' "$cfgfile" > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
-test 0 -eq $? || _lt_function_replace_fail=:
-
-
- sed -e '/^func_xform ()$/,/^} # func_xform /c\
-func_xform ()\
-{\
- func_xform_result=${1%.*}.lo\
-} # Extended-shell func_xform implementation' "$cfgfile" > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
-test 0 -eq $? || _lt_function_replace_fail=:
-
-
- sed -e '/^func_arith ()$/,/^} # func_arith /c\
-func_arith ()\
-{\
- func_arith_result=$(( $* ))\
-} # Extended-shell func_arith implementation' "$cfgfile" > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
-test 0 -eq $? || _lt_function_replace_fail=:
-
-
- sed -e '/^func_len ()$/,/^} # func_len /c\
-func_len ()\
-{\
- func_len_result=${#1}\
-} # Extended-shell func_len implementation' "$cfgfile" > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
-test 0 -eq $? || _lt_function_replace_fail=:
-
-fi
-
-if test x"$lt_shell_append" = xyes; then
- sed -e '/^func_append ()$/,/^} # func_append /c\
-func_append ()\
-{\
- eval "${1}+=\\${2}"\
-} # Extended-shell func_append implementation' "$cfgfile" > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
-test 0 -eq $? || _lt_function_replace_fail=:
-
-
- sed -e '/^func_append_quoted ()$/,/^} # func_append_quoted /c\
-func_append_quoted ()\
-{\
-\ func_quote_for_eval "${2}"\
-\ eval "${1}+=\\\\ \\$func_quote_for_eval_result"\
-} # Extended-shell func_append_quoted implementation' "$cfgfile" > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
-test 0 -eq $? || _lt_function_replace_fail=:
-
-
- # Save a `func_append' function call where possible by direct use of '+='
- sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1+="%g' $cfgfile > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
- test 0 -eq $? || _lt_function_replace_fail=:
-else
- # Save a `func_append' function call even when '+=' is not available
- sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1="$\1%g' $cfgfile > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
- test 0 -eq $? || _lt_function_replace_fail=:
-fi
-
-if test x"$_lt_function_replace_fail" = x":"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Unable to substitute extended shell functions in $ofile" >&5
-$as_echo "$as_me: WARNING: Unable to substitute extended shell functions in $ofile" >&2;}
-fi
-
-
mv -f "$cfgfile" "$ofile" ||
(rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
chmod +x "$ofile"
cat <<_LT_EOF >> "$ofile"
# ### BEGIN LIBTOOL TAG CONFIG: CXX
# The linker used to build libraries.
LD=$lt_LD_CXX
# How to create reloadable object files.
reload_flag=$lt_reload_flag_CXX
reload_cmds=$lt_reload_cmds_CXX
# Commands used to build an old-style archive.
old_archive_cmds=$lt_old_archive_cmds_CXX
# A language specific compiler.
CC=$lt_compiler_CXX
# Is the compiler the GNU compiler?
with_gcc=$GCC_CXX
# Compiler flag to turn off builtin functions.
no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX
# Additional compiler flags for building library objects.
pic_flag=$lt_lt_prog_compiler_pic_CXX
# How to pass a linker flag through the compiler.
wl=$lt_lt_prog_compiler_wl_CXX
# Compiler flag to prevent dynamic linking.
link_static_flag=$lt_lt_prog_compiler_static_CXX
# Does compiler simultaneously support -c and -o options?
compiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX
# Whether or not to add -lc for building shared libraries.
build_libtool_need_lc=$archive_cmds_need_lc_CXX
# Whether or not to disallow shared libs when runtime libs are static.
allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX
# Compiler flag to allow reflexive dlopens.
export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX
# Compiler flag to generate shared objects directly from archives.
whole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX
# Whether the compiler copes with passing no objects directly.
compiler_needs_object=$lt_compiler_needs_object_CXX
# Create an old-style archive from a shared archive.
old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX
# Create a temporary old-style archive to link instead of a shared archive.
old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX
# Commands used to build a shared archive.
archive_cmds=$lt_archive_cmds_CXX
archive_expsym_cmds=$lt_archive_expsym_cmds_CXX
# Commands used to build a loadable module if different from building
# a shared archive.
module_cmds=$lt_module_cmds_CXX
module_expsym_cmds=$lt_module_expsym_cmds_CXX
# Whether we are building with GNU ld or not.
with_gnu_ld=$lt_with_gnu_ld_CXX
# Flag that allows shared libraries with undefined symbols to be built.
allow_undefined_flag=$lt_allow_undefined_flag_CXX
# Flag that enforces no undefined symbols.
no_undefined_flag=$lt_no_undefined_flag_CXX
# Flag to hardcode \$libdir into a binary during linking.
# This must work even if \$libdir does not exist
hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX
# Whether we need a single "-rpath" flag with a separated argument.
hardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX
-# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes
# DIR into the resulting binary.
hardcode_direct=$hardcode_direct_CXX
-# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes
# DIR into the resulting binary and the resulting library dependency is
-# "absolute",i.e impossible to change by setting \${shlibpath_var} if the
+# "absolute",i.e impossible to change by setting \$shlibpath_var if the
# library is relocated.
hardcode_direct_absolute=$hardcode_direct_absolute_CXX
# Set to "yes" if using the -LDIR flag during linking hardcodes DIR
# into the resulting binary.
hardcode_minus_L=$hardcode_minus_L_CXX
# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
# into the resulting binary.
hardcode_shlibpath_var=$hardcode_shlibpath_var_CXX
# Set to "yes" if building a shared library automatically hardcodes DIR
# into the library and all subsequent libraries and executables linked
# against it.
hardcode_automatic=$hardcode_automatic_CXX
# Set to yes if linker adds runtime paths of dependent libraries
# to runtime path list.
inherit_rpath=$inherit_rpath_CXX
# Whether libtool must link a program against all its dependency libraries.
link_all_deplibs=$link_all_deplibs_CXX
# Set to "yes" if exported symbols are required.
always_export_symbols=$always_export_symbols_CXX
# The commands to list exported symbols.
export_symbols_cmds=$lt_export_symbols_cmds_CXX
# Symbols that should not be listed in the preloaded symbols.
exclude_expsyms=$lt_exclude_expsyms_CXX
# Symbols that must always be exported.
include_expsyms=$lt_include_expsyms_CXX
# Commands necessary for linking programs (against libraries) with templates.
prelink_cmds=$lt_prelink_cmds_CXX
# Commands necessary for finishing linking programs.
postlink_cmds=$lt_postlink_cmds_CXX
# Specify filename containing input files.
file_list_spec=$lt_file_list_spec_CXX
# How to hardcode a shared library path into an executable.
hardcode_action=$hardcode_action_CXX
# The directories searched by this compiler when creating a shared library.
compiler_lib_search_dirs=$lt_compiler_lib_search_dirs_CXX
# Dependencies to place before and after the objects being linked to
# create a shared library.
predep_objects=$lt_predep_objects_CXX
postdep_objects=$lt_postdep_objects_CXX
predeps=$lt_predeps_CXX
postdeps=$lt_postdeps_CXX
# The library search path used internally by the compiler when linking
# a shared library.
compiler_lib_search_path=$lt_compiler_lib_search_path_CXX
# ### END LIBTOOL TAG CONFIG: CXX
_LT_EOF
;;
"svn_private_config.h.tmp":C) svn_cf=subversion/svn_private_config.h;
$SED -e "s/@SVN_DB_HEADER@/$SVN_DB_HEADER/" $svn_cf.tmp > $svn_cf.tmp.new
cmp -s $svn_cf.tmp.new $svn_cf || mv -f $svn_cf.tmp.new $svn_cf
rm -f $svn_cf.tmp.new $svn_cf.tmp ;;
"tools/backup/hot-backup.py":F) chmod +x tools/backup/hot-backup.py ;;
"tools/hook-scripts/commit-access-control.pl":F) chmod +x tools/hook-scripts/commit-access-control.pl ;;
"subversion/bindings/swig/perl/native/Makefile.PL":F) chmod +x subversion/bindings/swig/perl/native/Makefile.PL ;;
"packages/solaris/pkginfo":F) chmod +x packages/solaris/pkginfo ;;
esac
done # for ac_tag
as_fn_exit 0
_ACEOF
ac_clean_files=$ac_clean_files_save
test $ac_write_fail = 0 ||
as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
# configure is writing to config.log, and then calls config.status.
# config.status does its own redirection, appending to config.log.
# Unfortunately, on DOS this fails, as config.log is still kept open
# by configure, so config.status won't be able to write to it; its
# output is simply discarded. So we exec the FD to /dev/null,
# effectively closing config.log, so it can be properly (re)opened and
# appended to by config.status. When coming back to configure, we
# need to make the FD available again.
if test "$no_create" != yes; then
ac_cs_success=:
ac_config_status_args=
test "$silent" = yes &&
ac_config_status_args="$ac_config_status_args --quiet"
exec 5>/dev/null
$SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
exec 5>>config.log
# Use ||, not &&, to avoid exiting from the if with $? = 1, which
# would make configure fail if this is the last instruction.
$ac_cs_success || as_fn_exit 1
fi
if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
fi
# ==== Print final messages to user ==========================================
if test "$svn_have_berkeley_db" = "no6" && test "$enable_bdb6" != "no"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: We have configured without BDB filesystem support
Berkeley DB 6 was found, but not used. Please re-run configure (see
./config.nice) with the '--enable-bdb6' flag to use it,
or explicitly specify '--disable-bdb6' or '--without-berkeley-db'
to silence this warning.
Please note that some versions of Berkeley DB 6+ are under the GNU Affero
General Public License, version 3:
https://oss.oracle.com/pipermail/bdb/2013-June/000056.html
The AGPL-3.0 licence may impose special requirements for making available
source code of server-side software. The text of the licence is:
https://www.gnu.org/licenses/agpl-3.0.html
http://opensource.org/licenses/AGPL-3.0
The Berkeley DB backend to Subversion is deprecated; see
http://subversion.apache.org/docs/release-notes/1.8#bdb-deprecated
The Subversion developers have not tested Subversion with Berkeley DB 6 for
technical problems or bugs.
" >&5
$as_echo "$as_me: WARNING: We have configured without BDB filesystem support
Berkeley DB 6 was found, but not used. Please re-run configure (see
./config.nice) with the '--enable-bdb6' flag to use it,
or explicitly specify '--disable-bdb6' or '--without-berkeley-db'
to silence this warning.
Please note that some versions of Berkeley DB 6+ are under the GNU Affero
General Public License, version 3:
https://oss.oracle.com/pipermail/bdb/2013-June/000056.html
The AGPL-3.0 licence may impose special requirements for making available
source code of server-side software. The text of the licence is:
https://www.gnu.org/licenses/agpl-3.0.html
http://opensource.org/licenses/AGPL-3.0
The Berkeley DB backend to Subversion is deprecated; see
http://subversion.apache.org/docs/release-notes/1.8#bdb-deprecated
The Subversion developers have not tested Subversion with Berkeley DB 6 for
technical problems or bugs.
" >&2;}
fi
Index: vendor/subversion/dist/configure.ac
===================================================================
--- vendor/subversion/dist/configure.ac (revision 286500)
+++ vendor/subversion/dist/configure.ac (revision 286501)
@@ -1,1538 +1,1538 @@
dnl Licensed to the Apache Software Foundation (ASF) under one
dnl or more contributor license agreements. See the NOTICE file
dnl distributed with this work for additional information
dnl regarding copyright ownership. The ASF licenses this file
dnl to you under the Apache License, Version 2.0 (the
dnl "License"); you may not use this file except in compliance
dnl with the License. You may obtain a copy of the License at
dnl
dnl http://www.apache.org/licenses/LICENSE-2.0
dnl
dnl Unless required by applicable law or agreed to in writing,
dnl software distributed under the License is distributed on an
dnl "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
dnl KIND, either express or implied. See the License for the
dnl specific language governing permissions and limitations
dnl under the License.
dnl configure.ac: Autoconfiscation for Subversion
dnl Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59)
dnl Get the version of Subversion, using m4's esyscmd() command to do this
dnl at m4-time, since AC_INIT() requires it then.
AC_INIT([subversion],
[esyscmd(python build/getversion.py SVN subversion/include/svn_version.h)],
[http://subversion.apache.org/])
AC_CONFIG_SRCDIR(subversion/include/svn_types.h)
AC_CONFIG_AUX_DIR([build])
AC_MSG_NOTICE([Configuring Subversion ]AC_PACKAGE_VERSION)
AC_SUBST([abs_srcdir], ["`cd $srcdir && pwd`"])
AC_SUBST([abs_builddir], ["`pwd`"])
if test "$abs_srcdir" = "$abs_builddir"; then
canonicalized_srcdir=""
else
canonicalized_srcdir="$srcdir/"
fi
AC_SUBST([canonicalized_srcdir])
SWIG_LDFLAGS="$LDFLAGS"
AC_SUBST([SWIG_LDFLAGS])
# Generate config.nice early (before the arguments are munged)
SVN_CONFIG_NICE(config.nice)
# ==== Check for programs ====================================================
# Look for a C compiler (before anything can set CFLAGS)
CMAINTAINERFLAGS="$CUSERFLAGS"
CUSERFLAGS="$CFLAGS"
AC_PROG_CC
SVN_CC_MODE_SETUP
# Look for a C++ compiler (before anything can set CXXFLAGS)
CXXMAINTAINERFLAGS="$CXXUSERFLAGS"
CXXUSERFLAGS="$CXXFLAGS"
AC_PROG_CXX
SVN_CXX_MODE_SETUP
# Look for a C pre-processor
AC_PROG_CPP
# Look for a good sed
# AC_PROG_SED was introduced in Autoconf 2.59b
m4_ifdef([AC_PROG_SED], [AC_PROG_SED], [SED="${SED:-sed}"])
# Grab target_cpu, so we can use it in the Solaris pkginfo file
AC_CANONICAL_TARGET
# Look for an extended grep
AC_PROG_EGREP
AC_PROG_LN_S
AC_PROG_INSTALL
# If $INSTALL is relative path to our fallback install-sh, then convert
# to an absolute path, as in some cases (e.g. Solaris VPATH build), libtool
# may try to use it from a changed working directory.
if test "$INSTALL" = "build/install-sh -c"; then
INSTALL="$abs_srcdir/$INSTALL"
fi
if test -z "$MKDIR"; then
MKDIR="$INSTALL -d"
fi
AC_SUBST([MKDIR])
# ==== Libraries, for which we may have source to build ======================
dnl verify apr version and set apr flags
dnl These regular expressions should not contain "\(" and "\)".
dnl The specific reason we require APR 0.9.7 is:
dnl It contains fixes to its file writing routines
dnl now generating errors instead of silently ignoring
dnl them. Only .7 and later can guarantee repository
dnl integrity with FSFS.
APR_VER_REGEXES=["0\.9\.[7-9] 0\.9\.[12][0-9] 1\. 2\."]
SVN_LIB_APR($APR_VER_REGEXES)
if test `expr $apr_version : 2` -ne 0; then
dnl Bump the library so-version to 2 if using APR-2
dnl (Debian uses so-version 1 for APR-1-with-largefile)
svn_lib_ver=2
dnl APR-2 provides APRUTIL
apu_config=$apr_config
AC_SUBST(SVN_APRUTIL_INCLUDES)
AC_SUBST(SVN_APRUTIL_CONFIG, ["$apu_config"])
AC_SUBST(SVN_APRUTIL_LIBS)
else
svn_lib_ver=0
APU_VER_REGEXES=["0\.9\.[7-9] 0\.9\.1[0-9] 1\."]
SVN_LIB_APRUTIL($APU_VER_REGEXES)
fi
SVN_LT_SOVERSION="-version-info $svn_lib_ver"
AC_SUBST(SVN_LT_SOVERSION)
AC_DEFINE_UNQUOTED(SVN_SOVERSION, $svn_lib_ver,
[Subversion library major verson])
dnl Search for pkg-config
AC_PATH_PROG(PKG_CONFIG, pkg-config)
dnl Search for serf
SVN_LIB_SERF(1,2,1)
if test "$svn_lib_serf" = "yes"; then
AC_DEFINE([SVN_HAVE_SERF], 1,
[Defined if support for Serf is enabled])
fi
dnl Search for apr_memcache (only affects fs_fs)
SVN_LIB_APR_MEMCACHE
if test "$svn_lib_apr_memcache" = "yes"; then
AC_DEFINE(SVN_HAVE_MEMCACHE, 1,
[Defined if apr_memcache (standalone or in apr-util) is present])
fi
dnl Find Apache with a recent-enough magic module number
SVN_FIND_APACHE(20020903)
dnl Search for SQLite. If you change SQLITE_URL from a .zip to
dnl something else also update build/ac-macros/sqlite.m4 to reflect
dnl the correct command to unpack the downloaded file.
SQLITE_MINIMUM_VER="3.7.12"
SQLITE_RECOMMENDED_VER="3.7.15.1"
SQLITE_URL="http://www.sqlite.org/sqlite-amalgamation-$(printf %d%02d%02d%02d $(echo ${SQLITE_RECOMMENDED_VER} | sed -e 's/\./ /g')).zip"
SVN_LIB_SQLITE(${SQLITE_MINIMUM_VER}, ${SQLITE_RECOMMENDED_VER},
${SQLITE_URL})
AC_ARG_ENABLE(sqlite-compatibility-version,
AS_HELP_STRING([--enable-sqlite-compatibility-version=X.Y.Z],
[Allow binary to run against SQLite as old as ARG]),
[sqlite_compat_ver=$enableval],[sqlite_compat_ver=no])
if test -n "$sqlite_compat_ver" && test "$sqlite_compat_ver" != no; then
SVN_SQLITE_VERNUM_PARSE([$sqlite_compat_ver],
[sqlite_compat_ver_num])
CFLAGS="-DSVN_SQLITE_MIN_VERSION='\"$sqlite_compat_ver\"' $CFLAGS"
CFLAGS="-DSVN_SQLITE_MIN_VERSION_NUMBER=$sqlite_compat_ver_num $CFLAGS"
fi
SVN_CHECK_FOR_ATOMIC_BUILTINS
if test "$svn_cv_atomic_builtins" = "yes"; then
AC_DEFINE(SVN_HAS_ATOMIC_BUILTINS, 1, [Define if compiler provides atomic builtins])
fi
dnl Set up a number of directories ---------------------
dnl Create SVN_BINDIR for proper substitution
if test "${bindir}" = '${exec_prefix}/bin'; then
if test "${exec_prefix}" = "NONE"; then
if test "${prefix}" = "NONE"; then
SVN_BINDIR="${ac_default_prefix}/bin"
else
SVN_BINDIR="${prefix}/bin"
fi
else
SVN_BINDIR="${exec_prefix}/bin"
fi
else
SVN_BINDIR="${bindir}"
fi
dnl fully evaluate this value. when we substitute it into our tool scripts,
dnl they will not have things such as ${bindir} available
SVN_BINDIR="`eval echo ${SVN_BINDIR}`"
AC_SUBST(SVN_BINDIR)
dnl provide ${bindir} in svn_private_config.h for use in compiled code
AC_DEFINE_UNQUOTED(SVN_BINDIR, "${SVN_BINDIR}",
[Defined to be the path to the installed binaries])
dnl This purposely does *not* allow for multiple parallel installs.
dnl However, it is compatible with most gettext usages.
localedir='${datadir}/locale'
AC_SUBST(localedir)
dnl For SVN_LOCALE_DIR, we have to expand it to something. See SVN_BINDIR.
if test "${datadir}" = '${prefix}/share' && test "${prefix}" = "NONE"; then
exp_localedir='${ac_default_prefix}/share/locale'
else
exp_localedir=$localedir
fi
SVN_EXPAND_VAR(svn_localedir, "${exp_localedir}")
AC_DEFINE_UNQUOTED(SVN_LOCALE_DIR, "${svn_localedir}",
[Defined to be the path to the installed locale dirs])
dnl Check for libtool -- we'll definitely need it for all our shared libs!
AC_MSG_NOTICE([configuring libtool now])
ifdef([LT_INIT], [LT_INIT], [AC_PROG_LIBTOOL])
AC_ARG_ENABLE(experimental-libtool,
AS_HELP_STRING([--enable-experimental-libtool],[Use APR's libtool]),
[experimental_libtool=$enableval],[experimental_libtool=no])
if test "$experimental_libtool" = "yes"; then
echo "using APR's libtool"
sh_libtool="`$apr_config --apr-libtool`"
LIBTOOL="$sh_libtool"
SVN_LIBTOOL="$sh_libtool"
else
sh_libtool="$abs_builddir/libtool"
SVN_LIBTOOL="\$(SHELL) $sh_libtool"
fi
AC_SUBST(SVN_LIBTOOL)
dnl Determine the libtool version
changequote(, )dnl
lt_pversion=`$LIBTOOL --version 2>/dev/null|$SED -e 's/([^)]*)//g;s/^[^0-9]*//;s/[- ].*//g;q'`
lt_version=`echo $lt_pversion|$SED -e 's/\([a-z]*\)$/.\1/'`
lt_major_version=`echo $lt_version | cut -d'.' -f 1`
changequote([, ])dnl
dnl set the default parameters
svn_enable_static=yes
svn_enable_shared=yes
dnl check for --enable-static option
AC_ARG_ENABLE(static,
AS_HELP_STRING([--enable-static],
[Build static libraries]),
[svn_enable_static="$enableval"], [svn_enable_static="yes"])
dnl check for --enable-shared option
AC_ARG_ENABLE(shared,
AS_HELP_STRING([--enable-shared],
[Build shared libraries]),
[svn_enable_shared="$enableval"], [svn_enable_shared="yes"])
if test "$svn_enable_static" = "yes" && test "$svn_enable_shared" = "yes" ; then
AC_MSG_NOTICE([building both shared and static libraries])
elif test "$svn_enable_static" = "yes" ; then
AC_MSG_NOTICE([building static libraries only])
LT_CFLAGS="-static $LT_CFLAGS"
LT_LDFLAGS="-static $LT_LDFLAGS"
elif test "$svn_enable_shared" = "yes" ; then
AC_MSG_NOTICE([building shared libraries only])
if test "$lt_major_version" = "1" ; then
LT_CFLAGS="-prefer-pic $LT_CFLAGS"
elif test "$lt_major_version" = "2" ; then
LT_CFLAGS="-shared $LT_CFLAGS"
fi
LT_LDFLAGS="-shared $LT_LDFLAGS"
else
AC_MSG_ERROR([cannot disable both shared and static libraries])
fi
dnl Check for --enable-all-static option
AC_ARG_ENABLE(all-static,
AS_HELP_STRING([--enable-all-static],
[Build completely static (standalone) binaries.]),
[
if test "$enableval" = "yes" ; then
LT_LDFLAGS="-all-static $LT_LDFLAGS"
elif test "$enableval" != "no" ; then
AC_MSG_ERROR([--enable-all-static doesn't accept argument])
fi
])
AC_SUBST(LT_CFLAGS)
AC_SUBST(LT_LDFLAGS)
AC_ARG_ENABLE(local-library-preloading,
AS_HELP_STRING([--enable-local-library-preloading],
[Enable preloading of locally built libraries in locally
built executables. This may be necessary for testing
prior to installation on some platforms. It does not
work on some platforms (Darwin, OpenBSD, ...).]),
[
if test "$enableval" != "no"; then
if test "$svn_enable_shared" = "yes"; then
TRANSFORM_LIBTOOL_SCRIPTS="transform-libtool-scripts"
else
AC_MSG_ERROR([--enable-local-library-preloading conflicts with --disable-shared])
fi
else
TRANSFORM_LIBTOOL_SCRIPTS=""
fi
], [
TRANSFORM_LIBTOOL_SCRIPTS=""
])
AC_SUBST(TRANSFORM_LIBTOOL_SCRIPTS)
dnl Check if -no-undefined is needed for the platform.
dnl It should always work but with libtool 1.4.3 on OS X it breaks the build.
dnl So we only turn it on for platforms where we know we really need it.
AC_MSG_CHECKING([whether libtool needs -no-undefined])
case $host in
*-*-cygwin*)
AC_MSG_RESULT([yes])
LT_NO_UNDEFINED="-no-undefined"
;;
*)
AC_MSG_RESULT([no])
LT_NO_UNDEFINED=""
;;
esac
AC_SUBST(LT_NO_UNDEFINED)
AC_MSG_CHECKING([whether to avoid circular linkage at all costs])
case $host in
*-*-cygwin*)
AC_MSG_RESULT([yes])
AC_DEFINE([SVN_AVOID_CIRCULAR_LINKAGE_AT_ALL_COSTS_HACK], 1,
[Define if circular linkage is not possible on this platform.])
;;
*)
AC_MSG_RESULT([no])
;;
esac
dnl Check for trang.
trang=yes
AC_ARG_WITH(trang,
AS_HELP_STRING([--with-trang=PATH],
[Specify the command to run the trang schema converter]),
[
trang="$withval"
])
if test "$trang" = "yes"; then
AC_PATH_PROG(TRANG, trang, none)
else
TRANG="$trang"
AC_SUBST(TRANG)
fi
dnl Check for doxygen
doxygen=yes
AC_ARG_WITH(doxygen,
AC_HELP_STRING([--with-doxygen=PATH],
[Specify the command to run doxygen]),
[
doxygen="$withval"
])
if test "$doxygen" = "yes"; then
AC_PATH_PROG(DOXYGEN, doxygen, none)
else
DOXYGEN="$doxygen"
AC_SUBST(DOXYGEN)
fi
dnl Check for libraries --------------------
dnl Expat -------------------
AC_ARG_WITH(expat,
AS_HELP_STRING([--with-expat=INCLUDES:LIB_SEARCH_DIRS:LIBS],
[Specify location of Expat]),
[svn_lib_expat="$withval"],
[svn_lib_expat="::expat"])
# APR-util accepts "builtin" as an argument to this option so if the user
# passed "builtin" pretend the user didn't specify the --with-expat option
# at all. Expat will (hopefully) be found in apr-util.
test "_$svn_lib_expat" = "_builtin" && svn_lib_expat="::expat"
AC_MSG_CHECKING([for Expat])
if test -n "`echo "$svn_lib_expat" | $EGREP ":.*:"`"; then
SVN_XML_INCLUDES=""
for i in [`echo "$svn_lib_expat" | $SED -e "s/\([^:]*\):.*/\1/"`]; do
SVN_XML_INCLUDES="$SVN_XML_INCLUDES -I$i"
done
SVN_XML_INCLUDES="${SVN_XML_INCLUDES## }"
for l in [`echo "$svn_lib_expat" | $SED -e "s/.*:\([^:]*\):.*/\1/"`]; do
LDFLAGS="$LDFLAGS -L$l"
done
for l in [`echo "$svn_lib_expat" | $SED -e "s/.*:\([^:]*\)/\1/"`]; do
SVN_XML_LIBS="$SVN_XML_LIBS -l$l"
done
SVN_XML_LIBS="${SVN_XML_LIBS## }"
old_CPPFLAGS="$CPPFLAGS"
old_LIBS="$LIBS"
CPPFLAGS="$CPPFLAGS $SVN_XML_INCLUDES"
LIBS="$LIBS $SVN_XML_LIBS"
AC_LINK_IFELSE([AC_LANG_SOURCE([[
#include <expat.h>
int main()
{XML_ParserCreate(NULL);}]])], svn_lib_expat="yes", svn_lib_expat="no")
LIBS="$old_LIBS"
if test "$svn_lib_expat" = "yes"; then
AC_MSG_RESULT([yes])
else
SVN_XML_INCLUDES=""
SVN_XML_LIBS=""
CPPFLAGS="$CPPFLAGS $SVN_APRUTIL_INCLUDES"
if test "$enable_all_static" != "yes"; then
SVN_APRUTIL_LIBS="$SVN_APRUTIL_LIBS `$apu_config --libs`"
fi
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
#include <expat.h>
int main()
{XML_ParserCreate(NULL);}]])], svn_lib_expat="yes", svn_lib_expat="no")
if test "$svn_lib_expat" = "yes"; then
AC_MSG_RESULT([yes])
AC_MSG_WARN([Expat found amongst libraries used by APR-Util, but Subversion libraries might be needlessly linked against additional unused libraries. It can be avoided by specifying exact location of Expat in argument of --with-expat option.])
else
AC_MSG_RESULT([no])
AC_MSG_ERROR([Expat not found])
fi
fi
CPPFLAGS="$old_CPPFLAGS"
else
AC_MSG_RESULT([no])
if test "$svn_lib_expat" = "yes"; then
AC_MSG_ERROR([--with-expat option requires argument])
elif test "$svn_lib_expat" = "no"; then
AC_MSG_ERROR([Expat is required])
else
AC_MSG_ERROR([Invalid syntax of argument of --with-expat option])
fi
fi
AC_SUBST(SVN_XML_INCLUDES)
AC_SUBST(SVN_XML_LIBS)
dnl Berkeley DB -------------------
# Berkeley DB on SCO OpenServer needs -lsocket
AC_CHECK_LIB(socket, socket)
# Build the BDB filesystem library only if we have an appropriate
# version of Berkeley DB.
case "$host" in
powerpc-apple-darwin*)
# Berkeley DB 4.0 does not work on OS X.
SVN_FS_WANT_DB_MAJOR=4
SVN_FS_WANT_DB_MINOR=1
SVN_FS_WANT_DB_PATCH=25
;;
*)
SVN_FS_WANT_DB_MAJOR=4
SVN_FS_WANT_DB_MINOR=0
SVN_FS_WANT_DB_PATCH=14
;;
esac
db_alt_version="5.x"
# Look for libdb4.so first:
SVN_LIB_BERKELEY_DB($SVN_FS_WANT_DB_MAJOR, $SVN_FS_WANT_DB_MINOR,
$SVN_FS_WANT_DB_PATCH, [db4 db])
AC_DEFINE_UNQUOTED(SVN_FS_WANT_DB_MAJOR, $SVN_FS_WANT_DB_MAJOR,
[The desired major version for the Berkeley DB])
AC_DEFINE_UNQUOTED(SVN_FS_WANT_DB_MINOR, $SVN_FS_WANT_DB_MINOR,
[The desired minor version for the Berkeley DB])
AC_DEFINE_UNQUOTED(SVN_FS_WANT_DB_PATCH, $SVN_FS_WANT_DB_PATCH,
[The desired patch version for the Berkeley DB])
AC_SUBST(SVN_DB_INCLUDES)
AC_SUBST(SVN_DB_LIBS)
SVN_LIB_SASL
if test "$svn_lib_sasl" = "yes"; then
AC_DEFINE(SVN_HAVE_SASL, 1,
[Defined if Cyrus SASL v2 is present on the system])
fi
dnl Mac OS specific features -------------------
SVN_LIB_MACHO_ITERATE
SVN_LIB_MACOS_PLIST
SVN_LIB_MACOS_KEYCHAIN
dnl APR_HAS_DSO -------------------
AC_MSG_CHECKING([whether APR has support for DSOs])
old_CPPFLAGS="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $SVN_APR_INCLUDES"
AC_PREPROC_IFELSE([AC_LANG_SOURCE([[
#include <apr.h>
#if !APR_HAS_DSO
#error
#endif]])],
APR_HAS_DSO="yes"
AC_MSG_RESULT([yes]),
APR_HAS_DSO="no"
AC_MSG_RESULT([no]))
CPPFLAGS="$old_CPPFLAGS"
dnl D-Bus (required for support for KWallet) -------------------
if test -n "$PKG_CONFIG"; then
AC_MSG_CHECKING([for D-Bus .pc file])
if $PKG_CONFIG --exists dbus-1; then
AC_MSG_RESULT([yes])
old_CPPFLAGS="$CPPFLAGS"
old_LIBS="$LIBS"
DBUS_CPPFLAGS="`$PKG_CONFIG --cflags dbus-1`"
AC_MSG_CHECKING([D-Bus version])
DBUS_VERSION="`$PKG_CONFIG --modversion dbus-1`"
AC_MSG_RESULT([$DBUS_VERSION])
# D-Bus 0.* requires DBUS_API_SUBJECT_TO_CHANGE
if test -n ["`echo "$DBUS_VERSION" | $EGREP '^0\.[[:digit:]]+'`"]; then
DBUS_CPPFLAGS="$DBUS_CPPFLAGS -DDBUS_API_SUBJECT_TO_CHANGE"
fi
DBUS_LIBS="`$PKG_CONFIG --libs dbus-1`"
CPPFLAGS="$CPPFLAGS $DBUS_CPPFLAGS"
LIBS="$LIBS $DBUS_LIBS"
AC_MSG_CHECKING([for D-Bus])
AC_LINK_IFELSE([AC_LANG_SOURCE([[
#include <dbus/dbus.h>
int main()
{dbus_bus_get(DBUS_BUS_SESSION, NULL);}]])], HAVE_DBUS="yes", HAVE_DBUS="no")
if test "$HAVE_DBUS" = "yes"; then
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
fi
CPPFLAGS="$old_CPPFLAGS"
LIBS="$old_LIBS"
else
AC_MSG_RESULT([no])
fi
fi
dnl GPG Agent -------------------
AC_ARG_WITH(gpg_agent,
AS_HELP_STRING([--without-gpg-agent],
[Disable support for GPG-Agent]),
[], [with_gpg_agent=yes])
AC_MSG_CHECKING([whether to support GPG-Agent])
if test "$with_gpg_agent" = "yes"; then
AC_MSG_RESULT([yes])
AC_DEFINE([SVN_HAVE_GPG_AGENT], [1],
[Is GPG Agent support enabled?])
else
AC_MSG_RESULT([no])
fi
AC_SUBST(SVN_HAVE_GPG_AGENT)
dnl GNOME Keyring -------------------
AC_ARG_WITH(gnome_keyring,
AS_HELP_STRING([--with-gnome-keyring],
[Enable use of GNOME Keyring for auth credentials (enabled by default if found)]),
[with_gnome_keyring="$withval"],
[with_gnome_keyring=auto])
found_gnome_keyring=no
AC_MSG_CHECKING([whether to look for GNOME Keyring])
if test "$with_gnome_keyring" != "no"; then
AC_MSG_RESULT([yes])
if test "$svn_enable_shared" = "yes"; then
if test "$APR_HAS_DSO" = "yes"; then
if test -n "$PKG_CONFIG"; then
AC_MSG_CHECKING([for GLib and GNOME Keyring .pc files])
if $PKG_CONFIG --exists glib-2.0 gnome-keyring-1; then
AC_MSG_RESULT([yes])
old_CPPFLAGS="$CPPFLAGS"
SVN_GNOME_KEYRING_INCLUDES="`$PKG_CONFIG --cflags glib-2.0 gnome-keyring-1`"
CPPFLAGS="$CPPFLAGS $SVN_GNOME_KEYRING_INCLUDES"
AC_CHECK_HEADER(gnome-keyring.h, found_gnome_keyring=yes, found_gnome_keyring=no)
AC_MSG_CHECKING([for GNOME Keyring])
if test "$found_gnome_keyring" = "yes"; then
AC_MSG_RESULT([yes])
AC_DEFINE([SVN_HAVE_GNOME_KEYRING], [1],
[Is GNOME Keyring support enabled?])
CPPFLAGS="$old_CPPFLAGS"
SVN_GNOME_KEYRING_LIBS="`$PKG_CONFIG --libs glib-2.0 gnome-keyring-1`"
else
AC_MSG_RESULT([no])
if test "$with_gnome_keyring" = "yes"; then
AC_MSG_ERROR([cannot find GNOME Keyring])
fi
fi
else
AC_MSG_RESULT([no])
if test "$with_gnome_keyring" = "yes"; then
AC_MSG_ERROR([cannot find GLib and GNOME Keyring .pc files.])
else
with_gnome_keyring=no
fi
fi
else
if test "$with_gnome_keyring" = "yes"; then
AC_MSG_ERROR([cannot find pkg-config. GNOME Keyring requires this.])
else
with_gnome_keyring=no
fi
fi
else
if test "$with_gnome_keyring" = "yes"; then
AC_MSG_ERROR([APR does not have support for DSOs. GNOME Keyring requires this.])
else
with_gnome_keyring=no
fi
fi
else
if test "$with_gnome_keyring" = "yes"; then
AC_MSG_ERROR([--with-gnome-keyring conflicts with --disable-shared])
else
with_gnome_keyring=no
fi
fi
else
AC_MSG_RESULT([no])
fi
AC_SUBST(SVN_GNOME_KEYRING_INCLUDES)
AC_SUBST(SVN_GNOME_KEYRING_LIBS)
dnl Ev2 experimental features ----------------------
dnl Note: The Ev2 implementations will be built unconditionally, but by
dnl providing this flag, users can choose to use the currently-shimmed Ev2
dnl editor implementations for various operations. This will probably
dnl negatively impact performance, but is useful for testing.
AC_ARG_ENABLE(ev2-impl,
AS_HELP_STRING([--enable-ev2-impl],
[Use Ev2 implementations, where available [EXPERIMENTAL]]),
[enable_ev2_impl=$enableval],[enable_ev2_impl=no])
if test "$enable_ev2_impl" = "yes"; then
AC_DEFINE(ENABLE_EV2_IMPL, 1,
[Define to 1 if Ev2 implementations should be used.])
fi
dnl I18n -------------------
AC_ARG_ENABLE(nls,
AS_HELP_STRING([--disable-nls],[Disable gettext functionality]),
[enable_nls=$enableval],[enable_nls=yes])
USE_NLS="no"
if test "$enable_nls" = "yes"; then
dnl First, check to see if there is a working msgfmt.
AC_PATH_PROG(MSGFMT, msgfmt, none)
AC_PATH_PROG(MSGMERGE, msgmerge, none)
AC_PATH_PROG(XGETTEXT, xgettext, none)
if test "$MSGFMT" != "none"; then
AC_SEARCH_LIBS(bindtextdomain, [intl], [],
[
enable_nls="no"
])
if test "$enable_nls" = "no"; then
# Destroy the cached result so we can test again
unset ac_cv_search_bindtextdomain
# On some systems, libintl needs libiconv to link properly,
# so try again with -liconv.
AC_SEARCH_LIBS(bindtextdomain, [intl],
[
enable_nls="yes"
# This is here so that -liconv ends up in LIBS
# if it worked with -liconv.
AC_CHECK_LIB(iconv, libiconv_open)
],
[
AC_MSG_WARN([bindtextdomain() not found. Disabling NLS.])
enable_nls="no"
], -liconv)
fi
if test "$enable_nls" = "yes"; then
AC_DEFINE(ENABLE_NLS, 1,
[Define to 1 if translation of program messages to the user's
native language is requested.])
USE_NLS="yes"
fi
fi
fi
AH_BOTTOM([
/* Indicate to translators that string X should be translated. Do not look
up the translation at run time; just expand to X. This macro is suitable
for use where a constant string is required at compile time. */
#define N_(x) x
/* Indicate to translators that we have decided the string X should not be
translated. Expand to X. */
#define U_(x) x
#ifdef ENABLE_NLS
#include <locale.h>
#include <libintl.h>
/* Indicate to translators that string X should be translated. At run time,
look up and return the translation of X. */
#define _(x) dgettext(PACKAGE_NAME, x)
/* Indicate to translators that strings X1 and X2 are singular and plural
forms of the same message, and should be translated. At run time, return
an appropriate translation depending on the number N. */
#define Q_(x1, x2, n) dngettext(PACKAGE_NAME, x1, x2, n)
#else
#define _(x) (x)
#define Q_(x1, x2, n) (((n) == 1) ? x1 : x2)
#define gettext(x) (x)
#define dgettext(domain, x) (x)
#endif
])
dnl Used to simulate makefile conditionals.
GETTEXT_CODESET=\#
NO_GETTEXT_CODESET=\#
if test $USE_NLS = "yes"; then
AC_CHECK_FUNCS(bind_textdomain_codeset,
[ GETTEXT_CODESET="" ],
[ NO_GETTEXT_CODESET="" ])
fi
AC_SUBST(GETTEXT_CODESET)
AC_SUBST(NO_GETTEXT_CODESET)
# Check if we are using GNU gettext.
GNU_GETTEXT=no
MSGFMTFLAGS=''
if test $USE_NLS = "yes"; then
AC_MSG_CHECKING(if we are using GNU gettext)
if $MSGFMT --version 2>&1 | $EGREP GNU > /dev/null; then
GNU_GETTEXT=yes
MSGFMTFLAGS='-c'
fi
AC_MSG_RESULT($GNU_GETTEXT)
fi
AC_SUBST(MSGFMTFLAGS)
dnl libmagic -------------------
libmagic_found=no
AC_ARG_WITH(libmagic,AS_HELP_STRING([--with-libmagic=PREFIX],
[libmagic filetype detection library]),
[
if test "$withval" = "yes" ; then
AC_CHECK_HEADER(magic.h, [
AC_CHECK_LIB(magic, magic_open, [libmagic_found="builtin"])
])
libmagic_prefix="the default locations"
elif test "$withval" != "no"; then
libmagic_prefix=$withval
save_cppflags="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS -I$libmagic_prefix/include"
AC_CHECK_HEADERS(magic.h,[
save_ldflags="$LDFLAGS"
LDFLAGS="-L$libmagic_prefix/lib $LDFLAGS"
AC_CHECK_LIB(magic, magic_open, [libmagic_found="yes"])
LDFLAGS="$save_ldflags"
])
CPPFLAGS="$save_cppflags"
fi
if test "$withval" != "no" && test "$libmagic_found" = "no"; then
AC_MSG_ERROR([[--with-libmagic requested, but libmagic not found at $libmagic_prefix]])
fi
],
[
AC_CHECK_HEADER(magic.h, [
AC_CHECK_LIB(magic, magic_open, [libmagic_found="builtin"])
])
])
if test "$libmagic_found" != "no"; then
AC_DEFINE([SVN_HAVE_LIBMAGIC], [1], [Defined if libmagic support is enabled])
SVN_MAGIC_LIBS="-lmagic"
fi
if test "$libmagic_found" = "yes"; then
SVN_MAGIC_INCLUDES="-I$libmagic_prefix/include"
LDFLAGS="$LDFLAGS `SVN_REMOVE_STANDARD_LIB_DIRS(-L$libmagic_prefix/lib)`"
fi
AC_SUBST(SVN_MAGIC_INCLUDES)
AC_SUBST(SVN_MAGIC_LIBS)
dnl KWallet -------------------
SVN_LIB_KWALLET
if test "$svn_lib_kwallet" = "yes"; then
AC_DEFINE([SVN_HAVE_KWALLET], 1,
[Defined if KWallet support is enabled])
fi
dnl plaintext passwords -------------------
AC_ARG_ENABLE(plaintext-password-storage,
AS_HELP_STRING([--disable-plaintext-password-storage],
[Disable on-disk caching of plaintext passwords and passphrases.
(Leaving this functionality enabled will not force Subversion
to store passwords in plaintext, but does permit users to
explicitly allow that behavior via runtime configuration.)]),
[
if test "$enableval" = "no"; then
AC_MSG_NOTICE([Disabling plaintext password/passphrase storage])
AC_DEFINE(SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE, 1,
[Defined if plaintext password/passphrase storage is disabled])
fi
])
dnl Build and install rules -------------------
INSTALL_STATIC_RULES="install-bin install-docs"
INSTALL_RULES="install-fsmod-lib install-ramod-lib install-lib install-include install-static"
INSTALL_RULES="$INSTALL_RULES $INSTALL_APACHE_RULE"
BUILD_RULES="fsmod-lib ramod-lib lib bin test sub-test $BUILD_APACHE_RULE tools"
if test "$svn_lib_berkeley_db" = "yes"; then
BUILD_RULES="$BUILD_RULES bdb-lib bdb-test"
INSTALL_RULES="`echo $INSTALL_RULES | $SED 's/install-fsmod-lib/install-fsmod-lib install-bdb-lib/'`"
INSTALL_STATIC_RULES="$INSTALL_STATIC_RULES install-bdb-lib"
BDB_TEST_DEPS="\$(BDB_TEST_DEPS)"
BDB_TEST_PROGRAMS="\$(BDB_TEST_PROGRAMS)"
fi
if test "$svn_lib_serf" = "yes"; then
BUILD_RULES="$BUILD_RULES serf-lib"
INSTALL_RULES="`echo $INSTALL_RULES | $SED 's/install-ramod-lib/install-ramod-lib install-serf-lib/'`"
INSTALL_STATIC_RULES="$INSTALL_STATIC_RULES install-serf-lib"
fi
if test "$svn_lib_kwallet" = "yes"; then
BUILD_RULES="$BUILD_RULES kwallet-lib"
INSTALL_RULES="`echo $INSTALL_RULES | $SED 's/install-lib/install-lib install-kwallet-lib/'`"
INSTALL_STATIC_RULES="$INSTALL_STATIC_RULES install-kwallet-lib"
fi
if test "$found_gnome_keyring" = "yes"; then
BUILD_RULES="$BUILD_RULES gnome-keyring-lib"
INSTALL_RULES="`echo $INSTALL_RULES | $SED 's/install-lib/install-lib install-gnome-keyring-lib/'`"
INSTALL_STATIC_RULES="$INSTALL_STATIC_RULES install-gnome-keyring-lib"
fi
if test "$USE_NLS" = "yes"; then
BUILD_RULES="$BUILD_RULES locale"
INSTALL_RULES="$INSTALL_RULES install-locale"
fi
AC_SUBST(BUILD_RULES)
AC_SUBST(INSTALL_STATIC_RULES)
AC_SUBST(INSTALL_RULES)
AC_SUBST(BDB_TEST_DEPS)
AC_SUBST(BDB_TEST_PROGRAMS)
dnl Check for header files ----------------
dnl Standard C headers
AC_HEADER_STDC
dnl Check for typedefs, structures, and compiler characteristics ----------
dnl if compiler doesn't understand `const', then define it empty
AC_C_CONST
dnl if non-existent, define size_t to be `unsigned'
AC_TYPE_SIZE_T
dnl Check for library functions ----------
AC_FUNC_MEMCMP
dnl svn_error's default warning handler uses vfprintf()
AC_FUNC_VPRINTF
dnl check for functions needed in special file handling
AC_CHECK_FUNCS(symlink readlink)
dnl check for uname
AC_CHECK_HEADERS(sys/utsname.h, [AC_CHECK_FUNCS(uname)], [])
dnl check for termios
AC_CHECK_HEADER(termios.h,[
AC_CHECK_FUNCS(tcgetattr tcsetattr,[
AC_DEFINE(HAVE_TERMIOS_H,1,[Defined if we have a usable termios library.])
])
])
dnl Process some configuration options ----------
AC_ARG_WITH(openssl,
AS_HELP_STRING([--with-openssl],
[This option does NOT affect the Subversion build process in any
way. It tells an integrated Serf HTTP client library build
process where to locate the OpenSSL library when (and only when)
building Serf as an integrated part of the Subversion build
process. When linking to a previously installed version of Serf
instead, you do not need to use this option.]),
[])
AC_ARG_ENABLE(debug,
AS_HELP_STRING([--enable-debug],
[Turn on debugging]),
[
if test "$enableval" = "yes" ; then
enable_debugging="yes"
else
enable_debugging="no"
fi
],
[
# Neither --enable-debug nor --disable-debug was passed.
enable_debugging="maybe"
])
AC_ARG_ENABLE(optimize,
AS_HELP_STRING([--enable-optimize],
[Turn on optimizations]),
[
if test "$enableval" = "yes" ; then
enable_optimization="yes"
else
enable_optimization="no"
fi
],
[
# Neither --enable-optimize nor --disable-optimize was passed.
enable_optimization="maybe"
])
dnl Use -Wl,--no-undefined during linking of some libraries
AC_ARG_ENABLE(disallowing-of-undefined-references,
[AS_HELP_STRING([--enable-disallowing-of-undefined-references],
[Use -Wl,--no-undefined flag during linking of some libraries to disallow undefined references])])
if test "$enable_disallowing_of_undefined_references" != "yes" && test "`uname`" != "Linux"; then
enable_disallowing_of_undefined_references="no"
fi
if test "$enable_disallowing_of_undefined_references" != "no"; then
AC_MSG_CHECKING([for -Wl,--no-undefined])
old_LDFLAGS="$LDFLAGS"
LDFLAGS="$LDFLAGS -Wl,--no-undefined"
AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(){;}]])], [svn_wl_no_undefined="yes"], [svn_wl_no_undefined="no"])
LDFLAGS="$old_LDFLAGS"
if test "$svn_wl_no_undefined" = "yes"; then
AC_MSG_RESULT([yes])
for library_dir in "$abs_srcdir/subversion/libsvn_"*; do
eval "`basename $library_dir`_LDFLAGS=-Wl,--no-undefined"
done
else
AC_MSG_RESULT([no])
if test "$enable_disallowing_of_undefined_references" = "yes"; then
AC_MSG_ERROR([--enable-disallowing-of-undefined-references explicitly requested, but -Wl,--no-undefined not supported])
fi
fi
fi
AC_SUBST([libsvn_auth_gnome_keyring_LDFLAGS])
AC_SUBST([libsvn_auth_kwallet_LDFLAGS])
AC_SUBST([libsvn_client_LDFLAGS])
AC_SUBST([libsvn_delta_LDFLAGS])
AC_SUBST([libsvn_diff_LDFLAGS])
AC_SUBST([libsvn_fs_LDFLAGS])
AC_SUBST([libsvn_fs_base_LDFLAGS])
AC_SUBST([libsvn_fs_fs_LDFLAGS])
AC_SUBST([libsvn_fs_util_LDFLAGS])
AC_SUBST([libsvn_ra_LDFLAGS])
AC_SUBST([libsvn_ra_local_LDFLAGS])
AC_SUBST([libsvn_ra_serf_LDFLAGS])
AC_SUBST([libsvn_ra_svn_LDFLAGS])
AC_SUBST([libsvn_repos_LDFLAGS])
AC_SUBST([libsvn_subr_LDFLAGS])
AC_SUBST([libsvn_wc_LDFLAGS])
AC_ARG_ENABLE(maintainer-mode,
AS_HELP_STRING([--enable-maintainer-mode],
[Turn on debugging and very strict compile-time warnings]),
[
if test "$enableval" = "yes" ; then
if test "$enable_debugging" = "no" ; then
AC_MSG_ERROR([Can't have --disable-debug and --enable-maintainer-mode])
fi
enable_debugging=yes
dnl Enable some extra warnings. Put these before the user's flags
dnl so the user can specify flags that override these.
if test "$GCC" = "yes"; then
AC_MSG_NOTICE([maintainer-mode: adding GCC warning flags])
dnl some additional flags that can be handy for an occasional review,
dnl but throw too many warnings in svn code, of too little importance,
dnl to keep these enabled. Remove the "dnl" to do a run with these
dnl switches enabled.
dnl ./configure CUSERFLAGS="-Wswitch-enum -Wswitch-default"
dnl Add each of the following flags only if the C compiler accepts it.
CFLAGS_KEEP="$CFLAGS"
CFLAGS=""
SVN_CFLAGS_ADD_IFELSE([-Werror=implicit-function-declaration])
SVN_CFLAGS_ADD_IFELSE([-Werror=declaration-after-statement])
SVN_CFLAGS_ADD_IFELSE([-Wextra-tokens])
SVN_CFLAGS_ADD_IFELSE([-Wnewline-eof])
SVN_CFLAGS_ADD_IFELSE([-Wshorten-64-to-32])
SVN_CFLAGS_ADD_IFELSE([-Wold-style-definition])
SVN_CFLAGS_ADD_IFELSE([-Wno-system-headers])
SVN_CFLAGS_ADD_IFELSE([-Wno-format-nonliteral])
CMAINTAINERFLAGS="$CFLAGS $CMAINTAINERFLAGS"
CFLAGS="$CFLAGS_KEEP"
dnl Add flags that all versions of GCC (should) support
CMAINTAINERFLAGS="-Wall -Wpointer-arith -Wwrite-strings -Wshadow -Wformat=2 -Wunused -Waggregate-return -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wno-multichar -Wredundant-decls -Wnested-externs -Winline -Wno-long-long $CMAINTAINERFLAGS"
fi
if test "$GXX" = "yes"; then
AC_MSG_NOTICE([maintainer-mode: adding G++ warning flags])
dnl Add each of the following flags only if the C++ compiler accepts it.
CXXFLAGS_KEEP="$CXXFLAGS"
CXXFLAGS=""
SVN_CXXFLAGS_ADD_IFELSE([-Wextra-tokens])
SVN_CXXFLAGS_ADD_IFELSE([-Wnewline-eof])
SVN_CXXFLAGS_ADD_IFELSE([-Wshorten-64-to-32])
SVN_CXXFLAGS_ADD_IFELSE([-Wno-system-headers])
CXXMAINTAINERFLAGS="$CXXFLAGS $CXXMAINTAINERFLAGS"
CXXFLAGS="$CXXFLAGS_KEEP"
dnl Add flags that all versions of G++ (should) support
CXXMAINTAINERFLAGS="-Wall -Wpointer-arith -Wwrite-strings -Wshadow -Wunused -Wunreachable-code $CXXMAINTAINERFLAGS"
fi
fi
])
if test "$enable_debugging" = "yes" ; then
dnl At the moment, we don't want optimization, because we're
dnl debugging. Unless optiization was explicitly enabled.
if test "$enable_optimization" != "yes"; then
AC_MSG_NOTICE([Disabling optimizations for debugging])
CFLAGS=["`echo $CFLAGS' ' | $SED -e 's/-O[^ ]* //g'`"]
CXXFLAGS=["`echo $CXXFLAGS' ' | $SED -e 's/-O[^ ]* //g'`"]
fi
dnl Add debugging flags, unless they were set by the user
if test -z ["`echo $CUSERFLAGS' ' | $EGREP -- '-g[0-9]? '`"]; then
AC_MSG_NOTICE([Enabling debugging for C])
CFLAGS=["`echo $CFLAGS' ' | $SED -e 's/-g[0-9] //g' -e 's/-g //g'`"]
SVN_CFLAGS_ADD_IFELSE([-fno-inline])
SVN_CFLAGS_ADD_IFELSE([-fno-omit-frame-pointer])
SVN_CFLAGS_ADD_IFELSE([-g3],[],[
SVN_CFLAGS_ADD_IFELSE([-g2],[],[
SVN_CFLAGS_ADD_IFELSE([-g])])])
fi
if test -z ["`echo $CXXUSERFLAGS' ' | $EGREP -- '-g[0-9]? '`"]; then
AC_MSG_NOTICE([Enabling debugging for C++])
CXXFLAGS=["`echo $CXXFLAGS' ' | $SED -e 's/-g[0-9] //g' -e 's/-g //g'`"]
SVN_CXXFLAGS_ADD_IFELSE([-fno-inline])
SVN_CXXFLAGS_ADD_IFELSE([-fno-omit-frame-pointer])
SVN_CXXFLAGS_ADD_IFELSE([-g3],[],[
SVN_CXXFLAGS_ADD_IFELSE([-g2],[],[
SVN_CXXFLAGS_ADD_IFELSE([-g])])])
fi
dnl SVN_DEBUG enables specific features for developer builds
dnl AP_DEBUG enables specific (Apache) features for developer builds
CFLAGS="$CFLAGS -DSVN_DEBUG -DAP_DEBUG"
CXXFLAGS="$CXXFLAGS -DSVN_DEBUG -DAP_DEBUG"
elif test "$enable_debugging" = "no" ; then
AC_MSG_NOTICE([Disabling debugging])
CFLAGS=["`echo $CFLAGS' ' | $SED -e 's/-g[0-9] //g' -e 's/-g //g'`"]
CXXFLAGS=["`echo $CXXFLAGS' ' | $SED -e 's/-g[0-9] //g' -e 's/-g //g'`"]
dnl Compile with NDEBUG to get rid of assertions
CFLAGS="$CFLAGS -DNDEBUG"
CXXFLAGS="$CXXFLAGS -DNDEBUG"
# elif test "$enable_debugging" = "maybe" ; then
# # do nothing
fi
if test "$enable_optimization" = "yes"; then
dnl Add optimization flags, unless they were set by the user
if test -z ["`echo $CUSERFLAGS' ' | $EGREP -- '-O[^ ]* '`"]; then
CFLAGS=["`echo $CFLAGS' ' | $SED -e 's/-O[^ ]* //g'`"]
if test "$enable_debugging" = "yes"; then
AC_MSG_NOTICE([Enabling optimizations for C (with debugging enabled)])
SVN_CFLAGS_ADD_IFELSE([-O1],[],[
SVN_CFLAGS_ADD_IFELSE([-O])])
else
AC_MSG_NOTICE([Enabling optimizations for C])
SVN_CFLAGS_ADD_IFELSE([-O3],[],[
SVN_CFLAGS_ADD_IFELSE([-O2],[],[
SVN_CFLAGS_ADD_IFELSE([-O1],[],[
SVN_CFLAGS_ADD_IFELSE([-O])])])])
SVN_CFLAGS_ADD_IFELSE([-Wno-clobbered])
SVN_CFLAGS_ADD_IFELSE([-flto])
fi
fi
if test -z ["`echo $CXXUSERFLAGS' ' | $EGREP -- '-O[^ ]* '`"]; then
CXXFLAGS=["`echo $CXXFLAGS' ' | $SED -e 's/-O[^ ]* //g'`"]
if test "$enable_debugging" = "yes"; then
AC_MSG_NOTICE([Enabling optimizations for C++ (with debugging enabled)])
SVN_CXXFLAGS_ADD_IFELSE([-O1],[],[
SVN_CXXFLAGS_ADD_IFELSE([-O])])
else
AC_MSG_NOTICE([Enabling optimizations for C++])
SVN_CXXFLAGS_ADD_IFELSE([-O3],[],[
SVN_CXXFLAGS_ADD_IFELSE([-O2],[],[
SVN_CXXFLAGS_ADD_IFELSE([-O1],[],[
SVN_CXXFLAGS_ADD_IFELSE([-O])])])])
SVN_CXXFLAGS_ADD_IFELSE([-Wno-clobbered])
SVN_CXXFLAGS_ADD_IFELSE([-flto])
fi
fi
elif test "$enable_optimization" = "no"; then
dnl Remove all optimization flags
AC_MSG_NOTICE([Disabling optimizations])
CFLAGS=["`echo $CFLAGS' ' | $SED -e 's/-O[^ ]* //g'`"]
CXXFLAGS=["`echo $CXXFLAGS' ' | $SED -e 's/-O[^ ]* //g'`"]
# elif test "$enable_optimization" = "maybe" ; then
# # do nothing
fi
dnl Dump the current compiler options
AC_MSG_NOTICE([C compiler flags: $CFLAGS])
AC_MSG_NOTICE([ user-defined: $CUSERFLAGS])
AC_MSG_NOTICE([ maintainer-mode: $CMAINTAINERFLAGS])
AC_MSG_NOTICE([C++ compiler flags: $CXXFLAGS])
AC_MSG_NOTICE([ user-defined: $CXXUSERFLAGS])
AC_MSG_NOTICE([ maintainer-mode: $CXXMAINTAINERFLAGS])
AC_ARG_ENABLE(full-version-match,
AS_HELP_STRING([--disable-full-version-match],
[Disable the full version match rules when checking
Subversion library compatibility.]),
[
if test "$enableval" = "no" ; then
AC_MSG_NOTICE([Disabling svn full version matching])
AC_DEFINE(SVN_DISABLE_FULL_VERSION_MATCH, 1,
[Defined if the full version matching rules are disabled])
fi
])
AC_ARG_WITH(editor,
AS_HELP_STRING([--with-editor=PATH],
[Specify a default editor for the subversion client.]),
[
if test "$withval" = "yes" ; then
AC_MSG_ERROR([--with-editor requires an argument.])
else
SVN_CLIENT_EDITOR=$withval
AC_DEFINE_UNQUOTED(SVN_CLIENT_EDITOR, "$SVN_CLIENT_EDITOR",
[The path of a default editor for the client.])
fi
])
SVN_LIB_Z
MOD_ACTIVATION=""
AC_ARG_ENABLE(mod-activation,
AS_HELP_STRING([--enable-mod-activation],
[Enable mod_dav_svn in httpd.conf]),
[
if test "$enableval" = "yes" ; then
MOD_ACTIVATION="-a"
AC_MSG_NOTICE([Enabling apache module activation])
else
AC_MSG_NOTICE([Disabling apache module activation])
fi
])
AC_SUBST(MOD_ACTIVATION)
AC_ARG_ENABLE(gcov,
AC_HELP_STRING([--enable-gcov],
[Turn on gcov coverage testing (GCC only).]),
[
if test "$enableval" = "yes" ; then
dnl Probably other compilers support something similar;
dnl feel free to extend this to include them.
if test "$GCC" = "yes"; then
if test "$svn_enable_shared" = "yes" ; then
AC_MSG_ERROR([Can't have --enable-gcov without --disable-shared (we
recommend also using --enable-all-static).])
fi
if test ! "$enable_all_static" = "yes" ; then
AC_MSG_WARN(We recommend --enable-all-static with --enable-gcov.)
fi
AC_MSG_NOTICE([Enabling gcov coverage testing.])
CFLAGS="$CFLAGS -fprofile-arcs -ftest-coverage"
CXXFLAGS="$CXXFLAGS -fprofile-arcs -ftest-coverage"
else
AC_MSG_ERROR([We only support --enable-gcov with GCC right now.])
fi
fi
])
AC_ARG_ENABLE(gprof,
AS_HELP_STRING([--enable-gprof],
[Produce gprof profiling data in 'gmon.out' (GCC only).]),
[
if test "$enableval" = "yes" ; then
dnl Probably other compilers support -pg or something similar;
dnl feel free to extend this to include them.
if test "$GCC" = "yes"; then
if test "$svn_enable_shared" = "yes" ; then
AC_MSG_ERROR([Can't have --enable-gprof without --disable-shared (we
recommend also using --enable-all-static).])
fi
if test ! "$enable_all_static" = "yes" ; then
AC_MSG_WARN(We recommend --enable-all-static with --enable-gprof.)
fi
AC_MSG_NOTICE([Enabling gprof profiling data (to gmon.out).])
CFLAGS="$CFLAGS -pg"
CXXFLAGS="$CXXFLAGS -pg"
LT_LDFLAGS="$LT_LDFLAGS -pg"
else
AC_MSG_ERROR([We only support --enable-gprof with GCC right now.])
fi
fi
])
# Scripting and Bindings languages
# Python: Used for testsuite, and bindings
PYTHON="`$abs_srcdir/build/find_python.sh`"
if test -z "$PYTHON"; then
AC_MSG_WARN([Python 2.5 or later is required to run the testsuite])
AC_MSG_WARN([or to use the Subversion Python bindings])
AC_MSG_WARN([])
AC_MSG_WARN([If you have a suitable Python installed, but not on the])
AC_MSG_WARN([PATH, set the environment variable PYTHON to the full path])
AC_MSG_WARN([to the Python executable, and re-run configure])
fi
AC_PATH_PROGS(PYTHON, "$PYTHON", none)
# The minimum version for the JVM runtime for our Java bytecode.
JAVA_OLDEST_WORKING_VER='1.5'
# SVN_CHECK_JDK sets $JAVA_CLASSPATH
SVN_CHECK_JDK($JAVA_OLDEST_WORKING_VER)
AC_PATH_PROG(PERL, perl, none)
if test -n "$RUBY"; then
AC_PATH_PROG(RUBY, "$RUBY", none)
else
- AC_PATH_PROGS(RUBY, ruby ruby1.8 ruby18 ruby1.9 ruby1 ruby1.9.3 ruby193, none)
+ AC_PATH_PROGS(RUBY, ruby ruby1.8 ruby18 ruby1.9 ruby1 ruby1.9.3 ruby193 ruby2.0 ruby2.1, none)
fi
if test "$RUBY" != "none"; then
AC_MSG_CHECKING([rb_hash_foreach])
if "$RUBY" -r mkmf -e 'exit(have_func("rb_hash_foreach") ? 0 : 1)' >/dev/null; then
AC_MSG_RESULT([yes])
if test -n "$RDOC"; then
AC_PATH_PROG(RDOC, "$RDOC", none)
else
- AC_PATH_PROGS(RDOC, rdoc rdoc1.8 rdoc18 rdoc1.9 rdoc19 rdoc1.9.3 rdoc193, none)
+ AC_PATH_PROGS(RDOC, rdoc rdoc1.8 rdoc18 rdoc1.9 rdoc19 rdoc1.9.3 rdoc193 rdoc2.0 rdoc2.1, none)
fi
AC_CACHE_CHECK([for Ruby major version], [svn_cv_ruby_major],[
svn_cv_ruby_major="`$RUBY -rrbconfig -e 'print RbConfig::CONFIG.fetch(%q(MAJOR))'`"
])
RUBY_MAJOR="$svn_cv_ruby_major"
AC_CACHE_CHECK([for Ruby minor version], [svn_cv_ruby_minor],[
svn_cv_ruby_minor="`$RUBY -rrbconfig -e 'print RbConfig::CONFIG.fetch(%q(MINOR))'`"
])
RUBY_MINOR="$svn_cv_ruby_minor"
AC_CACHE_CHECK([for Ruby teeny version], [svn_cv_ruby_teeny],[
svn_cv_ruby_teeny="`$RUBY -rrbconfig -e 'major, minor, teeny = RUBY_VERSION.split("."); print teeny;'`"
])
RUBY_TEENY="$svn_cv_ruby_teeny"
AC_SUBST(RUBY_MAJOR)
AC_SUBST(RUBY_MINOR)
AC_SUBST(RUBY_TEENY)
if test \( "$RUBY_MAJOR" -eq "1" -a "$RUBY_MINOR" -gt "8" -a "$RUBY_TEENY" -lt "3" \); then
# Disallow Ruby between 1.8.7 and 1.9.3
RUBY="none"
AC_MSG_WARN([The detected Ruby is between 1.9 and 1.9.3])
AC_MSG_WARN([Only 1.8.x and 1.9.3 releases are supported at this time])
fi
else
AC_MSG_RESULT([no])
RUBY="none"
AC_MSG_WARN([The detected Ruby is too old for Subversion to use])
AC_MSG_WARN([A Ruby which has rb_hash_foreach is required to use the])
AC_MSG_WARN([Subversion Ruby bindings])
AC_MSG_WARN([Upgrade to the official 1.8.2 release, or later])
fi
fi
SVN_CHECK_SWIG
SVN_CHECK_CTYPESGEN
dnl decide whether we want to link against the RA/FS libraries
AC_ARG_ENABLE(runtime-module-search,
AS_HELP_STRING([--enable-runtime-module-search],
[Turn on dynamic loading of RA/FS libraries including
third-party FS libraries]),
[
if test "$enableval" = "yes"; then
use_dso=yes
if test "$svn_enable_shared" = "no"; then
AC_MSG_ERROR([--enable-runtime-module-search conflicts with --disable-shared])
fi
AC_DEFINE(SVN_USE_DSO, 1,
[Defined if svn should try to load DSOs])
fi
])
if test "$svn_enable_shared" = "no" || test "$use_dso" != "yes"; then
AC_DEFINE(SVN_LIBSVN_CLIENT_LINKS_RA_LOCAL, 1,
[Defined if libsvn_client should link against libsvn_ra_local])
svn_ra_lib_deps="\$(RA_LOCAL_DEPS)"
svn_ra_lib_install_deps="install-ramod-lib"
svn_ra_lib_link="\$(RA_LOCAL_LINK)"
AC_DEFINE(SVN_LIBSVN_CLIENT_LINKS_RA_SVN, 1,
[Defined if libsvn_client should link against libsvn_ra_svn])
svn_ra_lib_deps="$svn_ra_lib_deps \$(RA_SVN_DEPS)"
svn_ra_lib_link="$svn_ra_lib_link \$(RA_SVN_LINK)"
if test "$svn_lib_serf" = "yes"; then
AC_DEFINE(SVN_LIBSVN_CLIENT_LINKS_RA_SERF, 1,
[Defined if libsvn_client should link against libsvn_ra_serf])
svn_ra_lib_deps="$svn_ra_lib_deps \$(RA_SERF_DEPS)"
svn_ra_lib_install_deps="$svn_ra_lib_install_deps install-serf-lib"
svn_ra_lib_link="$svn_ra_lib_link \$(RA_SERF_LINK)"
fi
SVN_RA_LIB_DEPS=$svn_ra_lib_deps
SVN_RA_LIB_INSTALL_DEPS=$svn_ra_lib_install_deps
SVN_RA_LIB_LINK=$svn_ra_lib_link
AC_DEFINE(SVN_LIBSVN_FS_LINKS_FS_FS, 1,
[Defined if libsvn_fs should link against libsvn_fs_fs])
svn_fs_lib_deps="\$(FS_FS_DEPS)"
svn_fs_lib_install_deps="install-fsmod-lib"
svn_fs_lib_link="\$(FS_FS_LINK)"
if test "$svn_lib_berkeley_db" = "yes"; then
AC_DEFINE(SVN_LIBSVN_FS_LINKS_FS_BASE, 1,
[Defined if libsvn_fs should link against libsvn_fs_base])
svn_fs_lib_deps="$svn_fs_lib_deps \$(FS_BASE_DEPS)"
svn_fs_lib_install_deps="$svn_fs_lib_install_deps install-bdb-lib"
svn_fs_lib_link="$svn_fs_lib_link \$(FS_BASE_LINK)"
fi
SVN_FS_LIB_DEPS=$svn_fs_lib_deps
SVN_FS_LIB_INSTALL_DEPS=$svn_fs_lib_install_deps
SVN_FS_LIB_LINK=$svn_fs_lib_link
fi
AC_SUBST(SVN_RA_LIB_DEPS)
AC_SUBST(SVN_RA_LIB_INSTALL_DEPS)
AC_SUBST(SVN_RA_LIB_LINK)
AC_SUBST(SVN_FS_LIB_DEPS)
AC_SUBST(SVN_FS_LIB_INSTALL_DEPS)
AC_SUBST(SVN_FS_LIB_LINK)
# ==== JavaHL ================================================================
dnl Possibly compile JavaHL
do_javahl_build=no
AC_ARG_ENABLE(javahl,
AS_HELP_STRING([--enable-javahl],
[Enable compilation of Java high-level bindings (requires C++)]),
[ if test "$enableval" = "yes" ; then
do_javahl_build="yes"
fi
])
JAVAHL_OBJDIR=""
INSTALL_EXTRA_JAVAHL_LIB=""
FIX_JAVAHL_LIB=""
JAVAHL_TESTS_TARGET=""
JAVAHL_COMPAT_TESTS_TARGET=""
LT_CXX_LIBADD=""
if test "$do_javahl_build" = "yes"; then
dnl Check for suitable JDK
if test "$JDK_SUITABLE" = "no"; then
AC_MSG_ERROR([Cannot compile JavaHL without a suitable JDK.
Please specify a suitable JDK using the --with-jdk option.])
fi
dnl The temporary directory where libtool compiles libsvnjavahl.
JAVAHL_OBJDIR='$(libsvnjavahl_PATH)/.libs'
os_arch=`uname`
if test "$os_arch" = "Darwin"; then
dnl On Darwin, JNI libs must be installed as .jnilib
INSTALL_EXTRA_JAVAHL_LIB='ln -sf $(libdir)/libsvnjavahl-1.dylib $(libdir)/libsvnjavahl-1.jnilib'
FIX_JAVAHL_LIB="ln -sf libsvnjavahl-1.dylib $JAVAHL_OBJDIR/libsvnjavahl-1.jnilib"
fi
# This segment (and the rest of r10800) is very likely unnecessary
# with libtool 1.5, which automatically adds libstdc++ as a
# dependency to the C++ libraries it builds. So at some future time
# when autogen.sh requires libtool 1.5 or higher, we can get rid of
# it.
AC_MSG_CHECKING([for additional flags to link C++ libraries])
if test "x$ac_compiler_gnu" = "xyes"; then
LT_CXX_LIBADD="-lstdc++"
AC_MSG_RESULT([$LT_CXX_LIBADD])
else
AC_MSG_RESULT([none needed])
fi
fi
AC_SUBST(INSTALL_EXTRA_JAVAHL_LIB)
AC_SUBST(JAVAHL_OBJDIR)
AC_SUBST(FIX_JAVAHL_LIB)
AC_SUBST(LT_CXX_LIBADD)
AC_ARG_WITH(junit,
AS_HELP_STRING([--with-junit=PATH],
[Specify a path to the junit JAR file.]),
[
if test "$withval" != "no"; then
if test -n "$JAVA_CLASSPATH"; then
JAVA_CLASSPATH="$withval:$JAVA_CLASSPATH"
else
JAVA_CLASSPATH="$withval"
fi
JAVAHL_TESTS_TARGET="javahl-tests"
JAVAHL_COMPAT_TESTS_TARGET="javahl-compat-tests"
fi
])
AC_SUBST(JAVA_CLASSPATH)
AC_SUBST(JAVAHL_TESTS_TARGET)
AC_SUBST(JAVAHL_COMPAT_TESTS_TARGET)
# ==== Miscellaneous bits ====================================================
# Strip '-no-cpp-precomp' from CPPFLAGS for the clang compiler
### I think we get this flag from APR, so the fix probably belongs there
if test "$CC" = "clang"; then
SVN_STRIP_FLAG(CPPFLAGS, [-no-cpp-precomp ])
fi
# Need to strip '-no-cpp-precomp' from CPPFLAGS for SWIG as well.
SWIG_CPPFLAGS="$CPPFLAGS"
SVN_STRIP_FLAG(SWIG_CPPFLAGS, [-no-cpp-precomp ])
AC_SUBST([SWIG_CPPFLAGS])
dnl Since this is used only on Unix-y systems, define the path separator as '/'
AC_DEFINE_UNQUOTED(SVN_PATH_LOCAL_SEPARATOR, '/',
[Defined to be the path separator used on your local filesystem])
AC_DEFINE_UNQUOTED(SVN_NULL_DEVICE_NAME, "/dev/null",
[Defined to be the null device for the system])
DEFAULT_FS_TYPE="fsfs"
AC_DEFINE_UNQUOTED(DEFAULT_FS_TYPE, "$DEFAULT_FS_TYPE",
[The fs type to use by default])
DEFAULT_HTTP_LIBRARY="serf"
AC_DEFINE_UNQUOTED(DEFAULT_HTTP_LIBRARY, "$DEFAULT_HTTP_LIBRARY",
[The http library to use by default])
# BSD/OS (BSDi) needs to use a different include syntax in Makefile
INCLUDE_OUTPUTS="include \$(top_srcdir)/build-outputs.mk"
case "$host" in
*bsdi*)
# Check whether they've installed GNU make
if ! make --version > /dev/null 2>&1; then
# BSDi make
INCLUDE_OUTPUTS=".include \"\$(top_srcdir)/build-outputs.mk\""
fi
;;
esac
AC_SUBST(INCLUDE_OUTPUTS)
# ==== Detection complete - output and run config.status =====================
AC_CONFIG_HEADERS(subversion/svn_private_config.h.tmp:subversion/svn_private_config.h.in)
AC_CONFIG_COMMANDS([svn_private_config.h.tmp],
[svn_cf=subversion/svn_private_config.h;
$SED -e "s/@SVN_DB_HEADER@/$SVN_DB_HEADER/" $svn_cf.tmp > $svn_cf.tmp.new
cmp -s $svn_cf.tmp.new $svn_cf || mv -f $svn_cf.tmp.new $svn_cf
rm -f $svn_cf.tmp.new $svn_cf.tmp],
[SED="$SED"
SVN_DB_HEADER="$SVN_DB_HEADER"])
AC_CONFIG_FILES([Makefile])
SVN_CONFIG_SCRIPT(tools/backup/hot-backup.py)
SVN_CONFIG_SCRIPT(tools/hook-scripts/commit-access-control.pl)
SVN_CONFIG_SCRIPT(subversion/bindings/swig/perl/native/Makefile.PL)
if test -e packages/solaris/pkginfo.in; then
SVN_CONFIG_SCRIPT(packages/solaris/pkginfo)
fi
AC_SUBST(SVN_CONFIG_SCRIPT_FILES)
# Ensure that SWIG is checked after reconfiguration.
rm -f .swig_checked
dnl Provide ${host} for use in compiled code (for svn --version)
AC_DEFINE_UNQUOTED([SVN_BUILD_HOST], "${host}",
[Defined to the config.guess name of the build system])
dnl Provide ${target} for use in compiled code (for user-agent string)
AC_DEFINE_UNQUOTED([SVN_BUILD_TARGET], "${target}",
[Defined to the config.guess name of the build target])
AC_OUTPUT
# ==== Print final messages to user ==========================================
dnl Configure is long - users tend to miss warnings printed during it.
dnl Hence, print a warnings about what we did and didn't configure at the
dnl end, where people will actually see them.
if test "$svn_have_berkeley_db" = "no6" && test "$enable_bdb6" != "no"; then
AC_MSG_WARN([We have configured without BDB filesystem support
Berkeley DB 6 was found, but not used. Please re-run configure (see
./config.nice) with the '--enable-bdb6' flag to use it,
or explicitly specify '--disable-bdb6' or '--without-berkeley-db'
to silence this warning.
Please note that some versions of Berkeley DB 6+ are under the GNU Affero
General Public License, version 3:
https://oss.oracle.com/pipermail/bdb/2013-June/000056.html
The AGPL-3.0 licence may impose special requirements for making available
source code of server-side software. The text of the licence is:
https://www.gnu.org/licenses/agpl-3.0.html
http://opensource.org/licenses/AGPL-3.0
The Berkeley DB backend to Subversion is deprecated; see
http://subversion.apache.org/docs/release-notes/1.8#bdb-deprecated
The Subversion developers have not tested Subversion with Berkeley DB 6 for
technical problems or bugs.
])
fi
Index: vendor/subversion/dist/get-deps.sh
===================================================================
--- vendor/subversion/dist/get-deps.sh (revision 286500)
+++ vendor/subversion/dist/get-deps.sh (revision 286501)
@@ -1,173 +1,173 @@
#!/bin/sh
#
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
#
#
# get-deps.sh -- download the dependencies useful for building Subversion
#
# If changing this file please take care to try to make your changes as
# portable as possible. That means at a minimum only use POSIX supported
# features and functions. However, it may be desirable to use an even
# more narrow set of features than POSIX, e.g. Solaris /bin/sh only has
# a subset of the POSIX shell features. If in doubt, limit yourself to
# features already used in the file. Reviewing the history of changes
# may be useful as well.
APR_VERSION=${APR_VERSION:-"1.4.6"}
APU_VERSION=${APU_VERSION:-"1.5.1"}
-SERF_VERSION=${SERF_VERSION:-"1.3.4"}
+SERF_VERSION=${SERF_VERSION:-"1.3.8"}
ZLIB_VERSION=${ZLIB_VERSION:-"1.2.8"}
SQLITE_VERSION=${SQLITE_VERSION:-"3.7.15.1"}
GTEST_VERSION=${GTEST_VERSION:-"1.6.0"}
HTTPD_VERSION=${HTTPD_VERSION:-"2.4.10"}
APR_ICONV_VERSION=${APR_ICONV_VERSION:-"1.2.1"}
APR=apr-${APR_VERSION}
APR_UTIL=apr-util-${APU_VERSION}
SERF=serf-${SERF_VERSION}
ZLIB=zlib-${ZLIB_VERSION}
SQLITE_VERSION_LIST=`echo $SQLITE_VERSION | sed -e 's/\./ /g'`
SQLITE=sqlite-amalgamation-`printf %d%02d%02d%02d $SQLITE_VERSION_LIST`
GTEST=gtest-${GTEST_VERSION}
GTEST_URL=http://googletest.googlecode.com/files/
HTTPD=httpd-${HTTPD_VERSION}
APR_ICONV=apr-iconv-${APR_ICONV_VERSION}
BASEDIR=`pwd`
TEMPDIR=$BASEDIR/temp
HTTP_FETCH=
[ -z "$HTTP_FETCH" ] && type wget >/dev/null 2>&1 && HTTP_FETCH="wget -q -nc"
[ -z "$HTTP_FETCH" ] && type curl >/dev/null 2>&1 && HTTP_FETCH="curl -sOL"
[ -z "$HTTP_FETCH" ] && type fetch >/dev/null 2>&1 && HTTP_FETCH="fetch -q"
# Need this uncommented if any of the specific versions of the ASF tarballs to
# be downloaded are no longer available on the general mirrors.
APACHE_MIRROR=http://archive.apache.org/dist
# helpers
usage() {
echo "Usage: $0"
echo "Usage: $0 [ apr | serf | zlib | sqlite | gtest ] ..."
exit $1
}
# getters
get_apr() {
cd $TEMPDIR
test -d $BASEDIR/apr || $HTTP_FETCH $APACHE_MIRROR/apr/$APR.tar.bz2
test -d $BASEDIR/apr-util || $HTTP_FETCH $APACHE_MIRROR/apr/$APR_UTIL.tar.bz2
cd $BASEDIR
test -d $BASEDIR/apr || bzip2 -dc $TEMPDIR/$APR.tar.bz2 | tar -xf -
test -d $BASEDIR/apr-util || bzip2 -dc $TEMPDIR/$APR_UTIL.tar.bz2 | tar -xf -
test -d $BASEDIR/apr || mv $APR apr
test -d $BASEDIR/apr-util || mv $APR_UTIL apr-util
}
get_serf() {
test -d $BASEDIR/serf && return
cd $TEMPDIR
$HTTP_FETCH http://serf.googlecode.com/svn/src_releases/$SERF.tar.bz2
cd $BASEDIR
bzip2 -dc $TEMPDIR/$SERF.tar.bz2 | tar -xf -
mv $SERF serf
}
get_zlib() {
test -d $BASEDIR/zlib && return
cd $TEMPDIR
$HTTP_FETCH http://sourceforge.net/projects/libpng/files/zlib/$ZLIB_VERSION/$ZLIB.tar.gz
cd $BASEDIR
gzip -dc $TEMPDIR/$ZLIB.tar.gz | tar -xf -
mv $ZLIB zlib
}
get_sqlite() {
test -d $BASEDIR/sqlite-amalgamation && return
cd $TEMPDIR
$HTTP_FETCH http://www.sqlite.org/$SQLITE.zip
cd $BASEDIR
unzip -q $TEMPDIR/$SQLITE.zip
mv $SQLITE sqlite-amalgamation
}
get_gtest() {
test -d $BASEDIR/gtest && return
cd $TEMPDIR
$HTTP_FETCH ${GTEST_URL}/${GTEST}.zip
cd $BASEDIR
unzip -q $TEMPDIR/$GTEST.zip
mv $GTEST gtest
}
# main()
get_deps() {
mkdir -p $TEMPDIR
for i in zlib serf sqlite-amalgamation apr apr-util gtest; do
if [ -d $i ]; then
echo "Local directory '$i' already exists; the downloaded copy won't be used" >&2
fi
done
if [ $# -gt 0 ]; then
for target in "$@"; do
if [ "$target" != "deps" ]; then
get_$target || usage
else
usage
fi
done
else
get_apr
get_serf
get_zlib
get_sqlite
echo
echo "If you require mod_dav_svn, the recommended version of httpd is:"
echo " $APACHE_MIRROR/httpd/$HTTPD.tar.bz2"
echo
echo "If you require apr-iconv, its recommended version is:"
echo " $APACHE_MIRROR/apr/$APR_ICONV.tar.bz2"
fi
rm -rf $TEMPDIR
}
get_deps "$@"
Index: vendor/subversion/dist/subversion/include/private/svn_diff_private.h
===================================================================
--- vendor/subversion/dist/subversion/include/private/svn_diff_private.h (revision 286500)
+++ vendor/subversion/dist/subversion/include/private/svn_diff_private.h (revision 286501)
@@ -1,115 +1,115 @@
/**
* @copyright
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
* @endcopyright
*/
#ifndef SVN_DIFF_PRIVATE_H
#define SVN_DIFF_PRIVATE_H
#include <apr_pools.h>
#include <apr_tables.h>
#include "svn_types.h"
#include "svn_io.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* The separator string used below the "Index:" or similar line of
* Subversion's Unidiff-like diff format. */
#define SVN_DIFF__EQUAL_STRING \
"==================================================================="
/* The separator string used below the "Properties on ..." line of
* Subversion's Unidiff-like diff format. */
#define SVN_DIFF__UNDER_STRING \
"___________________________________________________________________"
/* The string used to mark a line in a hunk that doesn't end with a newline,
* when diffing a file. Intentionally not marked for translation, for wider
* interoperability with patch(1) programs. */
#define SVN_DIFF__NO_NEWLINE_AT_END_OF_FILE \
"\\ No newline at end of file"
/* The string used to mark a line in a hunk that doesn't end with a newline,
* when diffing a Subversion property. */
#define SVN_DIFF__NO_NEWLINE_AT_END_OF_PROPERTY \
"\\ No newline at end of property"
/* Write a unidiff "---" and "+++" header to OUTPUT_STREAM.
*
* Write "---" followed by a space and OLD_HEADER and a newline,
* then "+++" followed by a space and NEW_HEADER and a newline.
*
* The text will be encoded into HEADER_ENCODING.
*/
svn_error_t *
svn_diff__unidiff_write_header(svn_stream_t *output_stream,
const char *header_encoding,
const char *old_header,
const char *new_header,
apr_pool_t *scratch_pool);
/* Display property changes in pseudo-Unidiff format.
*
* Write to @a outstream the changes described by @a propchanges based on
* original properties @a original_props.
*
* Write all mark-up text (headers and so on) using the character encoding
* @a encoding.
*
* ### I think the idea is: we want the output to use @a encoding, and
* we will assume the text of the user's files and the values of any
* user-defined properties are already using @a encoding, so we don't
* want to re-code the *whole* output.
* So, shouldn't we also convert all prop names and all 'svn:*' prop
* values to @a encoding, since we know those are stored in UTF-8?
*
* @a original_props is a hash mapping (const char *) property names to
* (svn_string_t *) values. @a propchanges is an array of svn_prop_t
* representing the new values for any of the properties that changed, with
* a NULL value to represent deletion.
*
* If @a pretty_print_mergeinfo is true, then describe 'svn:mergeinfo'
* property changes in a human-readable form that says what changes were
* merged or reverse merged; otherwise (or if the mergeinfo property values
* don't parse correctly) display them just like any other property.
*
- * Use @a pool for temporary allocations.
+ * Use @a scratch_pool for temporary allocations.
*/
svn_error_t *
svn_diff__display_prop_diffs(svn_stream_t *outstream,
const char *encoding,
const apr_array_header_t *propchanges,
apr_hash_t *original_props,
svn_boolean_t pretty_print_mergeinfo,
- apr_pool_t *pool);
+ apr_pool_t *scratch_pool);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* SVN_DIFF_PRIVATE_H */
Index: vendor/subversion/dist/subversion/include/private/svn_mergeinfo_private.h
===================================================================
--- vendor/subversion/dist/subversion/include/private/svn_mergeinfo_private.h (revision 286500)
+++ vendor/subversion/dist/subversion/include/private/svn_mergeinfo_private.h (revision 286501)
@@ -1,270 +1,291 @@
/**
* @copyright
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
* @endcopyright
*
* @file svn_mergeinfo_private.h
* @brief Subversion-internal mergeinfo APIs.
*/
#ifndef SVN_MERGEINFO_PRIVATE_H
#define SVN_MERGEINFO_PRIVATE_H
#include <apr_pools.h>
#include "svn_types.h"
#include "svn_error.h"
#include "svn_mergeinfo.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* Set inheritability of all ranges in RANGELIST to INHERITABLE.
If RANGELIST is NULL do nothing. */
void
svn_rangelist__set_inheritance(svn_rangelist_t *rangelist,
svn_boolean_t inheritable);
/* Parse a rangelist from the string STR. Set *RANGELIST to the result,
* allocated in RESULT_POOL. Return an error if the rangelist is not
* well-formed (for example, if it contains invalid characters or if
* R1 >= R2 in a "R1-R2" range element).
*
* Unlike svn_mergeinfo_parse(), this does not sort the ranges into order
* or combine adjacent and overlapping ranges.
*
* The compaction can be done with svn_rangelist__combine_adjacent_ranges().
*/
svn_error_t *
svn_rangelist__parse(svn_rangelist_t **rangelist,
const char *str,
apr_pool_t *result_pool);
/* In-place combines adjacent ranges in a rangelist.
SCRATCH_POOL is just used for providing error messages. */
svn_error_t *
svn_rangelist__combine_adjacent_ranges(svn_rangelist_t *rangelist,
apr_pool_t *scratch_pool);
+/** Canonicalize the @a rangelist: sort the ranges, and combine adjacent or
+ * overlapping ranges into single ranges where possible.
+ *
+ * If overlapping ranges have different inheritability, return an error.
+ *
+ * Modify @a rangelist in place. Use @a scratch_pool for temporary
+ * allocations.
+ */
+svn_error_t *
+svn_rangelist__canonicalize(svn_rangelist_t *rangelist,
+ apr_pool_t *scratch_pool);
+
+/** Canonicalize the revision range lists in the @a mergeinfo.
+ *
+ * Modify @a mergeinfo in place. Use @a scratch_pool for temporary
+ * allocations.
+ */
+svn_error_t *
+svn_mergeinfo__canonicalize_ranges(svn_mergeinfo_t mergeinfo,
+ apr_pool_t *scratch_pool);
+
/* Set inheritability of all rangelists in MERGEINFO to INHERITABLE.
If MERGEINFO is NULL do nothing. If a rangelist in MERGEINFO is
NULL leave it alone. */
void
svn_mergeinfo__set_inheritance(svn_mergeinfo_t mergeinfo,
svn_boolean_t inheritable,
apr_pool_t *scratch_pool);
/* Return whether INFO1 and INFO2 are equal in *IS_EQUAL.
CONSIDER_INHERITANCE determines how the rangelists in the two
hashes are compared for equality. If CONSIDER_INHERITANCE is FALSE,
then the start and end revisions of the svn_merge_range_t's being
compared are the only factors considered when determining equality.
e.g. '/trunk: 1,3-4*,5' == '/trunk: 1,3-5'
If CONSIDER_INHERITANCE is TRUE, then the inheritability of the
svn_merge_range_t's is also considered and must be the same for two
otherwise identical ranges to be judged equal.
e.g. '/trunk: 1,3-4*,5' != '/trunk: 1,3-5'
'/trunk: 1,3-4*,5' == '/trunk: 1,3-4*,5'
'/trunk: 1,3-4,5' == '/trunk: 1,3-4,5'
Use POOL for temporary allocations. */
svn_error_t *
svn_mergeinfo__equals(svn_boolean_t *is_equal,
svn_mergeinfo_t info1,
svn_mergeinfo_t info2,
svn_boolean_t consider_inheritance,
apr_pool_t *pool);
/* Examine MERGEINFO, removing all paths from the hash which map to
empty rangelists. POOL is used only to allocate the apr_hash_index_t
iterator. Returns TRUE if any paths were removed and FALSE if none were
removed or MERGEINFO is NULL. */
svn_boolean_t
svn_mergeinfo__remove_empty_rangelists(svn_mergeinfo_t mergeinfo,
apr_pool_t *pool);
/* Make a shallow (ie, mergeinfos are not duped, or altered at all;
keys share storage) copy of IN_CATALOG in *OUT_CATALOG, removing
PREFIX_PATH from the beginning of each key in the catalog.
PREFIX_PATH and the keys of IN_CATALOG are absolute 'fspaths',
starting with '/'. It is illegal for any key to not start with
PREFIX_PATH. The keys of *OUT_CATALOG are relpaths. The new hash
and temporary values are allocated in POOL. (This is useful for
making the return value from svn_ra_get_mergeinfo relative to the
session root, say.) */
svn_error_t *
svn_mergeinfo__remove_prefix_from_catalog(svn_mergeinfo_catalog_t *out_catalog,
svn_mergeinfo_catalog_t in_catalog,
const char *prefix_path,
apr_pool_t *pool);
/* Make a shallow (ie, mergeinfos are not duped, or altered at all;
though keys are reallocated) copy of IN_CATALOG in *OUT_CATALOG,
adding PREFIX_PATH to the beginning of each key in the catalog.
The new hash keys are allocated in RESULT_POOL. SCRATCH_POOL
is used for any temporary allocations.*/
svn_error_t *
svn_mergeinfo__add_prefix_to_catalog(svn_mergeinfo_catalog_t *out_catalog,
svn_mergeinfo_catalog_t in_catalog,
const char *prefix_path,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Set *OUT_MERGEINFO to a shallow copy of MERGEINFO with the relpath
SUFFIX_RELPATH added to the end of each key path.
Allocate *OUT_MERGEINFO and the new keys in RESULT_POOL. Use
SCRATCH_POOL for any temporary allocations. */
svn_error_t *
svn_mergeinfo__add_suffix_to_mergeinfo(svn_mergeinfo_t *out_mergeinfo,
svn_mergeinfo_t mergeinfo,
const char *suffix_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Create a string representation of CATALOG in *OUTPUT, allocated in POOL.
The hash keys of CATALOG and the merge source paths of each key's mergeinfo
are represented in sorted order as per svn_sort_compare_items_as_paths.
If CATALOG is empty or NULL then *OUTPUT->DATA is set to "\n". If SVN_DEBUG
is true, then a NULL or empty CATALOG causes *OUTPUT to be set to an
appropriate newline terminated string. If KEY_PREFIX is not NULL then
prepend KEY_PREFIX to each key (path) in *OUTPUT. if VAL_PREFIX is not
NULL then prepend VAL_PREFIX to each merge source:rangelist line in
*OUTPUT.
Any relative merge source paths in the mergeinfo in CATALOG are converted
to absolute paths in *OUTPUT. */
svn_error_t *
svn_mergeinfo__catalog_to_formatted_string(svn_string_t **output,
svn_mergeinfo_catalog_t catalog,
const char *key_prefix,
const char *val_prefix,
apr_pool_t *pool);
/* Set *YOUNGEST_REV and *OLDEST_REV to the youngest and oldest revisions
found in the rangelists within MERGEINFO. Note that *OLDEST_REV is
exclusive and *YOUNGEST_REV is inclusive. If MERGEINFO is NULL or empty
set *YOUNGEST_REV and *OLDEST_REV to SVN_INVALID_REVNUM. */
svn_error_t *
svn_mergeinfo__get_range_endpoints(svn_revnum_t *youngest_rev,
svn_revnum_t *oldest_rev,
svn_mergeinfo_t mergeinfo,
apr_pool_t *pool);
/* Set *FILTERED_MERGEINFO to a deep copy of MERGEINFO, allocated in
RESULT_POOL, less any revision ranges that fall outside of the range
OLDEST_REV:YOUNGEST_REV (exclusive:inclusive) if INCLUDE_RANGE is true,
or less any ranges within OLDEST_REV:YOUNGEST_REV if INCLUDE_RANGE
is false. If all the rangelists mapped to a given path are filtered
then filter that path as well. If all paths are filtered or MERGEINFO is
empty or NULL then *FILTERED_MERGEINFO is set to an empty hash.
Use SCRATCH_POOL for any temporary allocations. */
svn_error_t *
svn_mergeinfo__filter_mergeinfo_by_ranges(svn_mergeinfo_t *filtered_mergeinfo,
svn_mergeinfo_t mergeinfo,
svn_revnum_t youngest_rev,
svn_revnum_t oldest_rev,
svn_boolean_t include_range,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Filter each mergeinfo in CATALOG as per
svn_mergeinfo__filter_mergeinfo_by_ranges() and put a deep copy of the
result in *FILTERED_CATALOG, allocated in RESULT_POOL. If any mergeinfo
is filtered to an empty hash then filter that path/mergeinfo as well.
If all mergeinfo is filtered or CATALOG is NULL then set *FILTERED_CATALOG
to an empty hash.
Use SCRATCH_POOL for any temporary allocations. */
svn_error_t*
svn_mergeinfo__filter_catalog_by_ranges(
svn_mergeinfo_catalog_t *filtered_catalog,
svn_mergeinfo_catalog_t catalog,
svn_revnum_t youngest_rev,
svn_revnum_t oldest_rev,
svn_boolean_t include_range,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* If MERGEINFO is non-inheritable return TRUE, return FALSE otherwise.
MERGEINFO may be NULL or empty. */
svn_boolean_t
svn_mergeinfo__is_noninheritable(svn_mergeinfo_t mergeinfo,
apr_pool_t *scratch_pool);
/* Return a rangelist with one svn_merge_range_t * element defined by START,
END, and INHERITABLE. The rangelist and its contents are allocated in
RESULT_POOL. */
svn_rangelist_t *
svn_rangelist__initialize(svn_revnum_t start,
svn_revnum_t end,
svn_boolean_t inheritable,
apr_pool_t *result_pool);
/* Adjust in-place MERGEINFO's rangelists by OFFSET. If OFFSET is negative
and would adjust any part of MERGEINFO's source revisions to 0 or less,
then those revisions are dropped. If all the source revisions for a merge
source path are dropped, then the path itself is dropped. If all merge
source paths are dropped, then *ADJUSTED_MERGEINFO is set to an empty
hash. *ADJUSTED_MERGEINFO is allocated in RESULT_POOL. SCRATCH_POOL is
used for any temporary allocations. */
svn_error_t *
svn_mergeinfo__adjust_mergeinfo_rangelists(svn_mergeinfo_t *adjusted_mergeinfo,
svn_mergeinfo_t mergeinfo,
svn_revnum_t offset,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Translates an array SEGMENTS (of svn_location_segment_t *), like the one
returned from svn_client__repos_location_segments, into a mergeinfo
*MERGEINFO_P, allocated in POOL.
Note: A svn_location_segment_t segment may legitimately describe only revision 0,
but there is no way to describe that using svn_mergeinfo_t. Any such
segment in SEGMENTS are ignored. */
svn_error_t *
svn_mergeinfo__mergeinfo_from_segments(svn_mergeinfo_t *mergeinfo_p,
const apr_array_header_t *segments,
apr_pool_t *pool);
/* Merge every rangelist in MERGEINFO into the given MERGED_RANGELIST,
* ignoring the source paths of MERGEINFO. MERGED_RANGELIST may
* initially be empty. New elements added to RANGELIST are allocated in
* RESULT_POOL. See svn_rangelist_merge2() for details of inheritability
* etc. */
svn_error_t *
svn_rangelist__merge_many(svn_rangelist_t *merged_rangelist,
svn_mergeinfo_t mergeinfo,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* SVN_MERGEINFO_PRIVATE_H */
Index: vendor/subversion/dist/subversion/include/private/svn_repos_private.h
===================================================================
--- vendor/subversion/dist/subversion/include/private/svn_repos_private.h (revision 286500)
+++ vendor/subversion/dist/subversion/include/private/svn_repos_private.h (revision 286501)
@@ -1,121 +1,125 @@
/**
* @copyright
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
* @endcopyright
*
* @file svn_repos_private.h
* @brief Subversion-internal repos APIs.
*/
#ifndef SVN_REPOS_PRIVATE_H
#define SVN_REPOS_PRIVATE_H
#include <apr_pools.h>
#include "svn_types.h"
#include "svn_repos.h"
#include "svn_editor.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/** Validate that property @a name is valid for use in a Subversion
* repository; return @c SVN_ERR_REPOS_BAD_ARGS if it isn't. For some
* "svn:" properties, also validate the @a value, and return
* @c SVN_ERR_BAD_PROPERTY_VALUE if it is not valid.
*
* Use @a pool for temporary allocations.
*
* @note This function is used to implement server-side validation.
* Consequently, if you make this function stricter in what it accepts, you
* (a) break svnsync'ing of existing repositories that contain now-invalid
* properties, (b) do not preclude such invalid values from entering the
* repository via tools that use the svn_fs_* API directly (possibly
* including svnadmin and svnlook). This has happened before and there
* are known (documented, but unsupported) upgrade paths in some cases.
*
* @since New in 1.7.
*/
svn_error_t *
svn_repos__validate_prop(const char *name,
const svn_string_t *value,
apr_pool_t *pool);
/**
* Given the error @a err from svn_repos_fs_commit_txn(), return an
* string containing either or both of the svn_fs_commit_txn() error
* and the SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED wrapped error from
* the post-commit hook. Any error tracing placeholders in the error
* chain are skipped over.
*
* This function does not modify @a err.
*
* ### This method should not be necessary, but there are a few
* ### places, e.g. mod_dav_svn, where only a single error message
* ### string is returned to the caller and it is useful to have both
* ### error messages included in the message.
*
* Use @a pool to do any allocations in.
*
* @since New in 1.7.
*/
const char *
svn_repos__post_commit_error_str(svn_error_t *err,
apr_pool_t *pool);
/* A repos version of svn_fs_type */
svn_error_t *
svn_repos__fs_type(const char **fs_type,
const char *repos_path,
apr_pool_t *pool);
/* Create a commit editor for REPOS, based on REVISION. */
svn_error_t *
svn_repos__get_commit_ev2(svn_editor_t **editor,
svn_repos_t *repos,
svn_authz_t *authz,
const char *authz_repos_name,
const char *authz_user,
apr_hash_t *revprops,
svn_commit_callback2_t commit_cb,
void *commit_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
svn_error_t *
svn_repos__replay_ev2(svn_fs_root_t *root,
const char *base_dir,
svn_revnum_t low_water_mark,
svn_editor_t *editor,
svn_repos_authz_func_t authz_read_func,
void *authz_read_baton,
apr_pool_t *scratch_pool);
+/* A private addition to svn_repos_notify_warning_t. */
+#define svn_repos__notify_warning_invalid_mergeinfo \
+ ((svn_repos_notify_warning_t)(-1))
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* SVN_REPOS_PRIVATE_H */
Index: vendor/subversion/dist/subversion/include/private/svn_sqlite.h
===================================================================
--- vendor/subversion/dist/subversion/include/private/svn_sqlite.h (revision 286500)
+++ vendor/subversion/dist/subversion/include/private/svn_sqlite.h (revision 286501)
@@ -1,519 +1,545 @@
/* svn_sqlite.h
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#ifndef SVN_SQLITE_H
#define SVN_SQLITE_H
#include <apr_pools.h>
#include "svn_types.h"
#include "svn_checksum.h"
#include "svn_error.h"
#include "private/svn_token.h" /* for svn_token_map_t */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* Because the SQLite code can be inlined into libsvn_subre/sqlite.c,
we define accessors to its compile-time and run-time version
numbers here. */
/* Return the value that SQLITE_VERSION had at compile time. */
const char *svn_sqlite__compiled_version(void);
/* Return the value of sqlite3_libversion() at run time. */
const char *svn_sqlite__runtime_version(void);
typedef struct svn_sqlite__db_t svn_sqlite__db_t;
typedef struct svn_sqlite__stmt_t svn_sqlite__stmt_t;
typedef struct svn_sqlite__context_t svn_sqlite__context_t;
typedef struct svn_sqlite__value_t svn_sqlite__value_t;
typedef enum svn_sqlite__mode_e {
svn_sqlite__mode_readonly, /* open the database read-only */
svn_sqlite__mode_readwrite, /* open the database read-write */
svn_sqlite__mode_rwcreate /* open/create the database read-write */
} svn_sqlite__mode_t;
/* The type used for callback functions. */
typedef svn_error_t *(*svn_sqlite__func_t)(svn_sqlite__context_t *sctx,
int argc,
svn_sqlite__value_t *values[],
apr_pool_t *scatch_pool);
/* Step the given statement; if it returns SQLITE_DONE, reset the statement.
Otherwise, raise an SVN error. */
svn_error_t *
svn_sqlite__step_done(svn_sqlite__stmt_t *stmt);
/* Step the given statement; raise an SVN error (and reset the
statement) if it doesn't return SQLITE_ROW. */
svn_error_t *
svn_sqlite__step_row(svn_sqlite__stmt_t *stmt);
/* Step the given statement; raise an SVN error (and reset the
statement) if it doesn't return SQLITE_DONE or SQLITE_ROW. Set
*GOT_ROW to true iff it got SQLITE_ROW.
*/
svn_error_t *
svn_sqlite__step(svn_boolean_t *got_row, svn_sqlite__stmt_t *stmt);
/* Perform an insert as given by the prepared and bound STMT, and set
*ROW_ID to the id of the inserted row if ROW_ID is non-NULL.
STMT will be reset prior to returning. */
svn_error_t *
svn_sqlite__insert(apr_int64_t *row_id, svn_sqlite__stmt_t *stmt);
/* Perform an update/delete and then return the number of affected rows.
If AFFECTED_ROWS is not NULL, then set *AFFECTED_ROWS to the
number of rows changed.
STMT will be reset prior to returning. */
svn_error_t *
svn_sqlite__update(int *affected_rows, svn_sqlite__stmt_t *stmt);
/* Return in *VERSION the version of the schema in DB. Use SCRATCH_POOL
for temporary allocations. */
svn_error_t *
svn_sqlite__read_schema_version(int *version,
svn_sqlite__db_t *db,
apr_pool_t *scratch_pool);
/* Open a connection in *DB to the database at PATH. Validate the schema,
creating/upgrading to LATEST_SCHEMA if needed using the instructions
in UPGRADE_SQL. The resulting DB is allocated in RESULT_POOL, and any
temporary allocations are made in SCRATCH_POOL.
STATEMENTS is an array of strings which may eventually be executed, the
last element of which should be NULL. These strings and the array itself
are not duplicated internally, and should have a lifetime at least as long
as RESULT_POOL.
STATEMENTS itself may be NULL, in which case it has no impact.
See svn_sqlite__get_statement() for how these strings are used.
The statements will be finalized and the SQLite database will be closed
when RESULT_POOL is cleaned up. */
svn_error_t *
svn_sqlite__open(svn_sqlite__db_t **db, const char *repos_path,
svn_sqlite__mode_t mode, const char * const statements[],
int latest_schema, const char * const *upgrade_sql,
apr_pool_t *result_pool, apr_pool_t *scratch_pool);
/* Explicitly close the connection in DB. */
svn_error_t *
svn_sqlite__close(svn_sqlite__db_t *db);
/* Add a custom function to be used with this database connection. The data
in BATON should live at least as long as the connection in DB. */
svn_error_t *
svn_sqlite__create_scalar_function(svn_sqlite__db_t *db,
const char *func_name,
int argc,
svn_sqlite__func_t func,
void *baton);
/* Execute the (multiple) statements in the STATEMENTS[STMT_IDX] string. */
svn_error_t *
svn_sqlite__exec_statements(svn_sqlite__db_t *db, int stmt_idx);
/* Return the statement in *STMT which has been prepared from the
STATEMENTS[STMT_IDX] string, where STATEMENTS is the array that was
passed to svn_sqlite__open(). This statement is allocated in the same
pool as the DB, and will be cleaned up when DB is closed. */
svn_error_t *
svn_sqlite__get_statement(svn_sqlite__stmt_t **stmt, svn_sqlite__db_t *db,
int stmt_idx);
/* ---------------------------------------------------------------------
BINDING VALUES
*/
/* Bind values to SQL parameters in STMT, according to FMT. FMT may contain:
Spec Argument type Item type
---- ----------------- ---------
n <none, absent> Column assignment skip
d int Number
L apr_int64_t Number
i apr_int64_t Number (deprecated format spec)
s const char * String
b const void * Blob data
apr_size_t Blob length
r svn_revnum_t Revision number
t const svn_token_map_t * Token mapping table
int Token value
Each character in FMT maps to one SQL parameter, and one or two function
parameters, in the order they appear.
*/
svn_error_t *
svn_sqlite__bindf(svn_sqlite__stmt_t *stmt, const char *fmt, ...);
/* Error-handling wrapper around sqlite3_bind_int. */
svn_error_t *
svn_sqlite__bind_int(svn_sqlite__stmt_t *stmt, int slot, int val);
/* Error-handling wrapper around sqlite3_bind_int64. */
svn_error_t *
svn_sqlite__bind_int64(svn_sqlite__stmt_t *stmt, int slot,
apr_int64_t val);
/* Error-handling wrapper around sqlite3_bind_text. VAL cannot contain
zero bytes; we always pass SQLITE_TRANSIENT. */
svn_error_t *
svn_sqlite__bind_text(svn_sqlite__stmt_t *stmt, int slot,
const char *val);
/* Error-handling wrapper around sqlite3_bind_blob. */
svn_error_t *
svn_sqlite__bind_blob(svn_sqlite__stmt_t *stmt,
int slot,
const void *val,
apr_size_t len);
/* Look up VALUE in MAP, and bind the resulting token word at SLOT. */
svn_error_t *
svn_sqlite__bind_token(svn_sqlite__stmt_t *stmt,
int slot,
const svn_token_map_t *map,
int value);
/* Bind the value to SLOT, unless SVN_IS_VALID_REVNUM(value) is false,
in which case it binds NULL. */
svn_error_t *
svn_sqlite__bind_revnum(svn_sqlite__stmt_t *stmt, int slot,
svn_revnum_t value);
/* Bind a set of properties to the given slot. If PROPS is NULL, then no
binding will occur. PROPS will be stored as a serialized skel. */
svn_error_t *
svn_sqlite__bind_properties(svn_sqlite__stmt_t *stmt,
int slot,
const apr_hash_t *props,
apr_pool_t *scratch_pool);
/* Bind a set of inherited properties to the given slot. If INHERITED_PROPS
is NULL, then no binding will occur. INHERITED_PROPS will be stored as a
serialized skel. */
svn_error_t *
svn_sqlite__bind_iprops(svn_sqlite__stmt_t *stmt,
int slot,
const apr_array_header_t *inherited_props,
apr_pool_t *scratch_pool);
/* Bind a checksum's value to the given slot. If CHECKSUM is NULL, then no
binding will occur. */
svn_error_t *
svn_sqlite__bind_checksum(svn_sqlite__stmt_t *stmt,
int slot,
const svn_checksum_t *checksum,
apr_pool_t *scratch_pool);
/* ---------------------------------------------------------------------
FETCHING VALUES
*/
/* Wrapper around sqlite3_column_blob and sqlite3_column_bytes. The return
value will be NULL if the column is null.
If RESULT_POOL is not NULL, allocate the return value (if any) in it.
If RESULT_POOL is NULL, the return value will be valid until an
invocation of svn_sqlite__column_* performs a data type conversion (as
described in the SQLite documentation) or the statement is stepped or
reset or finalized. */
const void *
svn_sqlite__column_blob(svn_sqlite__stmt_t *stmt, int column,
apr_size_t *len, apr_pool_t *result_pool);
/* Wrapper around sqlite3_column_text. If the column is null, then the
return value will be NULL.
If RESULT_POOL is not NULL, allocate the return value (if any) in it.
If RESULT_POOL is NULL, the return value will be valid until an
invocation of svn_sqlite__column_* performs a data type conversion (as
described in the SQLite documentation) or the statement is stepped or
reset or finalized. */
const char *
svn_sqlite__column_text(svn_sqlite__stmt_t *stmt, int column,
apr_pool_t *result_pool);
/* Wrapper around sqlite3_column_int64. If the column is null, then the
return value will be SVN_INVALID_REVNUM. */
svn_revnum_t
svn_sqlite__column_revnum(svn_sqlite__stmt_t *stmt, int column);
/* Wrapper around sqlite3_column_int64. If the column is null, then the
return value will be FALSE. */
svn_boolean_t
svn_sqlite__column_boolean(svn_sqlite__stmt_t *stmt, int column);
/* Wrapper around sqlite3_column_int. If the column is null, then the
return value will be 0. */
int
svn_sqlite__column_int(svn_sqlite__stmt_t *stmt, int column);
/* Wrapper around sqlite3_column_int64. If the column is null, then the
return value will be 0. */
apr_int64_t
svn_sqlite__column_int64(svn_sqlite__stmt_t *stmt, int column);
/* Fetch the word at COLUMN, look it up in the MAP, and return its value.
MALFUNCTION is thrown if the column is null or contains an unknown word. */
int
svn_sqlite__column_token(svn_sqlite__stmt_t *stmt,
int column,
const svn_token_map_t *map);
/* Fetch the word at COLUMN, look it up in the MAP, and return its value.
Returns NULL_VAL if the column is null. MALFUNCTION is thrown if the
column contains an unknown word. */
int
svn_sqlite__column_token_null(svn_sqlite__stmt_t *stmt,
int column,
const svn_token_map_t *map,
int null_val);
/* Return the column as a hash of const char * => const svn_string_t *.
If the column is null, then set *PROPS to NULL. The
results will be allocated in RESULT_POOL, and any temporary allocations
will be made in SCRATCH_POOL. */
svn_error_t *
svn_sqlite__column_properties(apr_hash_t **props,
svn_sqlite__stmt_t *stmt,
int column,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Return the column as an array of depth-first ordered array of
svn_prop_inherited_item_t * structures. If the column is null, then
set *IPROPS to NULL. The results will be allocated in RESULT_POOL,
and any temporary allocations will be made in SCRATCH_POOL. */
svn_error_t *
svn_sqlite__column_iprops(apr_array_header_t **iprops,
svn_sqlite__stmt_t *stmt,
int column,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Return the column as a checksum. If the column is null, then NULL will
be stored into *CHECKSUM. The result will be allocated in RESULT_POOL. */
svn_error_t *
svn_sqlite__column_checksum(const svn_checksum_t **checksum,
svn_sqlite__stmt_t *stmt,
int column,
apr_pool_t *result_pool);
/* Return TRUE if the result of selecting the column is null,
FALSE otherwise */
svn_boolean_t
svn_sqlite__column_is_null(svn_sqlite__stmt_t *stmt, int column);
/* Return the number of bytes the column uses in a text or blob representation.
0 for NULL columns. */
int
svn_sqlite__column_bytes(svn_sqlite__stmt_t *stmt, int column);
/* --------------------------------------------------------------------- */
#define SVN_SQLITE__INTEGER 1
#define SVN_SQLITE__FLOAT 2
#define SVN_SQLITE__TEXT 3
#define SVN_SQLITE__BLOB 4
#define SVN_SQLITE__NULL 5
/* */
int
svn_sqlite__value_type(svn_sqlite__value_t *val);
/* */
const char *
svn_sqlite__value_text(svn_sqlite__value_t *val);
/* --------------------------------------------------------------------- */
/* */
void
svn_sqlite__result_null(svn_sqlite__context_t *sctx);
void
svn_sqlite__result_int64(svn_sqlite__context_t *sctx, apr_int64_t val);
/* --------------------------------------------------------------------- */
/* Error-handling wrapper around sqlite3_finalize. */
svn_error_t *
svn_sqlite__finalize(svn_sqlite__stmt_t *stmt);
/* Reset STMT by calling sqlite3_reset(), and also clear any bindings to it.
Note: svn_sqlite__get_statement() calls this function automatically if
the requested statement has been used and has not yet been reset. */
svn_error_t *
svn_sqlite__reset(svn_sqlite__stmt_t *stmt);
/* Begin a transaction in DB. */
svn_error_t *
svn_sqlite__begin_transaction(svn_sqlite__db_t *db);
/* Like svn_sqlite__begin_transaction(), but takes out a 'RESERVED' lock
immediately, instead of using the default deferred locking scheme. */
svn_error_t *
svn_sqlite__begin_immediate_transaction(svn_sqlite__db_t *db);
/* Begin a savepoint in DB. */
svn_error_t *
svn_sqlite__begin_savepoint(svn_sqlite__db_t *db);
/* Commit the current transaction in DB if ERR is SVN_NO_ERROR, otherwise
* roll back the transaction. Return a composition of ERR and any error
* that may occur during the commit or roll-back. */
svn_error_t *
svn_sqlite__finish_transaction(svn_sqlite__db_t *db,
svn_error_t *err);
/* Release the current savepoint in DB if EXPR is SVN_NO_ERROR, otherwise
* roll back to the savepoint and then release it. Return a composition of
* ERR and any error that may occur during the release or roll-back. */
svn_error_t *
svn_sqlite__finish_savepoint(svn_sqlite__db_t *db,
svn_error_t *err);
/* Evaluate the expression EXPR within a transaction.
*
* Begin a transaction in DB; evaluate the expression EXPR, which would
* typically be a function call that does some work in DB; finally commit
* the transaction if EXPR evaluated to SVN_NO_ERROR, otherwise roll back
* the transaction.
*/
#define SVN_SQLITE__WITH_TXN(expr, db) \
do { \
svn_sqlite__db_t *svn_sqlite__db = (db); \
svn_error_t *svn_sqlite__err; \
\
SVN_ERR(svn_sqlite__begin_transaction(svn_sqlite__db)); \
svn_sqlite__err = (expr); \
SVN_ERR(svn_sqlite__finish_transaction(svn_sqlite__db, svn_sqlite__err)); \
} while (0)
/* Callback function to for use with svn_sqlite__with_transaction(). */
typedef svn_error_t *(*svn_sqlite__transaction_callback_t)(
void *baton, svn_sqlite__db_t *db, apr_pool_t *scratch_pool);
/* Helper function to handle SQLite transactions. All the work done inside
CB_FUNC will be wrapped in an SQLite transaction, which will be committed
if CB_FUNC does not return an error. If any error is returned from CB_FUNC,
the transaction will be rolled back. DB and CB_BATON will be passed to
CB_FUNC. SCRATCH_POOL will be passed to the callback (NULL is valid). */
svn_error_t *
svn_sqlite__with_transaction(svn_sqlite__db_t *db,
svn_sqlite__transaction_callback_t cb_func,
void *cb_baton, apr_pool_t *scratch_pool);
/* Like SVN_SQLITE__WITH_TXN(), but takes out a 'RESERVED' lock
immediately, instead of using the default deferred locking scheme. */
#define SVN_SQLITE__WITH_IMMEDIATE_TXN(expr, db) \
do { \
svn_sqlite__db_t *svn_sqlite__db = (db); \
svn_error_t *svn_sqlite__err; \
\
SVN_ERR(svn_sqlite__begin_immediate_transaction(svn_sqlite__db)); \
svn_sqlite__err = (expr); \
SVN_ERR(svn_sqlite__finish_transaction(svn_sqlite__db, svn_sqlite__err)); \
} while (0)
/* Like svn_sqlite__with_transaction(), but takes out a 'RESERVED' lock
immediately, instead of using the default deferred locking scheme. */
svn_error_t *
svn_sqlite__with_immediate_transaction(svn_sqlite__db_t *db,
svn_sqlite__transaction_callback_t cb_func,
void *cb_baton,
apr_pool_t *scratch_pool);
/* Evaluate the expression EXPR within a 'savepoint'. Savepoints can be
* nested.
*
* Begin a savepoint in DB; evaluate the expression EXPR, which would
* typically be a function call that does some work in DB; finally release
* the savepoint if EXPR evaluated to SVN_NO_ERROR, otherwise roll back
* to the savepoint and then release it.
*/
#define SVN_SQLITE__WITH_LOCK(expr, db) \
do { \
svn_sqlite__db_t *svn_sqlite__db = (db); \
svn_error_t *svn_sqlite__err; \
\
SVN_ERR(svn_sqlite__begin_savepoint(svn_sqlite__db)); \
svn_sqlite__err = (expr); \
SVN_ERR(svn_sqlite__finish_savepoint(svn_sqlite__db, svn_sqlite__err)); \
} while (0)
+/* Evaluate the expression EXPR1..EXPR4 within a 'savepoint'. Savepoints can
+ * be nested.
+ *
+ * Begin a savepoint in DB; evaluate the expression EXPR1, which would
+ * typically be a function call that does some work in DB; if no error occurred,
+ * run EXPR2; if no error occurred EXPR3; ... and finally release
+ * the savepoint if EXPR evaluated to SVN_NO_ERROR, otherwise roll back
+ * to the savepoint and then release it.
+ */
+#define SVN_SQLITE__WITH_LOCK4(expr1, expr2, expr3, expr4, db) \
+ do { \
+ svn_sqlite__db_t *svn_sqlite__db = (db); \
+ svn_error_t *svn_sqlite__err; \
+ \
+ SVN_ERR(svn_sqlite__begin_savepoint(svn_sqlite__db)); \
+ svn_sqlite__err = (expr1); \
+ if (!svn_sqlite__err) \
+ svn_sqlite__err = (expr2); \
+ if (!svn_sqlite__err) \
+ svn_sqlite__err = (expr3); \
+ if (!svn_sqlite__err) \
+ svn_sqlite__err = (expr4); \
+ SVN_ERR(svn_sqlite__finish_savepoint(svn_sqlite__db, svn_sqlite__err)); \
+ } while (0)
+
+
/* Helper function to handle several SQLite operations inside a shared lock.
This callback is similar to svn_sqlite__with_transaction(), but can be
nested (even with a transaction).
Using this function as a wrapper around a group of operations can give a
*huge* performance boost as the shared-read lock will be shared over
multiple statements, instead of being reobtained every time, which may
require disk and/or network io, depending on SQLite's locking strategy.
SCRATCH_POOL will be passed to the callback (NULL is valid).
### Since we now require SQLite >= 3.6.18, this function has the effect of
always behaving like a defered transaction. Can it be combined with
svn_sqlite__with_transaction()?
*/
svn_error_t *
svn_sqlite__with_lock(svn_sqlite__db_t *db,
svn_sqlite__transaction_callback_t cb_func,
void *cb_baton,
apr_pool_t *scratch_pool);
/* Hotcopy an SQLite database from SRC_PATH to DST_PATH. */
svn_error_t *
svn_sqlite__hotcopy(const char *src_path,
const char *dst_path,
apr_pool_t *scratch_pool);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* SVN_SQLITE_H */
Index: vendor/subversion/dist/subversion/include/svn_io.h
===================================================================
--- vendor/subversion/dist/subversion/include/svn_io.h (revision 286500)
+++ vendor/subversion/dist/subversion/include/svn_io.h (revision 286501)
@@ -1,2285 +1,2287 @@
/**
* @copyright
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
* @endcopyright
*
* @file svn_io.h
* @brief General file I/O for Subversion
*/
/* ==================================================================== */
#ifndef SVN_IO_H
#define SVN_IO_H
#include <apr.h>
#include <apr_pools.h>
#include <apr_time.h>
#include <apr_hash.h>
#include <apr_tables.h>
#include <apr_file_io.h>
#include <apr_file_info.h>
#include <apr_thread_proc.h> /* for apr_proc_t, apr_exit_why_e */
#include "svn_types.h"
#include "svn_string.h"
#include "svn_checksum.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/** Used as an argument when creating temporary files to indicate
* when a file should be removed.
*
* @since New in 1.4.
*
* Not specifying any of these means no removal at all. */
typedef enum svn_io_file_del_t
{
/** No deletion ever */
svn_io_file_del_none = 0,
/** Remove when the file is closed */
svn_io_file_del_on_close,
/** Remove when the associated pool is cleared */
svn_io_file_del_on_pool_cleanup
} svn_io_file_del_t;
/** A set of directory entry data elements as returned by svn_io_get_dirents
*
* Note that the first two fields are exactly identical to svn_io_dirent_t
* to allow returning a svn_io_dirent2_t as a svn_io_dirent_t.
*
* Use svn_io_dirent2_create() to create new svn_dirent2_t instances or
* svn_io_dirent2_dup() to duplicate an existing instance.
*
* @since New in 1.7.
*/
typedef struct svn_io_dirent2_t {
/* New fields must be added at the end to preserve binary compatibility */
/** The kind of this entry. */
svn_node_kind_t kind;
/** If @c kind is #svn_node_file, whether this entry is a special file;
* else FALSE.
*
* @see svn_io_check_special_path().
*/
svn_boolean_t special;
/** The filesize of this entry or undefined for a directory */
svn_filesize_t filesize;
/** The time the file was last modified */
apr_time_t mtime;
/* Don't forget to update svn_io_dirent2_dup() when adding new fields */
} svn_io_dirent2_t;
/** Creates a new #svn_io_dirent2_t structure
*
* @since New in 1.7.
*/
svn_io_dirent2_t *
svn_io_dirent2_create(apr_pool_t *result_pool);
/** Duplicates a @c svn_io_dirent2_t structure into @a result_pool.
*
* @since New in 1.7.
*/
svn_io_dirent2_t *
svn_io_dirent2_dup(const svn_io_dirent2_t *item,
apr_pool_t *result_pool);
/** Represents the kind and special status of a directory entry.
*
* Note that the first two fields are exactly identical to svn_io_dirent2_t
* to allow returning a svn_io_dirent2_t as a svn_io_dirent_t.
*
* @since New in 1.3.
*/
typedef struct svn_io_dirent_t {
/** The kind of this entry. */
svn_node_kind_t kind;
/** If @c kind is #svn_node_file, whether this entry is a special file;
* else FALSE.
*
* @see svn_io_check_special_path().
*/
svn_boolean_t special;
} svn_io_dirent_t;
/** Determine the @a kind of @a path. @a path should be UTF-8 encoded.
*
* If @a path is a file, set @a *kind to #svn_node_file.
*
* If @a path is a directory, set @a *kind to #svn_node_dir.
*
* If @a path does not exist, set @a *kind to #svn_node_none.
*
* If @a path exists but is none of the above, set @a *kind to
* #svn_node_unknown.
*
* If @a path is not a valid pathname, set @a *kind to #svn_node_none. If
* unable to determine @a path's kind for any other reason, return an error,
* with @a *kind's value undefined.
*
* Use @a pool for temporary allocations.
*
* @see svn_node_kind_t
*/
svn_error_t *
svn_io_check_path(const char *path,
svn_node_kind_t *kind,
apr_pool_t *pool);
/**
* Like svn_io_check_path(), but also set *is_special to @c TRUE if
* the path is not a normal file.
*
* @since New in 1.1.
*/
svn_error_t *
svn_io_check_special_path(const char *path,
svn_node_kind_t *kind,
svn_boolean_t *is_special,
apr_pool_t *pool);
/** Like svn_io_check_path(), but resolve symlinks. This returns the
same varieties of @a kind as svn_io_check_path(). */
svn_error_t *
svn_io_check_resolved_path(const char *path,
svn_node_kind_t *kind,
apr_pool_t *pool);
/** Open a new file (for reading and writing) with a unique name based on
* utf-8 encoded @a filename, in the directory @a dirpath. The file handle is
* returned in @a *file, and the name, which ends with @a suffix, is returned
* in @a *unique_name, also utf8-encoded. Either @a file or @a unique_name
* may be @c NULL. If @a file is @c NULL, the file will be created but not
* open.
*
* The file will be deleted according to @a delete_when. If that is
* #svn_io_file_del_on_pool_cleanup, it refers to @a result_pool.
*
* The @c APR_BUFFERED flag will always be used when opening the file.
*
* The first attempt will just append @a suffix. If the result is not
* a unique name, then subsequent attempts will append a dot,
* followed by an iteration number ("2", then "3", and so on),
* followed by the suffix. For example, successive calls to
*
* svn_io_open_uniquely_named(&f, &u, "tests/t1/A/D/G", "pi", ".tmp", ...)
*
* will open
*
* tests/t1/A/D/G/pi.tmp
* tests/t1/A/D/G/pi.2.tmp
* tests/t1/A/D/G/pi.3.tmp
* tests/t1/A/D/G/pi.4.tmp
* tests/t1/A/D/G/pi.5.tmp
* ...
*
* Assuming @a suffix is non-empty, @a *unique_name will never be exactly
* the same as @a filename, even if @a filename does not exist.
*
* If @a dirpath is NULL, then the directory returned by svn_io_temp_dir()
* will be used.
*
* If @a filename is NULL, then "tempfile" will be used.
*
* If @a suffix is NULL, then ".tmp" will be used.
*
* Allocates @a *file and @a *unique_name in @a result_pool. All
* intermediate allocations will be performed in @a scratch_pool.
*
* If no unique name can be found, #SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED is
* the error returned.
*
* Claim of Historical Inevitability: this function was written
* because
*
* - tmpnam() is not thread-safe.
* - tempname() tries standard system tmp areas first.
*
* @since New in 1.6
*/
svn_error_t *
svn_io_open_uniquely_named(apr_file_t **file,
const char **unique_name,
const char *dirpath,
const char *filename,
const char *suffix,
svn_io_file_del_t delete_when,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/** Create a writable file, with an arbitrary and unique name, in the
* directory @a dirpath. Set @a *temp_path to its full path, and set
* @a *file to the file handle, both allocated from @a result_pool. Either
* @a file or @a temp_path may be @c NULL. If @a file is @c NULL, the file
* will be created but not open.
*
* If @a dirpath is @c NULL, use the path returned from svn_io_temp_dir().
* (Note that when using the system-provided temp directory, it may not
* be possible to atomically rename the resulting file due to cross-device
* issues.)
*
* The file will be deleted according to @a delete_when. If that is
* #svn_io_file_del_on_pool_cleanup, it refers to @a result_pool. If it
* is #svn_io_file_del_on_close and @a file is @c NULL, the file will be
* deleted before this function returns.
*
* When passing @c svn_io_file_del_none please don't forget to eventually
* remove the temporary file to avoid filling up the system temp directory.
* It is often appropriate to bind the lifetime of the temporary file to
* the lifetime of a pool by using @c svn_io_file_del_on_pool_cleanup.
*
* Temporary allocations will be performed in @a scratch_pool.
*
* @since New in 1.6
* @see svn_stream_open_unique()
*/
svn_error_t *
svn_io_open_unique_file3(apr_file_t **file,
const char **temp_path,
const char *dirpath,
svn_io_file_del_t delete_when,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/** Like svn_io_open_uniquely_named(), but takes a joined dirpath and
* filename, and a single pool.
*
* @since New in 1.4
*
* @deprecated Provided for backward compatibility with the 1.5 API
*/
SVN_DEPRECATED
svn_error_t *
svn_io_open_unique_file2(apr_file_t **f,
const char **unique_name_p,
const char *path,
const char *suffix,
svn_io_file_del_t delete_when,
apr_pool_t *pool);
/** Like svn_io_open_unique_file2, but can't delete on pool cleanup.
*
* @deprecated Provided for backward compatibility with the 1.3 API
*
* @note In 1.4 the API was extended to require either @a f or
* @a unique_name_p (the other can be NULL). Before that, both were
* required.
*/
SVN_DEPRECATED
svn_error_t *
svn_io_open_unique_file(apr_file_t **f,
const char **unique_name_p,
const char *path,
const char *suffix,
svn_boolean_t delete_on_close,
apr_pool_t *pool);
/**
* Like svn_io_open_unique_file(), except that instead of creating a
* file, a symlink is generated that references the path @a dest.
*
* @since New in 1.1.
*/
svn_error_t *
svn_io_create_unique_link(const char **unique_name_p,
const char *path,
const char *dest,
const char *suffix,
apr_pool_t *pool);
/**
* Set @a *dest to the path that the symlink at @a path references.
* Allocate the string from @a pool.
*
* @since New in 1.1.
*/
svn_error_t *
svn_io_read_link(svn_string_t **dest,
const char *path,
apr_pool_t *pool);
/** Set @a *dir to a directory path (allocated in @a pool) deemed
* usable for the creation of temporary files and subdirectories.
*/
svn_error_t *
svn_io_temp_dir(const char **dir,
apr_pool_t *pool);
/** Copy @a src to @a dst atomically, in a "byte-for-byte" manner.
* Overwrite @a dst if it exists, else create it. Both @a src and @a dst
* are utf8-encoded filenames. If @a copy_perms is TRUE, set @a dst's
* permissions to match those of @a src.
*/
svn_error_t *
svn_io_copy_file(const char *src,
const char *dst,
svn_boolean_t copy_perms,
apr_pool_t *pool);
/** Copy permission flags from @a src onto the file at @a dst. Both
* filenames are utf8-encoded filenames.
*
* @since New in 1.6.
*/
svn_error_t *
svn_io_copy_perms(const char *src,
const char *dst,
apr_pool_t *pool);
/**
* Copy symbolic link @a src to @a dst atomically. Overwrite @a dst
* if it exists, else create it. Both @a src and @a dst are
* utf8-encoded filenames. After copying, the @a dst link will point
* to the same thing @a src does.
*
* @since New in 1.1.
*/
svn_error_t *
svn_io_copy_link(const char *src,
const char *dst,
apr_pool_t *pool);
/** Recursively copy directory @a src into @a dst_parent, as a new entry named
* @a dst_basename. If @a dst_basename already exists in @a dst_parent,
* return error. @a copy_perms will be passed through to svn_io_copy_file()
* when any files are copied. @a src, @a dst_parent, and @a dst_basename are
* all utf8-encoded.
*
* If @a cancel_func is non-NULL, invoke it with @a cancel_baton at
* various points during the operation. If it returns any error
* (typically #SVN_ERR_CANCELLED), return that error immediately.
*/
svn_error_t *
svn_io_copy_dir_recursively(const char *src,
const char *dst_parent,
const char *dst_basename,
svn_boolean_t copy_perms,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool);
/** Create directory @a path on the file system, creating intermediate
* directories as required, like <tt>mkdir -p</tt>. Report no error if @a
* path already exists. @a path is utf8-encoded.
*
* This is essentially a wrapper for apr_dir_make_recursive(), passing
* @c APR_OS_DEFAULT as the permissions.
*/
svn_error_t *
svn_io_make_dir_recursively(const char *path,
apr_pool_t *pool);
/** Set @a *is_empty_p to @c TRUE if directory @a path is empty, else to
* @c FALSE if it is not empty. @a path must be a directory, and is
* utf8-encoded. Use @a pool for temporary allocation.
*/
svn_error_t *
svn_io_dir_empty(svn_boolean_t *is_empty_p,
const char *path,
apr_pool_t *pool);
/** Append @a src to @a dst. @a dst will be appended to if it exists, else it
* will be created. Both @a src and @a dst are utf8-encoded.
*/
svn_error_t *
svn_io_append_file(const char *src,
const char *dst,
apr_pool_t *pool);
/** Make a file as read-only as the operating system allows.
* @a path is the utf8-encoded path to the file. If @a ignore_enoent is
* @c TRUE, don't fail if the target file doesn't exist.
*
* If @a path is a symlink, do nothing.
*
* @note If @a path is a directory, act on it as though it were a
* file, as described above, but note that you probably don't want to
* call this function on directories. We have left it effective on
* directories for compatibility reasons, but as its name implies, it
* should be used only for files.
*/
svn_error_t *
svn_io_set_file_read_only(const char *path,
svn_boolean_t ignore_enoent,
apr_pool_t *pool);
/** Make a file as writable as the operating system allows.
* @a path is the utf8-encoded path to the file. If @a ignore_enoent is
* @c TRUE, don't fail if the target file doesn't exist.
* @warning On Unix this function will do the equivalent of chmod a+w path.
* If this is not what you want you should not use this function, but rather
* use apr_file_perms_set().
*
* If @a path is a symlink, do nothing.
*
* @note If @a path is a directory, act on it as though it were a
* file, as described above, but note that you probably don't want to
* call this function on directories. We have left it effective on
* directories for compatibility reasons, but as its name implies, it
* should be used only for files.
*/
svn_error_t *
svn_io_set_file_read_write(const char *path,
svn_boolean_t ignore_enoent,
apr_pool_t *pool);
/** Similar to svn_io_set_file_read_* functions.
* Change the read-write permissions of a file.
* @since New in 1.1.
*
* When making @a path read-write on operating systems with unix style
* permissions, set the permissions on @a path to the permissions that
* are set when a new file is created (effectively honoring the user's
* umask).
*
* When making the file read-only on operating systems with unix style
* permissions, remove all write permissions.
*
* On other operating systems, toggle the file's "writability" as much as
* the operating system allows.
*
* @a path is the utf8-encoded path to the file. If @a enable_write
* is @c TRUE, then make the file read-write. If @c FALSE, make it
* read-only. If @a ignore_enoent is @c TRUE, don't fail if the target
* file doesn't exist.
*
* @deprecated Provided for backward compatibility with the 1.3 API.
*/
SVN_DEPRECATED
svn_error_t *
svn_io_set_file_read_write_carefully(const char *path,
svn_boolean_t enable_write,
svn_boolean_t ignore_enoent,
apr_pool_t *pool);
/** Set @a path's "executability" (but do nothing if it is a symlink).
*
* @a path is the utf8-encoded path to the file. If @a executable
* is @c TRUE, then make the file executable. If @c FALSE, make it
* non-executable. If @a ignore_enoent is @c TRUE, don't fail if the target
* file doesn't exist.
*
* When making the file executable on operating systems with unix style
* permissions, never add an execute permission where there is not
* already a read permission: that is, only make the file executable
* for the user, group or world if the corresponding read permission
* is already set for user, group or world.
*
* When making the file non-executable on operating systems with unix style
* permissions, remove all execute permissions.
*
* On other operating systems, toggle the file's "executability" as much as
* the operating system allows.
*
* @note If @a path is a directory, act on it as though it were a
* file, as described above, but note that you probably don't want to
* call this function on directories. We have left it effective on
* directories for compatibility reasons, but as its name implies, it
* should be used only for files.
*/
svn_error_t *
svn_io_set_file_executable(const char *path,
svn_boolean_t executable,
svn_boolean_t ignore_enoent,
apr_pool_t *pool);
/** Determine whether a file is executable by the current user.
* Set @a *executable to @c TRUE if the file @a path is executable by the
* current user, otherwise set it to @c FALSE.
*
* On Windows and on platforms without userids, always returns @c FALSE.
*/
svn_error_t *
svn_io_is_file_executable(svn_boolean_t *executable,
const char *path,
apr_pool_t *pool);
/** Read a line from @a file into @a buf, but not exceeding @a *limit bytes.
* Does not include newline, instead '\\0' is put there.
* Length (as in strlen) is returned in @a *limit.
* @a buf should be pre-allocated.
* @a file should be already opened.
*
* When the file is out of lines, @c APR_EOF will be returned.
*/
svn_error_t *
svn_io_read_length_line(apr_file_t *file,
char *buf,
apr_size_t *limit,
apr_pool_t *pool);
/** Set @a *apr_time to the time of last modification of the contents of the
* file @a path. @a path is utf8-encoded.
*
* @note This is the APR mtime which corresponds to the traditional mtime
* on Unix, and the last write time on Windows.
*/
svn_error_t *
svn_io_file_affected_time(apr_time_t *apr_time,
const char *path,
apr_pool_t *pool);
/** Set the timestamp of file @a path to @a apr_time. @a path is
* utf8-encoded.
*
* @note This is the APR mtime which corresponds to the traditional mtime
* on Unix, and the last write time on Windows.
*/
svn_error_t *
svn_io_set_file_affected_time(apr_time_t apr_time,
const char *path,
apr_pool_t *pool);
/** Sleep to ensure that any files modified after we exit have a different
* timestamp than the one we recorded. If @a path is not NULL, check if we
* can determine how long we should wait for a new timestamp on the filesystem
* containing @a path, an existing file or directory. If @a path is NULL or we
* can't determine the timestamp resolution, sleep until the next second.
*
* Use @a pool for any necessary allocations. @a pool can be null if @a path
* is NULL.
*
* Errors while retrieving the timestamp resolution will result in sleeping
* to the next second, to keep the working copy stable in error conditions.
*
* @since New in 1.6.
*/
void
svn_io_sleep_for_timestamps(const char *path, apr_pool_t *pool);
/** Set @a *different_p to TRUE if @a file1 and @a file2 have different
* sizes, else set to FALSE. Both @a file1 and @a file2 are utf8-encoded.
*
* Setting @a *different_p to zero does not mean the files definitely
* have the same size, it merely means that the sizes are not
* definitely different. That is, if the size of one or both files
* cannot be determined, then the sizes are not known to be different,
* so @a *different_p is set to FALSE.
*/
svn_error_t *
svn_io_filesizes_different_p(svn_boolean_t *different_p,
const char *file1,
const char *file2,
apr_pool_t *pool);
/** Set @a *different_p12 to non-zero if @a file1 and @a file2 have different
* sizes, else set to zero. Do the similar for @a *different_p23 with
* @a file2 and @a file3, and @a *different_p13 for @a file1 and @a file3.
* The filenames @a file1, @a file2 and @a file3 are utf8-encoded.
*
* Setting @a *different_p12 to zero does not mean the files definitely
* have the same size, it merely means that the sizes are not
* definitely different. That is, if the size of one or both files
* cannot be determined (due to stat() returning an error), then the sizes
* are not known to be different, so @a *different_p12 is set to 0.
*
* @since New in 1.8.
*/
svn_error_t *
svn_io_filesizes_three_different_p(svn_boolean_t *different_p12,
svn_boolean_t *different_p23,
svn_boolean_t *different_p13,
const char *file1,
const char *file2,
const char *file3,
apr_pool_t *scratch_pool);
/** Return in @a *checksum the checksum of type @a kind of @a file
* Use @a pool for temporary allocations and to allocate @a *checksum.
*
* @since New in 1.6.
*/
svn_error_t *
svn_io_file_checksum2(svn_checksum_t **checksum,
const char *file,
svn_checksum_kind_t kind,
apr_pool_t *pool);
/** Put the md5 checksum of @a file into @a digest.
* @a digest points to @c APR_MD5_DIGESTSIZE bytes of storage.
* Use @a pool only for temporary allocations.
*
* @deprecated Provided for backward compatibility with the 1.5 API.
*/
SVN_DEPRECATED
svn_error_t *
svn_io_file_checksum(unsigned char digest[],
const char *file,
apr_pool_t *pool);
/** Set @a *same to TRUE if @a file1 and @a file2 have the same
* contents, else set it to FALSE. Use @a pool for temporary allocations.
*/
svn_error_t *
svn_io_files_contents_same_p(svn_boolean_t *same,
const char *file1,
const char *file2,
apr_pool_t *pool);
/** Set @a *same12 to TRUE if @a file1 and @a file2 have the same
* contents, else set it to FALSE. Do the similar for @a *same23
* with @a file2 and @a file3, and @a *same13 for @a file1 and @a
* file3. The filenames @a file1, @a file2 and @a file3 are
* utf8-encoded. Use @a scratch_pool for temporary allocations.
*
* @since New in 1.8.
*/
svn_error_t *
svn_io_files_contents_three_same_p(svn_boolean_t *same12,
svn_boolean_t *same23,
svn_boolean_t *same13,
const char *file1,
const char *file2,
const char *file3,
apr_pool_t *scratch_pool);
/** Create file at utf8-encoded @a file with contents @a contents.
* @a file must not already exist.
* Use @a pool for memory allocations.
*/
svn_error_t *
svn_io_file_create(const char *file,
const char *contents,
apr_pool_t *pool);
/**
* Lock file at @a lock_file. If @a exclusive is TRUE,
* obtain exclusive lock, otherwise obtain shared lock.
* Lock will be automatically released when @a pool is cleared or destroyed.
* Use @a pool for memory allocations.
*
* @deprecated Provided for backward compatibility with the 1.0 API.
*/
SVN_DEPRECATED
svn_error_t *
svn_io_file_lock(const char *lock_file,
svn_boolean_t exclusive,
apr_pool_t *pool);
/**
* Lock file at @a lock_file. If @a exclusive is TRUE,
* obtain exclusive lock, otherwise obtain shared lock.
*
* If @a nonblocking is TRUE, do not wait for the lock if it
* is not available: throw an error instead.
*
* Lock will be automatically released when @a pool is cleared or destroyed.
* Use @a pool for memory allocations.
*
* @since New in 1.1.
*/
svn_error_t *
svn_io_file_lock2(const char *lock_file,
svn_boolean_t exclusive,
svn_boolean_t nonblocking,
apr_pool_t *pool);
/**
* Lock the file @a lockfile_handle. If @a exclusive is TRUE,
* obtain exclusive lock, otherwise obtain shared lock.
*
* If @a nonblocking is TRUE, do not wait for the lock if it
* is not available: throw an error instead.
*
* Lock will be automatically released when @a pool is cleared or destroyed.
* You may also explicitly call svn_io_unlock_open_file().
* Use @a pool for memory allocations. @a pool must be the pool that
* @a lockfile_handle has been created in or one of its sub-pools.
*
* @since New in 1.8.
*/
svn_error_t *
svn_io_lock_open_file(apr_file_t *lockfile_handle,
svn_boolean_t exclusive,
svn_boolean_t nonblocking,
apr_pool_t *pool);
/**
* Unlock the file @a lockfile_handle.
*
* Use @a pool for memory allocations. @a pool must be the pool that
* was passed to svn_io_lock_open_file().
*
* @since New in 1.8.
*/
svn_error_t *
svn_io_unlock_open_file(apr_file_t *lockfile_handle,
apr_pool_t *pool);
/**
* Flush any unwritten data from @a file to disk. Use @a pool for
* memory allocations.
*
* @since New in 1.1.
*/
svn_error_t *
svn_io_file_flush_to_disk(apr_file_t *file,
apr_pool_t *pool);
/** Copy the file whose basename (or relative path) is @a file within
* directory @a src_path to the same basename (or relative path) within
* directory @a dest_path. Overwrite the destination file if it already
* exists. The destination directory (including any directory
* components in @a name) must already exist. Set the destination
* file's permissions to match those of the source. Use @a pool for
* memory allocations.
*/
svn_error_t *
svn_io_dir_file_copy(const char *src_path,
const char *dest_path,
const char *file,
apr_pool_t *pool);
/** Generic byte-streams
*
* @defgroup svn_io_byte_streams Generic byte streams
* @{
*/
/** An abstract stream of bytes--either incoming or outgoing or both.
*
* The creator of a stream sets functions to handle read and write.
* Both of these handlers accept a baton whose value is determined at
* stream creation time; this baton can point to a structure
* containing data associated with the stream. If a caller attempts
* to invoke a handler which has not been set, it will generate a
* runtime assertion failure. The creator can also set a handler for
* close requests so that it can flush buffered data or whatever;
* if a close handler is not specified, a close request on the stream
* will simply be ignored. Note that svn_stream_close() does not
* deallocate the memory used to allocate the stream structure; free
* the pool you created the stream in to free that memory.
*
* The read and write handlers accept length arguments via pointer.
* On entry to the handler, the pointed-to value should be the amount
* of data which can be read or the amount of data to write. When the
* handler returns, the value is reset to the amount of data actually
* read or written. Handlers are obliged to complete a read or write
* to the maximum extent possible; thus, a short read with no
* associated error implies the end of the input stream, and a short
* write should never occur without an associated error.
*
* In Subversion 1.7 reset support was added as an optional feature of
* streams. If a stream implements resetting it allows reading the data
* again after a successful call to svn_stream_reset().
*/
typedef struct svn_stream_t svn_stream_t;
/** Read handler function for a generic stream. @see svn_stream_t. */
typedef svn_error_t *(*svn_read_fn_t)(void *baton,
char *buffer,
apr_size_t *len);
/** Skip data handler function for a generic stream. @see svn_stream_t
* and svn_stream_skip().
* @since New in 1.7.
*/
typedef svn_error_t *(*svn_stream_skip_fn_t)(void *baton,
apr_size_t len);
/** Write handler function for a generic stream. @see svn_stream_t. */
typedef svn_error_t *(*svn_write_fn_t)(void *baton,
const char *data,
apr_size_t *len);
/** Close handler function for a generic stream. @see svn_stream_t. */
typedef svn_error_t *(*svn_close_fn_t)(void *baton);
/** An opaque type which represents a mark on a stream. There is no
* concrete definition of this type, it is a named type for stream
* implementation specific baton pointers.
*
* @see svn_stream_mark().
* @since New in 1.7.
*/
typedef struct svn_stream_mark_t svn_stream_mark_t;
/** Mark handler function for a generic stream. @see svn_stream_t and
* svn_stream_mark().
*
* @since New in 1.7.
*/
typedef svn_error_t *(*svn_stream_mark_fn_t)(void *baton,
svn_stream_mark_t **mark,
apr_pool_t *pool);
/** Seek handler function for a generic stream. @see svn_stream_t and
* svn_stream_seek().
*
* @since New in 1.7.
*/
typedef svn_error_t *(*svn_stream_seek_fn_t)(void *baton,
const svn_stream_mark_t *mark);
/** Create a generic stream. @see svn_stream_t. */
svn_stream_t *
svn_stream_create(void *baton,
apr_pool_t *pool);
/** Set @a stream's baton to @a baton */
void
svn_stream_set_baton(svn_stream_t *stream,
void *baton);
/** Set @a stream's read function to @a read_fn */
void
svn_stream_set_read(svn_stream_t *stream,
svn_read_fn_t read_fn);
/** Set @a stream's skip function to @a skip_fn
*
* @since New in 1.7
*/
void
svn_stream_set_skip(svn_stream_t *stream,
svn_stream_skip_fn_t skip_fn);
/** Set @a stream's write function to @a write_fn */
void
svn_stream_set_write(svn_stream_t *stream,
svn_write_fn_t write_fn);
/** Set @a stream's close function to @a close_fn */
void
svn_stream_set_close(svn_stream_t *stream,
svn_close_fn_t close_fn);
/** Set @a stream's mark function to @a mark_fn
*
* @since New in 1.7.
*/
void
svn_stream_set_mark(svn_stream_t *stream,
svn_stream_mark_fn_t mark_fn);
/** Set @a stream's seek function to @a seek_fn
*
* @since New in 1.7.
*/
void
svn_stream_set_seek(svn_stream_t *stream,
svn_stream_seek_fn_t seek_fn);
/** Create a stream that is empty for reading and infinite for writing. */
svn_stream_t *
svn_stream_empty(apr_pool_t *pool);
/** Return a stream allocated in @a pool which forwards all requests
* to @a stream. Destruction is explicitly excluded from forwarding.
*
* @see http://subversion.apache.org/docs/community-guide/conventions.html#destruction-of-stacked-resources
*
* @since New in 1.4.
*/
svn_stream_t *
svn_stream_disown(svn_stream_t *stream,
apr_pool_t *pool);
/** Create a stream to read the file at @a path. It will be opened using
* the APR_BUFFERED and APR_BINARY flag, and APR_OS_DEFAULT for the perms.
* If you'd like to use different values, then open the file yourself, and
* use the svn_stream_from_aprfile2() interface.
*
* The stream will be returned in @a stream, and allocated from @a result_pool.
* Temporary allocations will be performed in @a scratch_pool.
*
* @since New in 1.6
*/
svn_error_t *
svn_stream_open_readonly(svn_stream_t **stream,
const char *path,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/** Create a stream to write a file at @a path. The file will be *created*
* using the APR_BUFFERED and APR_BINARY flag, and APR_OS_DEFAULT for the
* perms. The file will be created "exclusively", so if it already exists,
* then an error will be thrown. If you'd like to use different values, or
* open an existing file, then open the file yourself, and use the
* svn_stream_from_aprfile2() interface.
*
* The stream will be returned in @a stream, and allocated from @a result_pool.
* Temporary allocations will be performed in @a scratch_pool.
*
* @since New in 1.6
*/
svn_error_t *
svn_stream_open_writable(svn_stream_t **stream,
const char *path,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/** Create a writable stream to a file in the directory @a dirpath.
* The file will have an arbitrary and unique name, and the full path
* will be returned in @a temp_path. The stream will be returned in
* @a stream. Both will be allocated from @a result_pool.
*
* If @a dirpath is @c NULL, use the path returned from svn_io_temp_dir().
* (Note that when using the system-provided temp directory, it may not
* be possible to atomically rename the resulting file due to cross-device
* issues.)
*
* The file will be deleted according to @a delete_when. If that is
* #svn_io_file_del_on_pool_cleanup, it refers to @a result_pool.
*
* Temporary allocations will be performed in @a scratch_pool.
*
* @since New in 1.6
* @see svn_io_open_unique_file3()
*/
svn_error_t *
svn_stream_open_unique(svn_stream_t **stream,
const char **temp_path,
const char *dirpath,
svn_io_file_del_t delete_when,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/** Create a stream from an APR file. For convenience, if @a file is
* @c NULL, an empty stream created by svn_stream_empty() is returned.
*
* This function should normally be called with @a disown set to FALSE,
* in which case closing the stream will also close the underlying file.
*
* If @a disown is TRUE, the stream will disown the underlying file,
* meaning that svn_stream_close() will not close the file.
*
* @since New in 1.4.
*/
svn_stream_t *
svn_stream_from_aprfile2(apr_file_t *file,
svn_boolean_t disown,
apr_pool_t *pool);
/** Similar to svn_stream_from_aprfile2(), except that the file will
* always be disowned.
*
* @note The stream returned is not considered to "own" the underlying
* file, meaning that svn_stream_close() on the stream will not
* close the file.
*
* @deprecated Provided for backward compatibility with the 1.3 API.
*/
SVN_DEPRECATED
svn_stream_t *
svn_stream_from_aprfile(apr_file_t *file,
apr_pool_t *pool);
/** Set @a *in to a generic stream connected to stdin, allocated in
* @a pool. The stream and its underlying APR handle will be closed
* when @a pool is cleared or destroyed.
*
* @since New in 1.7.
*/
svn_error_t *
svn_stream_for_stdin(svn_stream_t **in,
apr_pool_t *pool);
/** Set @a *err to a generic stream connected to stderr, allocated in
* @a pool. The stream and its underlying APR handle will be closed
* when @a pool is cleared or destroyed.
*
* @since New in 1.7.
*/
svn_error_t *
svn_stream_for_stderr(svn_stream_t **err,
apr_pool_t *pool);
/** Set @a *out to a generic stream connected to stdout, allocated in
* @a pool. The stream and its underlying APR handle will be closed
* when @a pool is cleared or destroyed.
*/
svn_error_t *
svn_stream_for_stdout(svn_stream_t **out,
apr_pool_t *pool);
/** Return a generic stream connected to stringbuf @a str. Allocate the
* stream in @a pool.
*/
svn_stream_t *
svn_stream_from_stringbuf(svn_stringbuf_t *str,
apr_pool_t *pool);
/** Return a generic read-only stream connected to string @a str.
* Allocate the stream in @a pool.
*/
svn_stream_t *
svn_stream_from_string(const svn_string_t *str,
apr_pool_t *pool);
/** Return a generic stream which implements buffered reads and writes.
* The stream will preferentially store data in-memory, but may use
* disk storage as backup if the amount of data is large.
* Allocate the stream in @a result_pool
*
* @since New in 1.8.
*/
svn_stream_t *
svn_stream_buffered(apr_pool_t *result_pool);
/** Return a stream that decompresses all data read and compresses all
* data written. The stream @a stream is used to read and write all
* compressed data. All compression data structures are allocated on
* @a pool. If compression support is not compiled in then
* svn_stream_compressed() returns @a stream unmodified. Make sure you
* call svn_stream_close() on the stream returned by this function,
* so that all data are flushed and cleaned up.
*
* @note From 1.4, compression support is always compiled in.
*/
svn_stream_t *
svn_stream_compressed(svn_stream_t *stream,
apr_pool_t *pool);
/** Return a stream that calculates checksums for all data read
* and written. The stream @a stream is used to read and write all data.
* The stream and the resulting digests are allocated in @a pool.
*
* When the stream is closed, @a *read_checksum and @a *write_checksum
* are set to point to the resulting checksums, of type @a read_checksum_kind
* and @a write_checksum_kind, respectively.
*
* Both @a read_checksum and @a write_checksum can be @c NULL, in which case
* the respective checksum isn't calculated.
*
* If @a read_all is TRUE, make sure that all data available on @a
* stream is read (and checksummed) when the stream is closed.
*
* Read and write operations can be mixed without interfering.
*
* The @a stream passed into this function is closed when the created
* stream is closed.
*
* @since New in 1.6.
*/
svn_stream_t *
svn_stream_checksummed2(svn_stream_t *stream,
svn_checksum_t **read_checksum,
svn_checksum_t **write_checksum,
svn_checksum_kind_t checksum_kind,
svn_boolean_t read_all,
apr_pool_t *pool);
/**
* Similar to svn_stream_checksummed2(), but always returning the MD5
* checksum in @a read_digest and @a write_digest.
*
* @since New in 1.4.
* @deprecated Provided for backward compatibility with the 1.5 API.
*/
SVN_DEPRECATED
svn_stream_t *
svn_stream_checksummed(svn_stream_t *stream,
const unsigned char **read_digest,
const unsigned char **write_digest,
svn_boolean_t read_all,
apr_pool_t *pool);
/** Read from a generic stream. @see svn_stream_t. */
svn_error_t *
svn_stream_read(svn_stream_t *stream,
char *buffer,
apr_size_t *len);
/**
* Skip @a len bytes from a generic @a stream. If the stream is exhausted
* before @a len bytes have been read, return an error.
*
* @note No assumption can be made on the semantics of this function
* other than that the stream read pointer will be advanced by *len
* bytes. Depending on the capabilities of the underlying stream
* implementation, this may for instance be translated into a sequence
* of reads or a simple seek operation. If the stream implementation has
* not provided a skip function, this will read from the stream and
* discard the data.
+ *
+ * @since New in 1.7.
*/
svn_error_t *
svn_stream_skip(svn_stream_t *stream,
apr_size_t len);
/** Write to a generic stream. @see svn_stream_t. */
svn_error_t *
svn_stream_write(svn_stream_t *stream,
const char *data,
apr_size_t *len);
/** Close a generic stream. @see svn_stream_t. */
svn_error_t *
svn_stream_close(svn_stream_t *stream);
/** Reset a generic stream back to its origin. (E.g. On a file this would be
* implemented as a seek to position 0). This function returns a
* #SVN_ERR_STREAM_SEEK_NOT_SUPPORTED error when the stream doesn't
* implement resetting.
*
* @since New in 1.7.
*/
svn_error_t *
svn_stream_reset(svn_stream_t *stream);
/** Returns @c TRUE if the generic @a stream supports svn_stream_mark().
*
* @see svn_stream_mark()
* @since New in 1.7.
*/
svn_boolean_t
svn_stream_supports_mark(svn_stream_t *stream);
/** Set a @a mark at the current position of a generic @a stream,
* which can later be sought back to using svn_stream_seek().
* The @a mark is allocated in @a pool.
*
* This function returns the #SVN_ERR_STREAM_SEEK_NOT_SUPPORTED error
* if the stream doesn't implement seeking.
*
* @see svn_stream_seek()
* @since New in 1.7.
*/
svn_error_t *
svn_stream_mark(svn_stream_t *stream,
svn_stream_mark_t **mark,
apr_pool_t *pool);
/** Seek to a @a mark in a generic @a stream.
* This function returns the #SVN_ERR_STREAM_SEEK_NOT_SUPPORTED error
* if the stream doesn't implement seeking. Passing NULL as @a mark,
* seeks to the start of the stream.
*
* @see svn_stream_mark()
* @since New in 1.7.
*/
svn_error_t *
svn_stream_seek(svn_stream_t *stream, const svn_stream_mark_t *mark);
/** Return a writable stream which, when written to, writes to both of the
* underlying streams. Both of these streams will be closed upon closure of
* the returned stream; use svn_stream_disown() if this is not the desired
* behavior. One or both of @a out1 and @a out2 may be @c NULL. If both are
* @c NULL, @c NULL is returned.
*
* @since New in 1.7.
*/
svn_stream_t *
svn_stream_tee(svn_stream_t *out1,
svn_stream_t *out2,
apr_pool_t *pool);
/** Write NULL-terminated string @a str to @a stream.
*
* @since New in 1.8.
*
*/
svn_error_t *
svn_stream_puts(svn_stream_t *stream,
const char *str);
/** Write to @a stream using a printf-style @a fmt specifier, passed through
* apr_psprintf() using memory from @a pool.
*/
svn_error_t *
svn_stream_printf(svn_stream_t *stream,
apr_pool_t *pool,
const char *fmt,
...)
__attribute__((format(printf, 3, 4)));
/** Write to @a stream using a printf-style @a fmt specifier, passed through
* apr_psprintf() using memory from @a pool. The resulting string
* will be translated to @a encoding before it is sent to @a stream.
*
* @note Use @c APR_LOCALE_CHARSET to translate to the encoding of the
* current locale.
*
* @since New in 1.3.
*/
svn_error_t *
svn_stream_printf_from_utf8(svn_stream_t *stream,
const char *encoding,
apr_pool_t *pool,
const char *fmt,
...)
__attribute__((format(printf, 4, 5)));
/** Allocate @a *stringbuf in @a pool, and read into it one line (terminated
* by @a eol) from @a stream. The line-terminator is read from the stream,
* but is not added to the end of the stringbuf. Instead, the stringbuf
* ends with a usual '\\0'.
*
* If @a stream runs out of bytes before encountering a line-terminator,
* then set @a *eof to @c TRUE, otherwise set @a *eof to FALSE.
*/
svn_error_t *
svn_stream_readline(svn_stream_t *stream,
svn_stringbuf_t **stringbuf,
const char *eol,
svn_boolean_t *eof,
apr_pool_t *pool);
/**
* Read the contents of the readable stream @a from and write them to the
* writable stream @a to calling @a cancel_func before copying each chunk.
*
* @a cancel_func may be @c NULL.
*
* @note both @a from and @a to will be closed upon successful completion of
* the copy (but an error may still be returned, based on trying to close
* the two streams). If the closure is not desired, then you can use
* svn_stream_disown() to protect either or both of the streams from
* being closed.
*
* @since New in 1.6.
*/
svn_error_t *
svn_stream_copy3(svn_stream_t *from,
svn_stream_t *to,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool);
/**
* Same as svn_stream_copy3() but the streams are not closed.
*
* @since New in 1.5.
* @deprecated Provided for backward compatibility with the 1.5 API.
*/
SVN_DEPRECATED
svn_error_t *
svn_stream_copy2(svn_stream_t *from,
svn_stream_t *to,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool);
/**
* Same as svn_stream_copy3(), but without the cancellation function
* or stream closing.
*
* @since New in 1.1.
* @deprecated Provided for backward compatibility with the 1.4 API.
*/
SVN_DEPRECATED
svn_error_t *
svn_stream_copy(svn_stream_t *from,
svn_stream_t *to,
apr_pool_t *pool);
/** Set @a *same to TRUE if @a stream1 and @a stream2 have the same
* contents, else set it to FALSE.
*
* Both streams will be closed before this function returns (regardless of
* the result, or any possible error).
*
* Use @a scratch_pool for temporary allocations.
*
* @since New in 1.7.
*/
svn_error_t *
svn_stream_contents_same2(svn_boolean_t *same,
svn_stream_t *stream1,
svn_stream_t *stream2,
apr_pool_t *pool);
/**
* Same as svn_stream_contents_same2(), but the streams will not be closed.
*
* @since New in 1.4.
* @deprecated Provided for backward compatibility with the 1.6 API.
*/
SVN_DEPRECATED
svn_error_t *
svn_stream_contents_same(svn_boolean_t *same,
svn_stream_t *stream1,
svn_stream_t *stream2,
apr_pool_t *pool);
/** Read the contents of @a stream into memory, returning the data in
* @a result. The stream will be closed when it has been successfully and
* completely read.
*
* The returned memory is allocated in @a result_pool, and any temporary
* allocations are performed in @a scratch_pool.
*
* @note due to memory pseudo-reallocation behavior (due to pools), this
* can be a memory-intensive operation for large files.
*
* @since New in 1.6
*/
svn_error_t *
svn_string_from_stream(svn_string_t **result,
svn_stream_t *stream,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/** A function type provided for use as a callback from
* @c svn_stream_lazyopen_create().
*
* The callback function shall open a new stream and set @a *stream to
* the stream object, allocated in @a result_pool. @a baton is the
* callback baton that was passed to svn_stream_lazyopen_create().
*
* @a result_pool is the result pool that was passed to
* svn_stream_lazyopen_create(). The callback function may use
* @a scratch_pool for temporary allocations; the caller may clear or
* destroy @a scratch_pool any time after the function returns.
*
* @since New in 1.8.
*/
typedef svn_error_t *
(*svn_stream_lazyopen_func_t)(svn_stream_t **stream,
void *baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/** Return a generic stream which wraps another primary stream,
* delaying the "opening" of that stream until the first time the
* returned stream is accessed.
*
* @a open_func and @a open_baton are a callback function/baton pair
* which will be invoked upon the first access of the returned
* stream (read, write, mark, seek, skip, or possibly close). The
* callback shall open the primary stream.
*
* If the only "access" the returned stream gets is to close it
* then @a open_func will only be called if @a open_on_close is TRUE.
*
* @since New in 1.8.
*/
svn_stream_t *
svn_stream_lazyopen_create(svn_stream_lazyopen_func_t open_func,
void *open_baton,
svn_boolean_t open_on_close,
apr_pool_t *result_pool);
/** @} */
/** Set @a *result to a string containing the contents of @a
* filename, which is either "-" (indicating that stdin should be
* read) or the utf8-encoded path of a real file.
*
* @warning Callers should be aware of possible unexpected results
* when using this function to read from stdin where additional
* stdin-reading processes abound. For example, if a program tries
* both to invoke an external editor and to read from stdin, stdin
* could be trashed and the editor might act funky or die outright.
*
* @note due to memory pseudo-reallocation behavior (due to pools), this
* can be a memory-intensive operation for large files.
*
* @since New in 1.5.
*/
svn_error_t *
svn_stringbuf_from_file2(svn_stringbuf_t **result,
const char *filename,
apr_pool_t *pool);
/** Similar to svn_stringbuf_from_file2(), except that if @a filename
* is "-", return the error #SVN_ERR_UNSUPPORTED_FEATURE and don't
* touch @a *result.
*
* @deprecated Provided for backwards compatibility with the 1.4 API.
*/
SVN_DEPRECATED
svn_error_t *
svn_stringbuf_from_file(svn_stringbuf_t **result,
const char *filename,
apr_pool_t *pool);
/** Sets @a *result to a string containing the contents of the already opened
* @a file. Reads from the current position in file to the end. Does not
* close the file or reset the cursor position.
*
* @note due to memory pseudo-reallocation behavior (due to pools), this
* can be a memory-intensive operation for large files.
*/
svn_error_t *
svn_stringbuf_from_aprfile(svn_stringbuf_t **result,
apr_file_t *file,
apr_pool_t *pool);
/** Remove file @a path, a utf8-encoded path. This wraps apr_file_remove(),
* converting any error to a Subversion error. If @a ignore_enoent is TRUE, and
* the file is not present (APR_STATUS_IS_ENOENT returns TRUE), then no
* error will be returned.
*
* The file will be removed even if it is not writable. (On Windows and
* OS/2, this function first clears the file's read-only bit.)
*
* @since New in 1.7.
*/
svn_error_t *
svn_io_remove_file2(const char *path,
svn_boolean_t ignore_enoent,
apr_pool_t *scratch_pool);
/** Similar to svn_io_remove_file2(), except with @a ignore_enoent set to FALSE.
*
* @deprecated Provided for backwards compatibility with the 1.6 API.
*/
SVN_DEPRECATED
svn_error_t *
svn_io_remove_file(const char *path,
apr_pool_t *pool);
/** Recursively remove directory @a path. @a path is utf8-encoded.
* If @a ignore_enoent is @c TRUE, don't fail if the target directory
* doesn't exist. Use @a pool for temporary allocations.
*
* Because recursive delete of a directory tree can be a lengthy operation,
* provide @a cancel_func and @a cancel_baton for interruptibility.
*
* @since New in 1.5.
*/
svn_error_t *
svn_io_remove_dir2(const char *path,
svn_boolean_t ignore_enoent,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool);
/** Similar to svn_io_remove_dir2(), but with @a ignore_enoent set to
* @c FALSE and @a cancel_func and @a cancel_baton set to @c NULL.
*
* @deprecated Provided for backward compatibility with the 1.4 API
*/
SVN_DEPRECATED
svn_error_t *
svn_io_remove_dir(const char *path,
apr_pool_t *pool);
/** Read all of the disk entries in directory @a path, a utf8-encoded
* path. Set @a *dirents to a hash mapping dirent names (<tt>char *</tt>) to
* undefined non-NULL values, allocated in @a pool.
*
* @note The `.' and `..' directories normally returned by
* apr_dir_read() are NOT returned in the hash.
*
* @since New in 1.4.
* @deprecated Provided for backward compatibility with the 1.6 API.
*/
SVN_DEPRECATED
svn_error_t *
svn_io_get_dir_filenames(apr_hash_t **dirents,
const char *path,
apr_pool_t *pool);
/** Read all of the disk entries in directory @a path, a utf8-encoded
* path. Set @a *dirents to a hash mapping dirent names (<tt>char *</tt>) to
* #svn_io_dirent2_t structures, allocated in @a pool.
*
* If @a only_check_type is set to @c TRUE, only the kind and special
* fields of the svn_io_dirent2_t are filled.
*
* @note The `.' and `..' directories normally returned by
* apr_dir_read() are NOT returned in the hash.
*
* @note The kind field in the @a dirents is set according to the mapping
* as documented for svn_io_check_path().
*
* @since New in 1.7.
*/
svn_error_t *
svn_io_get_dirents3(apr_hash_t **dirents,
const char *path,
svn_boolean_t only_check_type,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/** Similar to svn_io_get_dirents3, but returns a mapping to svn_io_dirent_t
* structures instead of svn_io_dirent2_t and with only a single pool.
*
* @since New in 1.3.
* @deprecated Provided for backward compatibility with the 1.6 API.
*/
SVN_DEPRECATED
svn_error_t *
svn_io_get_dirents2(apr_hash_t **dirents,
const char *path,
apr_pool_t *pool);
/** Similar to svn_io_get_dirents2(), but @a *dirents is a hash table
* with #svn_node_kind_t values.
*
* @deprecated Provided for backwards compatibility with the 1.2 API.
*/
SVN_DEPRECATED
svn_error_t *
svn_io_get_dirents(apr_hash_t **dirents,
const char *path,
apr_pool_t *pool);
/** Create a svn_io_dirent2_t instance for path. Specialized variant of
* svn_io_stat() that directly translates node_kind and special.
*
* If @a verify_truename is @c TRUE, an additional check is performed to
* verify the truename of the last path component on case insensitive
* filesystems. This check is expensive compared to a just a stat,
* but certainly cheaper than a full truename calculation using
* apr_filepath_merge() which verifies all path components.
*
* If @a ignore_enoent is set to @c TRUE, set *dirent_p->kind to
* svn_node_none instead of returning an error.
*
* @since New in 1.8.
*/
svn_error_t *
svn_io_stat_dirent2(const svn_io_dirent2_t **dirent_p,
const char *path,
svn_boolean_t verify_truename,
svn_boolean_t ignore_enoent,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/** Similar to svn_io_stat_dirent2(), but always passes FALSE for
* @a verify_truename.
*
* @since New in 1.7.
* @deprecated Provided for backwards compatibility with the 1.7 API.
*/
SVN_DEPRECATED
svn_error_t *
svn_io_stat_dirent(const svn_io_dirent2_t **dirent_p,
const char *path,
svn_boolean_t ignore_enoent,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/** Callback function type for svn_io_dir_walk() */
typedef svn_error_t * (*svn_io_walk_func_t)(void *baton,
const char *path,
const apr_finfo_t *finfo,
apr_pool_t *pool);
/** Recursively walk the directory rooted at @a dirname, a
* utf8-encoded path, invoking @a walk_func (with @a walk_baton) for
* each item in the tree. For a given directory, invoke @a walk_func
* on the directory itself before invoking it on any children thereof.
*
* Deliver to @a walk_func the information specified by @a wanted,
* which is a combination of @c APR_FINFO_* flags, plus the
* information specified by @c APR_FINFO_TYPE and @c APR_FINFO_NAME.
*
* Use @a pool for all allocations.
*
* @note This function does not currently pass all file types to @a
* walk_func -- only APR_DIR, APR_REG, and APR_LNK. We reserve the
* right to pass additional file types through this interface in the
* future, though, so implementations of this callback should
* explicitly test FINFO->filetype. See the APR library's
* apr_filetype_e enum for the various filetypes and their meanings.
*
* @since New in 1.7.
*/
svn_error_t *
svn_io_dir_walk2(const char *dirname,
apr_int32_t wanted,
svn_io_walk_func_t walk_func,
void *walk_baton,
apr_pool_t *pool);
/** Similar to svn_io_dir_walk(), but only calls @a walk_func for
* files of type APR_DIR (directory) and APR_REG (regular file).
*
* @deprecated Provided for backwards compatibility with the 1.6 API.
*/
SVN_DEPRECATED
svn_error_t *
svn_io_dir_walk(const char *dirname,
apr_int32_t wanted,
svn_io_walk_func_t walk_func,
void *walk_baton,
apr_pool_t *pool);
/**
* Start @a cmd with @a args, using utf8-encoded @a path as working
* directory. Return the process handle for the invoked program in @a
* *cmd_proc.
*
* If @a infile_pipe is TRUE, connect @a cmd's stdin to a pipe;
* otherwise, connect it to @a infile (which may be NULL). If
* @a outfile_pipe is TRUE, connect @a cmd's stdout to a pipe; otherwise,
* connect it to @a outfile (which may be NULL). If @a errfile_pipe
* is TRUE, connect @a cmd's stderr to a pipe; otherwise, connect it
* to @a errfile (which may be NULL). (Callers must pass FALSE for
* each of these boolean values for which the corresponding file
* handle is non-NULL.)
*
* @a args is a list of utf8-encoded <tt>const char *</tt> arguments,
* terminated by @c NULL. @a args[0] is the name of the program, though it
* need not be the same as @a cmd.
*
* If @a inherit is TRUE, the invoked program inherits its environment from
* the caller and @a cmd, if not absolute, is searched for in PATH.
*
* If @a inherit is FALSE @a cmd must be an absolute path and the invoked
* program inherits the environment defined by @a env or runs with an empty
* environment in @a env is NULL.
*
* @note On some platforms, failure to execute @a cmd in the child process
* will result in error output being written to @a errfile, if non-NULL, and
* a non-zero exit status being returned to the parent process.
*
* @note An APR bug affects Windows: passing a NULL @a env does not
* guarantee the invoked program to run with an empty environment when
* @a inherit is FALSE, the program may inherit its parent's environment.
* Explicitly pass an empty @a env to get an empty environment.
*
* @since New in 1.8.
*/
svn_error_t *svn_io_start_cmd3(apr_proc_t *cmd_proc,
const char *path,
const char *cmd,
const char *const *args,
const char *const *env,
svn_boolean_t inherit,
svn_boolean_t infile_pipe,
apr_file_t *infile,
svn_boolean_t outfile_pipe,
apr_file_t *outfile,
svn_boolean_t errfile_pipe,
apr_file_t *errfile,
apr_pool_t *pool);
/**
* Similar to svn_io_start_cmd3() but with @a env always set to NULL.
*
* @deprecated Provided for backward compatibility with the 1.7 API
* @since New in 1.7.
*/
SVN_DEPRECATED
svn_error_t *svn_io_start_cmd2(apr_proc_t *cmd_proc,
const char *path,
const char *cmd,
const char *const *args,
svn_boolean_t inherit,
svn_boolean_t infile_pipe,
apr_file_t *infile,
svn_boolean_t outfile_pipe,
apr_file_t *outfile,
svn_boolean_t errfile_pipe,
apr_file_t *errfile,
apr_pool_t *pool);
/**
* Similar to svn_io_start_cmd2() but with @a infile_pipe, @a
* outfile_pipe, and @a errfile_pipe always FALSE.
*
* @deprecated Provided for backward compatibility with the 1.6 API
* @since New in 1.3.
*/
SVN_DEPRECATED
svn_error_t *
svn_io_start_cmd(apr_proc_t *cmd_proc,
const char *path,
const char *cmd,
const char *const *args,
svn_boolean_t inherit,
apr_file_t *infile,
apr_file_t *outfile,
apr_file_t *errfile,
apr_pool_t *pool);
/**
* Wait for the process @a *cmd_proc to complete and optionally retrieve
* its exit code. @a cmd is used only in error messages.
*
* If @a exitcode is not NULL, set @a *exitcode to the exit code of the
* process and do not consider any exit code to be an error. If @a exitcode
* is NULL, then if the exit code of the process is non-zero then return an
* #SVN_ERR_EXTERNAL_PROGRAM error.
*
* If @a exitwhy is not NULL, set @a *exitwhy to indicate why the process
* terminated and do not consider any reason to be an error. If @a exitwhy
* is NULL, then if the termination reason is not @c APR_PROC_CHECK_EXIT()
* then return an #SVN_ERR_EXTERNAL_PROGRAM error.
*
* @since New in 1.3.
*/
svn_error_t *
svn_io_wait_for_cmd(apr_proc_t *cmd_proc,
const char *cmd,
int *exitcode,
apr_exit_why_e *exitwhy,
apr_pool_t *pool);
/** Run a command to completion, by first calling svn_io_start_cmd() and
* then calling svn_io_wait_for_cmd(). The parameters correspond to
* the same-named parameters of those two functions.
*/
svn_error_t *
svn_io_run_cmd(const char *path,
const char *cmd,
const char *const *args,
int *exitcode,
apr_exit_why_e *exitwhy,
svn_boolean_t inherit,
apr_file_t *infile,
apr_file_t *outfile,
apr_file_t *errfile,
apr_pool_t *pool);
/** Invoke the configured @c diff program, with @a user_args (an array
* of utf8-encoded @a num_user_args arguments) if they are specified
* (that is, if @a user_args is non-NULL), or "-u" if they are not.
* If @a user_args is NULL, the value of @a num_user_args is ignored.
*
* Diff runs in utf8-encoded @a dir, and its exit status is stored in
* @a exitcode, if it is not @c NULL.
*
* If @a label1 and/or @a label2 are not NULL they will be passed to the diff
* process as the arguments of "-L" options. @a label1 and @a label2 are also
* in utf8, and will be converted to native charset along with the other args.
*
* @a from is the first file passed to diff, and @a to is the second. The
* stdout of diff will be sent to @a outfile, and the stderr to @a errfile.
*
* @a diff_cmd must be non-NULL.
*
* Do all allocation in @a pool.
* @since New in 1.6.0.
*/
svn_error_t *
svn_io_run_diff2(const char *dir,
const char *const *user_args,
int num_user_args,
const char *label1,
const char *label2,
const char *from,
const char *to,
int *exitcode,
apr_file_t *outfile,
apr_file_t *errfile,
const char *diff_cmd,
apr_pool_t *pool);
/** Similar to svn_io_run_diff2() but with @a diff_cmd encoded in internal
* encoding used by APR.
*
* @deprecated Provided for backwards compatibility with the 1.5 API. */
SVN_DEPRECATED
svn_error_t *
svn_io_run_diff(const char *dir,
const char *const *user_args,
int num_user_args,
const char *label1,
const char *label2,
const char *from,
const char *to,
int *exitcode,
apr_file_t *outfile,
apr_file_t *errfile,
const char *diff_cmd,
apr_pool_t *pool);
/** Invoke the configured @c diff3 program, in utf8-encoded @a dir
* like this:
*
* diff3 -E -m @a mine @a older @a yours > @a merged
*
* (See the diff3 documentation for details.)
*
* If @a user_args is non-NULL, replace "-E" with the <tt>const char*</tt>
* elements that @a user_args contains.
*
* @a mine, @a older and @a yours are utf8-encoded paths (relative to
* @a dir or absolute) to three files that already exist.
*
* @a merged is an open file handle, and is left open after the merge
* result is written to it. (@a merged should *not* be the same file
* as @a mine, or nondeterministic things may happen!)
*
* @a mine_label, @a older_label, @a yours_label are utf8-encoded label
* parameters for diff3's -L option. Any of them may be @c NULL, in
* which case the corresponding @a mine, @a older, or @a yours parameter is
* used instead.
*
* Set @a *exitcode to diff3's exit status. If @a *exitcode is anything
* other than 0 or 1, then return #SVN_ERR_EXTERNAL_PROGRAM. (Note the
* following from the diff3 info pages: "An exit status of 0 means
* `diff3' was successful, 1 means some conflicts were found, and 2
* means trouble.")
*
* @a diff3_cmd must be non-NULL.
*
* Do all allocation in @a pool.
*
* @since New in 1.4.
*/
svn_error_t *
svn_io_run_diff3_3(int *exitcode,
const char *dir,
const char *mine,
const char *older,
const char *yours,
const char *mine_label,
const char *older_label,
const char *yours_label,
apr_file_t *merged,
const char *diff3_cmd,
const apr_array_header_t *user_args,
apr_pool_t *pool);
/** Similar to svn_io_run_diff3_3(), but with @a diff3_cmd encoded in
* internal encoding used by APR.
*
* @deprecated Provided for backwards compatibility with the 1.5 API.
* @since New in 1.4.
*/
SVN_DEPRECATED
svn_error_t *
svn_io_run_diff3_2(int *exitcode,
const char *dir,
const char *mine,
const char *older,
const char *yours,
const char *mine_label,
const char *older_label,
const char *yours_label,
apr_file_t *merged,
const char *diff3_cmd,
const apr_array_header_t *user_args,
apr_pool_t *pool);
/** Similar to svn_io_run_diff3_2(), but with @a user_args set to @c NULL.
*
* @deprecated Provided for backwards compatibility with the 1.3 API.
*/
SVN_DEPRECATED
svn_error_t *
svn_io_run_diff3(const char *dir,
const char *mine,
const char *older,
const char *yours,
const char *mine_label,
const char *older_label,
const char *yours_label,
apr_file_t *merged,
int *exitcode,
const char *diff3_cmd,
apr_pool_t *pool);
/** Parse utf8-encoded @a mimetypes_file as a MIME types file (such as
* is provided with Apache HTTP Server), and set @a *type_map to a
* hash mapping <tt>const char *</tt> filename extensions to
* <tt>const char *</tt> MIME types.
*
* @since New in 1.5.
*/
svn_error_t *
svn_io_parse_mimetypes_file(apr_hash_t **type_map,
const char *mimetypes_file,
apr_pool_t *pool);
/** Examine utf8-encoded @a file to determine if it can be described by a
* known (as in, known by this function) Multipurpose Internet Mail
* Extension (MIME) type. If so, set @a *mimetype to a character string
* describing the MIME type, else set it to @c NULL.
*
* If not @c NULL, @a mimetype_map is a hash mapping <tt>const char *</tt>
* filename extensions to <tt>const char *</tt> MIME types, and is the
* first source consulted regarding @a file's MIME type.
*
* Use @a pool for any necessary allocations.
*
* @since New in 1.5.
*/
svn_error_t *
svn_io_detect_mimetype2(const char **mimetype,
const char *file,
apr_hash_t *mimetype_map,
apr_pool_t *pool);
/** Like svn_io_detect_mimetype2, but with @a mimetypes_map set to
* @c NULL.
*
* @deprecated Provided for backward compatibility with the 1.4 API
*/
SVN_DEPRECATED
svn_error_t *
svn_io_detect_mimetype(const char **mimetype,
const char *file,
apr_pool_t *pool);
/** Examine up to @a len bytes of data in @a buf to determine if the
* can be considered binary data, in which case return TRUE.
* If the data can be considered plain-text data, return FALSE.
*
* @since New in 1.7.
*/
svn_boolean_t
svn_io_is_binary_data(const void *buf, apr_size_t len);
/** Wrapper for apr_file_open(). @a fname is utf8-encoded.
Always passed flag | APR_BINARY to apr. */
svn_error_t *
svn_io_file_open(apr_file_t **new_file,
const char *fname,
apr_int32_t flag,
apr_fileperms_t perm,
apr_pool_t *pool);
/** Wrapper for apr_file_close(). */
svn_error_t *
svn_io_file_close(apr_file_t *file,
apr_pool_t *pool);
/** Wrapper for apr_file_getc(). */
svn_error_t *
svn_io_file_getc(char *ch,
apr_file_t *file,
apr_pool_t *pool);
/** Wrapper for apr_file_putc().
* @since New in 1.7
*/
svn_error_t *
svn_io_file_putc(char ch,
apr_file_t *file,
apr_pool_t *pool);
/** Wrapper for apr_file_info_get(). */
svn_error_t *
svn_io_file_info_get(apr_finfo_t *finfo,
apr_int32_t wanted,
apr_file_t *file,
apr_pool_t *pool);
/** Wrapper for apr_file_read(). */
svn_error_t *
svn_io_file_read(apr_file_t *file,
void *buf,
apr_size_t *nbytes,
apr_pool_t *pool);
/** Wrapper for apr_file_read_full().
*
* If @a hit_eof is not NULL, EOF will be indicated there and no
* svn_error_t error object will be created upon EOF.
*
* @since New in 1.7
*/
svn_error_t *
svn_io_file_read_full2(apr_file_t *file,
void *buf,
apr_size_t nbytes,
apr_size_t *bytes_read,
svn_boolean_t *hit_eof,
apr_pool_t *pool);
/** Similar to svn_io_file_read_full2 with hit_eof being set
* to @c NULL.
*
* @deprecated Provided for backward compatibility with the 1.6 API
*/
SVN_DEPRECATED
svn_error_t *
svn_io_file_read_full(apr_file_t *file,
void *buf,
apr_size_t nbytes,
apr_size_t *bytes_read,
apr_pool_t *pool);
/** Wrapper for apr_file_seek(). */
svn_error_t *
svn_io_file_seek(apr_file_t *file,
apr_seek_where_t where,
apr_off_t *offset,
apr_pool_t *pool);
/** Wrapper for apr_file_write(). */
svn_error_t *
svn_io_file_write(apr_file_t *file,
const void *buf,
apr_size_t *nbytes,
apr_pool_t *pool);
/** Wrapper for apr_file_write_full(). */
svn_error_t *
svn_io_file_write_full(apr_file_t *file,
const void *buf,
apr_size_t nbytes,
apr_size_t *bytes_written,
apr_pool_t *pool);
/**
* Open a unique file in @a dirpath, and write @a nbytes from @a buf to
* the file before flushing it to disk and closing it. Return the name
* of the newly created file in @a *tmp_path, allocated in @a pool.
*
* If @a dirpath is @c NULL, use the path returned from svn_io_temp_dir().
* (Note that when using the system-provided temp directory, it may not
* be possible to atomically rename the resulting file due to cross-device
* issues.)
*
* The file will be deleted according to @a delete_when.
*
* @since New in 1.6.
*/
svn_error_t *
svn_io_write_unique(const char **tmp_path,
const char *dirpath,
const void *buf,
apr_size_t nbytes,
svn_io_file_del_t delete_when,
apr_pool_t *pool);
/** Wrapper for apr_file_trunc().
* @since New in 1.6. */
svn_error_t *
svn_io_file_trunc(apr_file_t *file,
apr_off_t offset,
apr_pool_t *pool);
/** Wrapper for apr_stat(). @a fname is utf8-encoded. */
svn_error_t *
svn_io_stat(apr_finfo_t *finfo,
const char *fname,
apr_int32_t wanted,
apr_pool_t *pool);
/** Rename and/or move the node (not necessarily a regular file) at
* @a from_path to a new path @a to_path within the same filesystem.
* In some cases, an existing node at @a to_path will be overwritten.
*
* A wrapper for apr_file_rename(). @a from_path and @a to_path are
* utf8-encoded.
*/
svn_error_t *
svn_io_file_rename(const char *from_path,
const char *to_path,
apr_pool_t *pool);
/** Move the file from @a from_path to @a to_path, even across device
* boundaries. Overwrite @a to_path if it exists.
*
* @note This function is different from svn_io_file_rename in that the
* latter fails in the 'across device boundaries' case.
*
* @since New in 1.3.
*/
svn_error_t *
svn_io_file_move(const char *from_path,
const char *to_path,
apr_pool_t *pool);
/** Wrapper for apr_dir_make(). @a path is utf8-encoded. */
svn_error_t *
svn_io_dir_make(const char *path,
apr_fileperms_t perm,
apr_pool_t *pool);
/** Same as svn_io_dir_make(), but sets the hidden attribute on the
directory on systems that support it. */
svn_error_t *
svn_io_dir_make_hidden(const char *path,
apr_fileperms_t perm,
apr_pool_t *pool);
/**
* Same as svn_io_dir_make(), but attempts to set the sgid on the
* directory on systems that support it. Does not return an error if
* the attempt to set the sgid bit fails. On Unix filesystems,
* setting the sgid bit on a directory ensures that files and
* subdirectories created within inherit group ownership from the
* parent instead of from the primary gid.
*
* @since New in 1.1.
*/
svn_error_t *
svn_io_dir_make_sgid(const char *path,
apr_fileperms_t perm,
apr_pool_t *pool);
/** Wrapper for apr_dir_open(). @a dirname is utf8-encoded. */
svn_error_t *
svn_io_dir_open(apr_dir_t **new_dir,
const char *dirname,
apr_pool_t *pool);
/** Wrapper for apr_dir_close().
*
* @since New in 1.7.
*/
svn_error_t *
svn_io_dir_close(apr_dir_t *thedir);
/** Wrapper for apr_dir_remove(). @a dirname is utf8-encoded.
* @note This function has this name to avoid confusion with
* svn_io_remove_dir2(), which is recursive.
*/
svn_error_t *
svn_io_dir_remove_nonrecursive(const char *dirname,
apr_pool_t *pool);
/** Wrapper for apr_dir_read(). Ensures that @a finfo->name is
* utf8-encoded, which means allocating @a finfo->name in @a pool,
* which may or may not be the same as @a finfo's pool. Use @a pool
* for error allocation as well.
*/
svn_error_t *
svn_io_dir_read(apr_finfo_t *finfo,
apr_int32_t wanted,
apr_dir_t *thedir,
apr_pool_t *pool);
/** Wrapper for apr_file_name_get(). @a *filename is utf8-encoded.
*
* @note The file name may be NULL.
*
* @since New in 1.7. */
svn_error_t *
svn_io_file_name_get(const char **filename,
apr_file_t *file,
apr_pool_t *pool);
/** Version/format files.
*
* @defgroup svn_io_format_files Version/format files
* @{
*/
/** Set @a *version to the integer that starts the file at @a path. If the
* file does not begin with a series of digits followed by a newline,
* return the error #SVN_ERR_BAD_VERSION_FILE_FORMAT. Use @a pool for
* all allocations.
*/
svn_error_t *
svn_io_read_version_file(int *version,
const char *path,
apr_pool_t *pool);
/** Create (or overwrite) the file at @a path with new contents,
* formatted as a non-negative integer @a version followed by a single
* newline. On successful completion the file will be read-only. Use
* @a pool for all allocations.
*/
svn_error_t *
svn_io_write_version_file(const char *path,
int version,
apr_pool_t *pool);
/** Read a line of text from a file, up to a specified length.
*
* Allocate @a *stringbuf in @a result_pool, and read into it one line
* from @a file. Reading stops either after a line-terminator was found
* or after @a max_len bytes have been read.
*
* If end-of-file is reached or @a max_len bytes have been read, and @a eof
* is not NULL, then set @a *eof to @c TRUE.
*
* The line-terminator is not stored in @a *stringbuf.
* The line-terminator is detected automatically and stored in @a *eol
* if @a eol is not NULL. If EOF is reached and @a file does not end
* with a newline character, and @a eol is not NULL, @ *eol is set to NULL.
*
* @a scratch_pool is used for temporary allocations.
*
* Hint: To read all data until a line-terminator is hit, pass APR_SIZE_MAX
* for @a max_len.
*
* @since New in 1.8.
*/
svn_error_t *
svn_io_file_readline(apr_file_t *file,
svn_stringbuf_t **stringbuf,
const char **eol,
svn_boolean_t *eof,
apr_size_t max_len,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/** @} */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* SVN_IO_H */
Index: vendor/subversion/dist/subversion/include/svn_version.h
===================================================================
--- vendor/subversion/dist/subversion/include/svn_version.h (revision 286500)
+++ vendor/subversion/dist/subversion/include/svn_version.h (revision 286501)
@@ -1,411 +1,409 @@
/**
* @copyright
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
* @endcopyright
*
* @file svn_version.h
* @brief Version information.
*/
#ifndef SVN_VERSION_H
#define SVN_VERSION_H
/* Hack to prevent the resource compiler from including
- apr_general.h. It doesn't resolve the include paths
- correctly and blows up without this.
- */
-#ifndef APR_STRINGIFY
+ apr and other headers. */
+#ifndef SVN_WIN32_RESOURCE_COMPILATION
#include <apr_general.h>
-#endif
#include <apr_tables.h>
#include "svn_types.h"
+#endif
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* Symbols that define the version number. */
/* Version numbers: <major>.<minor>.<micro>
*
* The version numbers in this file follow the rules established by:
*
* http://apr.apache.org/versioning.html
*/
/** Major version number.
*
* Modify when incompatible changes are made to published interfaces.
*/
#define SVN_VER_MAJOR 1
/** Minor version number.
*
* Modify when new functionality is added or new interfaces are
* defined, but all changes are backward compatible.
*/
#define SVN_VER_MINOR 8
/**
* Patch number.
*
* Modify for every released patch.
*
* @since New in 1.1.
*/
-#define SVN_VER_PATCH 10
+#define SVN_VER_PATCH 14
/** @deprecated Provided for backward compatibility with the 1.0 API. */
#define SVN_VER_MICRO SVN_VER_PATCH
/** @deprecated Provided for backward compatibility with the 1.0 API. */
#define SVN_VER_LIBRARY SVN_VER_MAJOR
/** Version tag: a string describing the version.
*
* This tag remains " (dev build)" in the repository so that we can
* always see from "svn --version" that the software has been built
* from the repository rather than a "blessed" distribution.
*
* When rolling a tarball, we automatically replace this text with " (r1234)"
* (where 1234 is the last revision on the branch prior to the release)
* for final releases; in prereleases, it becomes " (Alpha 1)",
* " (Beta 1)", etc., as appropriate.
*
* Always change this at the same time as SVN_VER_NUMTAG.
*/
-#define SVN_VER_TAG " (r1615264)"
+#define SVN_VER_TAG " (r1692801)"
/** Number tag: a string describing the version.
*
* This tag is used to generate a version number string to identify
* the client and server in HTTP requests, for example. It must not
* contain any spaces. This value remains "-dev" in the repository.
*
* When rolling a tarball, we automatically replace this text with ""
* for final releases; in prereleases, it becomes "-alpha1, "-beta1",
* etc., as appropriate.
*
* Always change this at the same time as SVN_VER_TAG.
*/
#define SVN_VER_NUMTAG ""
/** Revision number: The repository revision number of this release.
*
* This constant is used to generate the build number part of the Windows
* file version. Its value remains 0 in the repository.
*
* When rolling a tarball, we automatically replace it with what we
* guess to be the correct revision number.
*/
-#define SVN_VER_REVISION 1615264
+#define SVN_VER_REVISION 1692801
/* Version strings composed from the above definitions. */
/** Version number */
#define SVN_VER_NUM APR_STRINGIFY(SVN_VER_MAJOR) \
"." APR_STRINGIFY(SVN_VER_MINOR) \
"." APR_STRINGIFY(SVN_VER_PATCH)
/** Version number with tag (contains no whitespace) */
#define SVN_VER_NUMBER SVN_VER_NUM SVN_VER_NUMTAG
/** Complete version string */
#define SVN_VERSION SVN_VER_NUMBER SVN_VER_TAG
/* Version queries and compatibility checks */
/**
* Version information. Each library contains a function called
* svn_<i>libname</i>_version() that returns a pointer to a statically
* allocated object of this type.
*
* @since New in 1.1.
*/
struct svn_version_t
{
int major; /**< Major version number */
int minor; /**< Minor version number */
int patch; /**< Patch number */
/**
* The version tag (#SVN_VER_NUMTAG). Must always point to a
* statically allocated string.
*/
const char *tag;
};
/**
* Define a static svn_version_t object.
*
* @since New in 1.1.
*/
#define SVN_VERSION_DEFINE(name) \
static const svn_version_t name = \
{ \
SVN_VER_MAJOR, \
SVN_VER_MINOR, \
SVN_VER_PATCH, \
SVN_VER_NUMTAG \
} \
/**
* Generate the implementation of a version query function.
*
* @since New in 1.1.
*/
#define SVN_VERSION_BODY \
SVN_VERSION_DEFINE(versioninfo); \
return &versioninfo
/**
* Check library version compatibility. Return #TRUE if the client's
* version, given in @a my_version, is compatible with the library
* version, provided in @a lib_version.
*
* This function checks for version compatibility as per our
* guarantees, but requires an exact match when linking to an
* unreleased library. A development client is always compatible with
* a previous released library.
*
* @since New in 1.1.
*/
svn_boolean_t
svn_ver_compatible(const svn_version_t *my_version,
const svn_version_t *lib_version);
/**
* Check if @a my_version and @a lib_version encode the same version number.
*
* @since New in 1.2.
*/
svn_boolean_t
svn_ver_equal(const svn_version_t *my_version,
const svn_version_t *lib_version);
/**
* An entry in the compatibility checklist.
* @see svn_ver_check_list()
*
* @since New in 1.1.
*/
typedef struct svn_version_checklist_t
{
const char *label; /**< Entry label */
/** Version query function for this entry */
const svn_version_t *(*version_query)(void);
} svn_version_checklist_t;
/**
* Perform a series of version compatibility checks. Checks if @a
* my_version is compatible with each entry in @a checklist. @a
* checklist must end with an entry whose label is @c NULL.
*
* @see svn_ver_compatible()
*
* @since New in 1.1.
*/
svn_error_t *
svn_ver_check_list(const svn_version_t *my_version,
const svn_version_checklist_t *checklist);
/**
* Type of function returning library version.
*
* @since New in 1.6.
*/
typedef const svn_version_t *(*svn_version_func_t)(void);
/* libsvn_subr doesn't have an svn_subr header, so put the prototype here. */
/**
* Get libsvn_subr version information.
*
* @since New in 1.1.
*/
const svn_version_t *
svn_subr_version(void);
/**
* Extended version information, including info about the running system.
*
* @since New in 1.8.
*/
typedef struct svn_version_extended_t svn_version_extended_t;
/**
* Return version information for the running program. If @a verbose
* is #TRUE, collect extra information that may be expensive to
* retrieve (for example, the OS release name, list of shared
* libraries, etc.). Use @a pool for all allocations.
*
* @since New in 1.8.
*/
const svn_version_extended_t *
svn_version_extended(svn_boolean_t verbose,
apr_pool_t *pool);
/**
* Accessor for svn_version_extended_t.
*
* @return The date when the libsvn_subr library was compiled, in the
* format defined by the C standard macro @c __DATE__.
*
* @since New in 1.8.
*/
const char *
svn_version_ext_build_date(const svn_version_extended_t *ext_info);
/**
* Accessor for svn_version_extended_t.
*
* @return The time when the libsvn_subr library was compiled, in the
* format defined by the C standard macro @c __TIME__.
*
* @since New in 1.8.
*/
const char *
svn_version_ext_build_time(const svn_version_extended_t *ext_info);
/**
* Accessor for svn_version_extended_t.
*
* @return The canonical host triplet (arch-vendor-osname) of the
* system where libsvn_subr was compiled.
*
* @note On Unix-like systems (includng Mac OS X), this string is the
* same as the output of the config.guess script.
*
* @since New in 1.8.
*/
const char *
svn_version_ext_build_host(const svn_version_extended_t *ext_info);
/**
* Accessor for svn_version_extended_t.
*
* @return The localized copyright notice.
*
* @since New in 1.8.
*/
const char *
svn_version_ext_copyright(const svn_version_extended_t *ext_info);
/**
* Accessor for svn_version_extended_t.
*
* @return The canonical host triplet (arch-vendor-osname) of the
* system where the current process is running.
*
* @note This string may not be the same as the output of config.guess
* on the same system.
*
* @since New in 1.8.
*/
const char *
svn_version_ext_runtime_host(const svn_version_extended_t *ext_info);
/**
* Accessor for svn_version_extended_t.
*
* @return The "commercial" release name of the running operating
* system, if available. Not to be confused with, e.g., the output of
* "uname -v" or "uname -r". The returned value may be @c NULL.
*
* @since New in 1.8.
*/
const char *
svn_version_ext_runtime_osname(const svn_version_extended_t *ext_info);
/**
* Dependent library information.
* Describes the name and versions of known dependencies
* used by libsvn_subr.
*
* @since New in 1.8.
*/
typedef struct svn_version_ext_linked_lib_t
{
const char *name; /**< Library name */
const char *compiled_version; /**< Compile-time version string */
const char *runtime_version; /**< Run-time version string (optional) */
} svn_version_ext_linked_lib_t;
/**
* Accessor for svn_version_extended_t.
*
* @return Array of svn_version_ext_linked_lib_t describing dependent
* libraries. The returned value may be @c NULL.
*
* @since New in 1.8.
*/
const apr_array_header_t *
svn_version_ext_linked_libs(const svn_version_extended_t *ext_info);
/**
* Loaded shared library information.
* Describes the name and, where available, version of the shared libraries
* loaded by the running program.
*
* @since New in 1.8.
*/
typedef struct svn_version_ext_loaded_lib_t
{
const char *name; /**< Library name */
const char *version; /**< Library version (optional) */
} svn_version_ext_loaded_lib_t;
/**
* Accessor for svn_version_extended_t.
*
* @return Array of svn_version_ext_loaded_lib_t describing loaded
* shared libraries. The returned value may be @c NULL.
*
* @note On Mac OS X, the loaded frameworks, private frameworks and
* system libraries will not be listed.
*
* @since New in 1.8.
*/
const apr_array_header_t *
svn_version_ext_loaded_libs(const svn_version_extended_t *ext_info);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* SVN_VERSION_H */
Index: vendor/subversion/dist/subversion/libsvn_auth_gnome_keyring/gnome_keyring.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_auth_gnome_keyring/gnome_keyring.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_auth_gnome_keyring/gnome_keyring.c (revision 286501)
@@ -1,517 +1,395 @@
/*
* gnome_keyring.c: GNOME Keyring provider for SVN_AUTH_CRED_*
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
/* ==================================================================== */
/*** Includes. ***/
#include <apr_pools.h>
#include <apr_strings.h>
#include <glib.h>
#include <gnome-keyring.h>
#include "svn_auth.h"
#include "svn_config.h"
#include "svn_error.h"
#include "svn_hash.h"
#include "svn_pools.h"
#include "private/svn_auth_private.h"
#include "svn_private_config.h"
/*-----------------------------------------------------------------------*/
/* GNOME Keyring simple provider, puts passwords in GNOME Keyring */
/*-----------------------------------------------------------------------*/
-struct gnome_keyring_baton
-{
- const char *keyring_name;
- GnomeKeyringInfo *info;
- GMainLoop *loop;
-};
-
-
-/* Callback function to destroy gnome_keyring_baton. */
-static void
-callback_destroy_data_keyring(void *data)
-{
- struct gnome_keyring_baton *key_info = data;
-
- if (data == NULL)
- return;
-
- free((void*)key_info->keyring_name);
- key_info->keyring_name = NULL;
-
- if (key_info->info)
- {
- gnome_keyring_info_free(key_info->info);
- key_info->info = NULL;
- }
-
- return;
-}
-
-
-/* Callback function to complete the keyring operation. */
-static void
-callback_done(GnomeKeyringResult result,
- gpointer data)
-{
- struct gnome_keyring_baton *key_info = data;
-
- g_main_loop_quit(key_info->loop);
- return;
-}
-
-
-/* Callback function to get the keyring info. */
-static void
-callback_get_info_keyring(GnomeKeyringResult result,
- GnomeKeyringInfo *info,
- void *data)
-{
- struct gnome_keyring_baton *key_info = data;
-
- if (result == GNOME_KEYRING_RESULT_OK && info != NULL)
- {
- key_info->info = gnome_keyring_info_copy(info);
- }
- else
- {
- if (key_info->info != NULL)
- gnome_keyring_info_free(key_info->info);
-
- key_info->info = NULL;
- }
-
- g_main_loop_quit(key_info->loop);
-
- return;
-}
-
-
-/* Callback function to get the default keyring string name. */
-static void
-callback_default_keyring(GnomeKeyringResult result,
- const char *string,
- void *data)
-{
- struct gnome_keyring_baton *key_info = data;
-
- if (result == GNOME_KEYRING_RESULT_OK && string != NULL)
- {
- key_info->keyring_name = strdup(string);
- }
- else
- {
- free((void*)key_info->keyring_name);
- key_info->keyring_name = NULL;
- }
-
- g_main_loop_quit(key_info->loop);
-
- return;
-}
-
/* Returns the default keyring name, allocated in RESULT_POOL. */
static char*
get_default_keyring_name(apr_pool_t *result_pool)
{
- char *def = NULL;
- struct gnome_keyring_baton key_info;
+ char *name, *def;
+ GnomeKeyringResult gkr;
- key_info.info = NULL;
- key_info.keyring_name = NULL;
+ gkr = gnome_keyring_get_default_keyring_sync(&name);
+ if (gkr != GNOME_KEYRING_RESULT_OK)
+ return NULL;
- /* Finds default keyring. */
- key_info.loop = g_main_loop_new(NULL, FALSE);
- gnome_keyring_get_default_keyring(callback_default_keyring, &key_info, NULL);
- g_main_loop_run(key_info.loop);
+ def = apr_pstrdup(result_pool, name);
+ g_free(name);
- if (key_info.keyring_name == NULL)
- {
- callback_destroy_data_keyring(&key_info);
- return NULL;
- }
-
- def = apr_pstrdup(result_pool, key_info.keyring_name);
- callback_destroy_data_keyring(&key_info);
-
return def;
}
/* Returns TRUE if the KEYRING_NAME is locked. */
static svn_boolean_t
check_keyring_is_locked(const char *keyring_name)
{
- struct gnome_keyring_baton key_info;
+ GnomeKeyringInfo *info;
+ svn_boolean_t locked;
+ GnomeKeyringResult gkr;
- key_info.info = NULL;
- key_info.keyring_name = NULL;
+ gkr = gnome_keyring_get_info_sync(keyring_name, &info);
+ if (gkr != GNOME_KEYRING_RESULT_OK)
+ return FALSE;
- /* Get details about the default keyring. */
- key_info.loop = g_main_loop_new(NULL, FALSE);
- gnome_keyring_get_info(keyring_name, callback_get_info_keyring, &key_info,
- NULL);
- g_main_loop_run(key_info.loop);
+ if (gnome_keyring_info_get_is_locked(info))
+ locked = TRUE;
+ else
+ locked = FALSE;
- if (key_info.info == NULL)
- {
- callback_destroy_data_keyring(&key_info);
- return FALSE;
- }
+ gnome_keyring_info_free(info);
- /* Check if keyring is locked. */
- if (gnome_keyring_info_get_is_locked(key_info.info))
- return TRUE;
- else
- return FALSE;
+ return locked;
}
/* Unlock the KEYRING_NAME with the KEYRING_PASSWORD. If KEYRING was
successfully unlocked return TRUE. */
static svn_boolean_t
unlock_gnome_keyring(const char *keyring_name,
const char *keyring_password,
apr_pool_t *pool)
{
- struct gnome_keyring_baton key_info;
+ GnomeKeyringInfo *info;
+ GnomeKeyringResult gkr;
- key_info.info = NULL;
- key_info.keyring_name = NULL;
+ gkr = gnome_keyring_get_info_sync(keyring_name, &info);
+ if (gkr != GNOME_KEYRING_RESULT_OK)
+ return FALSE;
- /* Get details about the default keyring. */
- key_info.loop = g_main_loop_new(NULL, FALSE);
- gnome_keyring_get_info(keyring_name, callback_get_info_keyring,
- &key_info, NULL);
- g_main_loop_run(key_info.loop);
-
- if (key_info.info == NULL)
- {
- callback_destroy_data_keyring(&key_info);
- return FALSE;
- }
- else
- {
- key_info.loop = g_main_loop_new(NULL, FALSE);
- gnome_keyring_unlock(keyring_name, keyring_password,
- callback_done, &key_info, NULL);
- g_main_loop_run(key_info.loop);
- }
- callback_destroy_data_keyring(&key_info);
- if (check_keyring_is_locked(keyring_name))
+ gkr = gnome_keyring_unlock_sync(keyring_name, keyring_password);
+ gnome_keyring_info_free(info);
+ if (gkr != GNOME_KEYRING_RESULT_OK)
return FALSE;
- return TRUE;
+ return check_keyring_is_locked(keyring_name);
}
/* There is a race here: this ensures keyring is unlocked just now,
but will it still be unlocked when we use it? */
static svn_error_t *
ensure_gnome_keyring_is_unlocked(svn_boolean_t non_interactive,
apr_hash_t *parameters,
apr_pool_t *scratch_pool)
{
const char *default_keyring = get_default_keyring_name(scratch_pool);
if (! non_interactive)
{
svn_auth_gnome_keyring_unlock_prompt_func_t unlock_prompt_func =
svn_hash_gets(parameters,
SVN_AUTH_PARAM_GNOME_KEYRING_UNLOCK_PROMPT_FUNC);
void *unlock_prompt_baton =
svn_hash_gets(parameters,
SVN_AUTH_PARAM_GNOME_KEYRING_UNLOCK_PROMPT_BATON);
char *keyring_password;
if (unlock_prompt_func && check_keyring_is_locked(default_keyring))
{
SVN_ERR((*unlock_prompt_func)(&keyring_password,
default_keyring,
unlock_prompt_baton,
scratch_pool));
/* If keyring is locked give up and try the next provider. */
if (! unlock_gnome_keyring(default_keyring, keyring_password,
scratch_pool))
return SVN_NO_ERROR;
}
}
else
{
if (check_keyring_is_locked(default_keyring))
{
return svn_error_create(SVN_ERR_AUTHN_CREDS_UNAVAILABLE, NULL,
_("GNOME Keyring is locked and "
"we are non-interactive"));
}
}
return SVN_NO_ERROR;
}
/* Implementation of svn_auth__password_get_t that retrieves the password
from GNOME Keyring. */
static svn_error_t *
password_get_gnome_keyring(svn_boolean_t *done,
const char **password,
apr_hash_t *creds,
const char *realmstring,
const char *username,
apr_hash_t *parameters,
svn_boolean_t non_interactive,
apr_pool_t *pool)
{
GnomeKeyringResult result;
GList *items;
*done = FALSE;
SVN_ERR(ensure_gnome_keyring_is_unlocked(non_interactive, parameters, pool));
if (! svn_hash_gets(parameters, "gnome-keyring-opening-failed"))
{
result = gnome_keyring_find_network_password_sync(username, realmstring,
NULL, NULL, NULL, NULL,
0, &items);
}
else
{
result = GNOME_KEYRING_RESULT_DENIED;
}
if (result == GNOME_KEYRING_RESULT_OK)
{
if (items && items->data)
{
GnomeKeyringNetworkPasswordData *item = items->data;
if (item->password)
{
size_t len = strlen(item->password);
if (len > 0)
{
*password = apr_pstrmemdup(pool, item->password, len);
*done = TRUE;
}
}
gnome_keyring_network_password_list_free(items);
}
}
else
{
svn_hash_sets(parameters, "gnome-keyring-opening-failed", "");
}
return SVN_NO_ERROR;
}
/* Implementation of svn_auth__password_set_t that stores the password in
GNOME Keyring. */
static svn_error_t *
password_set_gnome_keyring(svn_boolean_t *done,
apr_hash_t *creds,
const char *realmstring,
const char *username,
const char *password,
apr_hash_t *parameters,
svn_boolean_t non_interactive,
apr_pool_t *pool)
{
GnomeKeyringResult result;
guint32 item_id;
*done = FALSE;
SVN_ERR(ensure_gnome_keyring_is_unlocked(non_interactive, parameters, pool));
if (! svn_hash_gets(parameters, "gnome-keyring-opening-failed"))
{
result = gnome_keyring_set_network_password_sync(NULL, /* default keyring */
username, realmstring,
NULL, NULL, NULL, NULL,
0, password,
&item_id);
}
else
{
result = GNOME_KEYRING_RESULT_DENIED;
}
if (result != GNOME_KEYRING_RESULT_OK)
{
svn_hash_sets(parameters, "gnome-keyring-opening-failed", "");
}
*done = (result == GNOME_KEYRING_RESULT_OK);
return SVN_NO_ERROR;
}
/* Get cached encrypted credentials from the simple provider's cache. */
static svn_error_t *
simple_gnome_keyring_first_creds(void **credentials,
void **iter_baton,
void *provider_baton,
apr_hash_t *parameters,
const char *realmstring,
apr_pool_t *pool)
{
return svn_auth__simple_creds_cache_get(credentials,
iter_baton, provider_baton,
parameters, realmstring,
password_get_gnome_keyring,
SVN_AUTH__GNOME_KEYRING_PASSWORD_TYPE,
pool);
}
/* Save encrypted credentials to the simple provider's cache. */
static svn_error_t *
simple_gnome_keyring_save_creds(svn_boolean_t *saved,
void *credentials,
void *provider_baton,
apr_hash_t *parameters,
const char *realmstring,
apr_pool_t *pool)
{
return svn_auth__simple_creds_cache_set(saved, credentials,
provider_baton, parameters,
realmstring,
password_set_gnome_keyring,
SVN_AUTH__GNOME_KEYRING_PASSWORD_TYPE,
pool);
}
#if GLIB_CHECK_VERSION(2,6,0)
static void
log_noop(const gchar *log_domain, GLogLevelFlags log_level,
const gchar *message, gpointer user_data)
{
/* do nothing */
}
#endif
static void
init_gnome_keyring(void)
{
const char *application_name = NULL;
application_name = g_get_application_name();
if (!application_name)
g_set_application_name("Subversion");
/* Ideally we call g_log_set_handler() with a log_domain specific to
libgnome-keyring. Unfortunately, at least as of gnome-keyring
2.22.3, it doesn't have its own log_domain. As a result, we
suppress stderr spam for not only libgnome-keyring, but for
anything else the app is linked to that uses glib logging and
doesn't specify a log_domain. */
#if GLIB_CHECK_VERSION(2,6,0)
g_log_set_default_handler(log_noop, NULL);
#endif
}
static const svn_auth_provider_t gnome_keyring_simple_provider = {
SVN_AUTH_CRED_SIMPLE,
simple_gnome_keyring_first_creds,
NULL,
simple_gnome_keyring_save_creds
};
/* Public API */
void
svn_auth_get_gnome_keyring_simple_provider
(svn_auth_provider_object_t **provider,
apr_pool_t *pool)
{
svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
po->vtable = &gnome_keyring_simple_provider;
*provider = po;
init_gnome_keyring();
}
/*-----------------------------------------------------------------------*/
/* GNOME Keyring SSL client certificate passphrase provider, */
/* puts passphrases in GNOME Keyring */
/*-----------------------------------------------------------------------*/
/* Get cached encrypted credentials from the ssl client cert password
provider's cache. */
static svn_error_t *
ssl_client_cert_pw_gnome_keyring_first_creds(void **credentials,
void **iter_baton,
void *provider_baton,
apr_hash_t *parameters,
const char *realmstring,
apr_pool_t *pool)
{
return svn_auth__ssl_client_cert_pw_cache_get(
credentials, iter_baton, provider_baton, parameters, realmstring,
password_get_gnome_keyring, SVN_AUTH__GNOME_KEYRING_PASSWORD_TYPE,
pool);
}
/* Save encrypted credentials to the ssl client cert password provider's
cache. */
static svn_error_t *
ssl_client_cert_pw_gnome_keyring_save_creds(svn_boolean_t *saved,
void *credentials,
void *provider_baton,
apr_hash_t *parameters,
const char *realmstring,
apr_pool_t *pool)
{
return svn_auth__ssl_client_cert_pw_cache_set(
saved, credentials, provider_baton, parameters, realmstring,
password_set_gnome_keyring, SVN_AUTH__GNOME_KEYRING_PASSWORD_TYPE,
pool);
}
static const svn_auth_provider_t gnome_keyring_ssl_client_cert_pw_provider = {
SVN_AUTH_CRED_SSL_CLIENT_CERT_PW,
ssl_client_cert_pw_gnome_keyring_first_creds,
NULL,
ssl_client_cert_pw_gnome_keyring_save_creds
};
/* Public API */
void
svn_auth_get_gnome_keyring_ssl_client_cert_pw_provider
(svn_auth_provider_object_t **provider,
apr_pool_t *pool)
{
svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
po->vtable = &gnome_keyring_ssl_client_cert_pw_provider;
*provider = po;
init_gnome_keyring();
}
Index: vendor/subversion/dist/subversion/libsvn_client/copy.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_client/copy.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_client/copy.c (revision 286501)
@@ -1,2439 +1,2442 @@
/*
* copy.c: copy/move wrappers around wc 'copy' functionality.
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
/* ==================================================================== */
/*** Includes. ***/
#include <string.h>
#include "svn_hash.h"
#include "svn_client.h"
#include "svn_error.h"
#include "svn_error_codes.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_opt.h"
#include "svn_time.h"
#include "svn_props.h"
#include "svn_mergeinfo.h"
#include "svn_pools.h"
#include "client.h"
#include "mergeinfo.h"
#include "svn_private_config.h"
#include "private/svn_wc_private.h"
#include "private/svn_ra_private.h"
#include "private/svn_mergeinfo_private.h"
#include "private/svn_client_private.h"
/*
* OUR BASIC APPROACH TO COPIES
* ============================
*
* for each source/destination pair
* if (not exist src_path)
* return ERR_BAD_SRC error
*
* if (exist dst_path)
* return ERR_OBSTRUCTION error
* else
* copy src_path into parent_of_dst_path as basename (dst_path)
*
* if (this is a move)
* delete src_path
*/
/*** Code. ***/
/* Extend the mergeinfo for the single WC path TARGET_WCPATH, adding
MERGEINFO to any mergeinfo pre-existing in the WC. */
static svn_error_t *
extend_wc_mergeinfo(const char *target_abspath,
apr_hash_t *mergeinfo,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
apr_hash_t *wc_mergeinfo;
/* Get a fresh copy of the pre-existing state of the WC's mergeinfo
updating it. */
SVN_ERR(svn_client__parse_mergeinfo(&wc_mergeinfo, ctx->wc_ctx,
target_abspath, pool, pool));
/* Combine the provided mergeinfo with any mergeinfo from the WC. */
if (wc_mergeinfo && mergeinfo)
SVN_ERR(svn_mergeinfo_merge2(wc_mergeinfo, mergeinfo, pool, pool));
else if (! wc_mergeinfo)
wc_mergeinfo = mergeinfo;
return svn_error_trace(
svn_client__record_wc_mergeinfo(target_abspath, wc_mergeinfo,
FALSE, ctx, pool));
}
/* Find the longest common ancestor of paths in COPY_PAIRS. If
SRC_ANCESTOR is NULL, ignore source paths in this calculation. If
DST_ANCESTOR is NULL, ignore destination paths in this calculation.
COMMON_ANCESTOR will be the common ancestor of both the
SRC_ANCESTOR and DST_ANCESTOR, and will only be set if it is not
NULL.
*/
static svn_error_t *
get_copy_pair_ancestors(const apr_array_header_t *copy_pairs,
const char **src_ancestor,
const char **dst_ancestor,
const char **common_ancestor,
apr_pool_t *pool)
{
apr_pool_t *subpool = svn_pool_create(pool);
svn_client__copy_pair_t *first;
const char *first_dst;
const char *first_src;
const char *top_dst;
svn_boolean_t src_is_url;
svn_boolean_t dst_is_url;
char *top_src;
int i;
first = APR_ARRAY_IDX(copy_pairs, 0, svn_client__copy_pair_t *);
/* Because all the destinations are in the same directory, we can easily
determine their common ancestor. */
first_dst = first->dst_abspath_or_url;
dst_is_url = svn_path_is_url(first_dst);
if (copy_pairs->nelts == 1)
top_dst = apr_pstrdup(subpool, first_dst);
else
top_dst = dst_is_url ? svn_uri_dirname(first_dst, subpool)
: svn_dirent_dirname(first_dst, subpool);
/* Sources can came from anywhere, so we have to actually do some
work for them. */
first_src = first->src_abspath_or_url;
src_is_url = svn_path_is_url(first_src);
top_src = apr_pstrdup(subpool, first_src);
for (i = 1; i < copy_pairs->nelts; i++)
{
/* We don't need to clear the subpool here for several reasons:
1) If we do, we can't use it to allocate the initial versions of
top_src and top_dst (above).
2) We don't return any errors in the following loop, so we
are guanteed to destroy the subpool at the end of this function.
3) The number of iterations is likely to be few, and the loop will
be through quickly, so memory leakage will not be significant,
in time or space.
*/
const svn_client__copy_pair_t *pair =
APR_ARRAY_IDX(copy_pairs, i, svn_client__copy_pair_t *);
top_src = src_is_url
? svn_uri_get_longest_ancestor(top_src, pair->src_abspath_or_url,
subpool)
: svn_dirent_get_longest_ancestor(top_src, pair->src_abspath_or_url,
subpool);
}
if (src_ancestor)
*src_ancestor = apr_pstrdup(pool, top_src);
if (dst_ancestor)
*dst_ancestor = apr_pstrdup(pool, top_dst);
if (common_ancestor)
*common_ancestor =
src_is_url
? svn_uri_get_longest_ancestor(top_src, top_dst, pool)
: svn_dirent_get_longest_ancestor(top_src, top_dst, pool);
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
/* The guts of do_wc_to_wc_copies */
static svn_error_t *
do_wc_to_wc_copies_with_write_lock(svn_boolean_t *timestamp_sleep,
const apr_array_header_t *copy_pairs,
const char *dst_parent,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
int i;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
svn_error_t *err = SVN_NO_ERROR;
for (i = 0; i < copy_pairs->nelts; i++)
{
const char *dst_abspath;
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
svn_pool_clear(iterpool);
/* Check for cancellation */
if (ctx->cancel_func)
SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
/* Perform the copy */
dst_abspath = svn_dirent_join(pair->dst_parent_abspath, pair->base_name,
iterpool);
*timestamp_sleep = TRUE;
err = svn_wc_copy3(ctx->wc_ctx, pair->src_abspath_or_url, dst_abspath,
FALSE /* metadata_only */,
ctx->cancel_func, ctx->cancel_baton,
ctx->notify_func2, ctx->notify_baton2, iterpool);
if (err)
break;
}
svn_pool_destroy(iterpool);
SVN_ERR(err);
return SVN_NO_ERROR;
}
/* Copy each COPY_PAIR->SRC into COPY_PAIR->DST. Use POOL for temporary
allocations. */
static svn_error_t *
do_wc_to_wc_copies(svn_boolean_t *timestamp_sleep,
const apr_array_header_t *copy_pairs,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
const char *dst_parent, *dst_parent_abspath;
SVN_ERR(get_copy_pair_ancestors(copy_pairs, NULL, &dst_parent, NULL, pool));
if (copy_pairs->nelts == 1)
dst_parent = svn_dirent_dirname(dst_parent, pool);
SVN_ERR(svn_dirent_get_absolute(&dst_parent_abspath, dst_parent, pool));
SVN_WC__CALL_WITH_WRITE_LOCK(
do_wc_to_wc_copies_with_write_lock(timestamp_sleep, copy_pairs, dst_parent,
ctx, pool),
ctx->wc_ctx, dst_parent_abspath, FALSE, pool);
return SVN_NO_ERROR;
}
/* The locked bit of do_wc_to_wc_moves. */
static svn_error_t *
do_wc_to_wc_moves_with_locks2(svn_client__copy_pair_t *pair,
const char *dst_parent_abspath,
svn_boolean_t lock_src,
svn_boolean_t lock_dst,
svn_boolean_t allow_mixed_revisions,
svn_boolean_t metadata_only,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
const char *dst_abspath;
dst_abspath = svn_dirent_join(dst_parent_abspath, pair->base_name,
scratch_pool);
SVN_ERR(svn_wc__move2(ctx->wc_ctx, pair->src_abspath_or_url,
dst_abspath, metadata_only,
allow_mixed_revisions,
ctx->cancel_func, ctx->cancel_baton,
ctx->notify_func2, ctx->notify_baton2,
scratch_pool));
return SVN_NO_ERROR;
}
/* Wrapper to add an optional second lock */
static svn_error_t *
do_wc_to_wc_moves_with_locks1(svn_client__copy_pair_t *pair,
const char *dst_parent_abspath,
svn_boolean_t lock_src,
svn_boolean_t lock_dst,
svn_boolean_t allow_mixed_revisions,
svn_boolean_t metadata_only,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
if (lock_dst)
SVN_WC__CALL_WITH_WRITE_LOCK(
do_wc_to_wc_moves_with_locks2(pair, dst_parent_abspath, lock_src,
lock_dst, allow_mixed_revisions,
metadata_only,
ctx, scratch_pool),
ctx->wc_ctx, dst_parent_abspath, FALSE, scratch_pool);
else
SVN_ERR(do_wc_to_wc_moves_with_locks2(pair, dst_parent_abspath, lock_src,
lock_dst, allow_mixed_revisions,
metadata_only,
ctx, scratch_pool));
return SVN_NO_ERROR;
}
/* Move each COPY_PAIR->SRC into COPY_PAIR->DST, deleting COPY_PAIR->SRC
afterwards. Use POOL for temporary allocations. */
static svn_error_t *
do_wc_to_wc_moves(svn_boolean_t *timestamp_sleep,
const apr_array_header_t *copy_pairs,
const char *dst_path,
svn_boolean_t allow_mixed_revisions,
svn_boolean_t metadata_only,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
int i;
apr_pool_t *iterpool = svn_pool_create(pool);
svn_error_t *err = SVN_NO_ERROR;
for (i = 0; i < copy_pairs->nelts; i++)
{
const char *src_parent_abspath;
svn_boolean_t lock_src, lock_dst;
const char *src_wcroot_abspath;
const char *dst_wcroot_abspath;
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
svn_pool_clear(iterpool);
/* Check for cancellation */
if (ctx->cancel_func)
SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
src_parent_abspath = svn_dirent_dirname(pair->src_abspath_or_url,
iterpool);
SVN_ERR(svn_wc__get_wcroot(&src_wcroot_abspath,
ctx->wc_ctx, src_parent_abspath,
iterpool, iterpool));
SVN_ERR(svn_wc__get_wcroot(&dst_wcroot_abspath,
ctx->wc_ctx, pair->dst_parent_abspath,
iterpool, iterpool));
/* We now need to lock the right combination of batons.
Four cases:
1) src_parent == dst_parent
2) src_parent is parent of dst_parent
3) dst_parent is parent of src_parent
4) src_parent and dst_parent are disjoint
We can handle 1) as either 2) or 3) */
if (strcmp(src_parent_abspath, pair->dst_parent_abspath) == 0
|| (svn_dirent_is_child(src_parent_abspath, pair->dst_parent_abspath,
NULL)
&& !svn_dirent_is_child(src_parent_abspath, dst_wcroot_abspath,
NULL)))
{
lock_src = TRUE;
lock_dst = FALSE;
}
else if (svn_dirent_is_child(pair->dst_parent_abspath,
src_parent_abspath, NULL)
&& !svn_dirent_is_child(pair->dst_parent_abspath,
src_wcroot_abspath, NULL))
{
lock_src = FALSE;
lock_dst = TRUE;
}
else
{
lock_src = TRUE;
lock_dst = TRUE;
}
*timestamp_sleep = TRUE;
/* Perform the copy and then the delete. */
if (lock_src)
SVN_WC__CALL_WITH_WRITE_LOCK(
do_wc_to_wc_moves_with_locks1(pair, pair->dst_parent_abspath,
lock_src, lock_dst,
allow_mixed_revisions,
metadata_only,
ctx, iterpool),
ctx->wc_ctx, src_parent_abspath,
FALSE, iterpool);
else
SVN_ERR(do_wc_to_wc_moves_with_locks1(pair, pair->dst_parent_abspath,
lock_src, lock_dst,
allow_mixed_revisions,
metadata_only,
ctx, iterpool));
}
svn_pool_destroy(iterpool);
return svn_error_trace(err);
}
/* Verify that the destinations stored in COPY_PAIRS are valid working copy
destinations and set pair->dst_parent_abspath and pair->base_name for each
item to the resulting location if they do */
static svn_error_t *
verify_wc_dsts(const apr_array_header_t *copy_pairs,
svn_boolean_t make_parents,
svn_boolean_t is_move,
svn_boolean_t metadata_only,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
int i;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
/* Check that DST does not exist, but its parent does */
for (i = 0; i < copy_pairs->nelts; i++)
{
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
svn_node_kind_t dst_kind, dst_parent_kind;
svn_pool_clear(iterpool);
/* If DST_PATH does not exist, then its basename will become a new
file or dir added to its parent (possibly an implicit '.').
Else, just error out. */
SVN_ERR(svn_wc_read_kind2(&dst_kind, ctx->wc_ctx,
pair->dst_abspath_or_url,
FALSE /* show_deleted */,
TRUE /* show_hidden */,
iterpool));
if (dst_kind != svn_node_none)
{
svn_boolean_t is_excluded;
svn_boolean_t is_server_excluded;
SVN_ERR(svn_wc__node_is_not_present(NULL, &is_excluded,
&is_server_excluded, ctx->wc_ctx,
pair->dst_abspath_or_url, FALSE,
iterpool));
if (is_excluded || is_server_excluded)
{
return svn_error_createf(
SVN_ERR_WC_OBSTRUCTED_UPDATE,
NULL, _("Path '%s' exists, but is excluded"),
svn_dirent_local_style(pair->dst_abspath_or_url, iterpool));
}
else
return svn_error_createf(
SVN_ERR_ENTRY_EXISTS, NULL,
_("Path '%s' already exists"),
svn_dirent_local_style(pair->dst_abspath_or_url,
scratch_pool));
}
/* Check that there is no unversioned obstruction */
if (metadata_only)
dst_kind = svn_node_none;
else
SVN_ERR(svn_io_check_path(pair->dst_abspath_or_url, &dst_kind,
iterpool));
if (dst_kind != svn_node_none)
{
if (is_move
&& copy_pairs->nelts == 1
&& strcmp(svn_dirent_dirname(pair->src_abspath_or_url, iterpool),
svn_dirent_dirname(pair->dst_abspath_or_url,
iterpool)) == 0)
{
const char *dst;
char *dst_apr;
apr_status_t apr_err;
/* We have a rename inside a directory, which might collide
just because the case insensivity of the filesystem makes
the source match the destination. */
SVN_ERR(svn_path_cstring_from_utf8(&dst,
pair->dst_abspath_or_url,
scratch_pool));
apr_err = apr_filepath_merge(&dst_apr, NULL, dst,
APR_FILEPATH_TRUENAME, iterpool);
if (!apr_err)
{
/* And now bring it back to our canonical format */
SVN_ERR(svn_path_cstring_to_utf8(&dst, dst_apr, iterpool));
dst = svn_dirent_canonicalize(dst, iterpool);
}
/* else: Don't report this error; just report the normal error */
if (!apr_err && strcmp(dst, pair->src_abspath_or_url) == 0)
{
/* Ok, we have a single case only rename. Get out of here */
svn_dirent_split(&pair->dst_parent_abspath, &pair->base_name,
pair->dst_abspath_or_url, result_pool);
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
}
return svn_error_createf(
SVN_ERR_ENTRY_EXISTS, NULL,
_("Path '%s' already exists as unversioned node"),
svn_dirent_local_style(pair->dst_abspath_or_url,
scratch_pool));
}
svn_dirent_split(&pair->dst_parent_abspath, &pair->base_name,
pair->dst_abspath_or_url, result_pool);
/* Make sure the destination parent is a directory and produce a clear
error message if it is not. */
SVN_ERR(svn_wc_read_kind2(&dst_parent_kind,
ctx->wc_ctx, pair->dst_parent_abspath,
FALSE, TRUE,
iterpool));
if (make_parents && dst_parent_kind == svn_node_none)
{
SVN_ERR(svn_client__make_local_parents(pair->dst_parent_abspath,
TRUE, ctx, iterpool));
}
else if (dst_parent_kind != svn_node_dir)
{
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("Path '%s' is not a directory"),
svn_dirent_local_style(
pair->dst_parent_abspath, scratch_pool));
}
SVN_ERR(svn_io_check_path(pair->dst_parent_abspath,
&dst_parent_kind, scratch_pool));
if (dst_parent_kind != svn_node_dir)
return svn_error_createf(SVN_ERR_WC_MISSING, NULL,
_("Path '%s' is not a directory"),
svn_dirent_local_style(
pair->dst_parent_abspath, scratch_pool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_error_t *
verify_wc_srcs_and_dsts(const apr_array_header_t *copy_pairs,
svn_boolean_t make_parents,
svn_boolean_t is_move,
svn_boolean_t metadata_only,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
int i;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
/* Check that all of our SRCs exist. */
for (i = 0; i < copy_pairs->nelts; i++)
{
svn_boolean_t deleted_ok;
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
svn_pool_clear(iterpool);
deleted_ok = (pair->src_peg_revision.kind == svn_opt_revision_base
|| pair->src_op_revision.kind == svn_opt_revision_base);
/* Verify that SRC_PATH exists. */
SVN_ERR(svn_wc_read_kind2(&pair->src_kind, ctx->wc_ctx,
pair->src_abspath_or_url,
deleted_ok, FALSE, iterpool));
if (pair->src_kind == svn_node_none)
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("Path '%s' does not exist"),
svn_dirent_local_style(
pair->src_abspath_or_url,
scratch_pool));
}
SVN_ERR(verify_wc_dsts(copy_pairs, make_parents, is_move, metadata_only, ctx,
result_pool, iterpool));
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Path-specific state used as part of path_driver_cb_baton. */
typedef struct path_driver_info_t
{
const char *src_url;
const char *src_path;
const char *dst_path;
svn_node_kind_t src_kind;
svn_revnum_t src_revnum;
svn_boolean_t resurrection;
svn_boolean_t dir_add;
svn_string_t *mergeinfo; /* the new mergeinfo for the target */
} path_driver_info_t;
/* The baton used with the path_driver_cb_func() callback for a copy
or move operation. */
struct path_driver_cb_baton
{
/* The editor (and its state) used to perform the operation. */
const svn_delta_editor_t *editor;
void *edit_baton;
/* A hash of path -> path_driver_info_t *'s. */
apr_hash_t *action_hash;
/* Whether the operation is a move or copy. */
svn_boolean_t is_move;
};
static svn_error_t *
path_driver_cb_func(void **dir_baton,
void *parent_baton,
void *callback_baton,
const char *path,
apr_pool_t *pool)
{
struct path_driver_cb_baton *cb_baton = callback_baton;
svn_boolean_t do_delete = FALSE, do_add = FALSE;
path_driver_info_t *path_info = svn_hash_gets(cb_baton->action_hash, path);
/* Initialize return value. */
*dir_baton = NULL;
/* This function should never get an empty PATH. We can neither
create nor delete the empty PATH, so if someone is calling us
with such, the code is just plain wrong. */
SVN_ERR_ASSERT(! svn_path_is_empty(path));
/* Check to see if we need to add the path as a directory. */
if (path_info->dir_add)
{
return cb_baton->editor->add_directory(path, parent_baton, NULL,
SVN_INVALID_REVNUM, pool,
dir_baton);
}
/* If this is a resurrection, we know the source and dest paths are
the same, and that our driver will only be calling us once. */
if (path_info->resurrection)
{
/* If this is a move, we do nothing. Otherwise, we do the copy. */
if (! cb_baton->is_move)
do_add = TRUE;
}
/* Not a resurrection. */
else
{
/* If this is a move, we check PATH to see if it is the source
or the destination of the move. */
if (cb_baton->is_move)
{
if (strcmp(path_info->src_path, path) == 0)
do_delete = TRUE;
else
do_add = TRUE;
}
/* Not a move? This must just be the copy addition. */
else
{
do_add = TRUE;
}
}
if (do_delete)
{
SVN_ERR(cb_baton->editor->delete_entry(path, SVN_INVALID_REVNUM,
parent_baton, pool));
}
if (do_add)
{
SVN_ERR(svn_path_check_valid(path, pool));
if (path_info->src_kind == svn_node_file)
{
void *file_baton;
SVN_ERR(cb_baton->editor->add_file(path, parent_baton,
path_info->src_url,
path_info->src_revnum,
pool, &file_baton));
if (path_info->mergeinfo)
SVN_ERR(cb_baton->editor->change_file_prop(file_baton,
SVN_PROP_MERGEINFO,
path_info->mergeinfo,
pool));
SVN_ERR(cb_baton->editor->close_file(file_baton, NULL, pool));
}
else
{
SVN_ERR(cb_baton->editor->add_directory(path, parent_baton,
path_info->src_url,
path_info->src_revnum,
pool, dir_baton));
if (path_info->mergeinfo)
SVN_ERR(cb_baton->editor->change_dir_prop(*dir_baton,
SVN_PROP_MERGEINFO,
path_info->mergeinfo,
pool));
}
}
return SVN_NO_ERROR;
}
/* Starting with the path DIR relative to the RA_SESSION's session
URL, work up through DIR's parents until an existing node is found.
Push each nonexistent path onto the array NEW_DIRS, allocating in
POOL. Raise an error if the existing node is not a directory.
### Multiple requests for HEAD (SVN_INVALID_REVNUM) make this
### implementation susceptible to race conditions. */
static svn_error_t *
find_absent_parents1(svn_ra_session_t *ra_session,
const char *dir,
apr_array_header_t *new_dirs,
apr_pool_t *pool)
{
svn_node_kind_t kind;
apr_pool_t *iterpool = svn_pool_create(pool);
SVN_ERR(svn_ra_check_path(ra_session, dir, SVN_INVALID_REVNUM, &kind,
iterpool));
while (kind == svn_node_none)
{
svn_pool_clear(iterpool);
APR_ARRAY_PUSH(new_dirs, const char *) = dir;
dir = svn_dirent_dirname(dir, pool);
SVN_ERR(svn_ra_check_path(ra_session, dir, SVN_INVALID_REVNUM,
&kind, iterpool));
}
if (kind != svn_node_dir)
return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
_("Path '%s' already exists, but is not a "
"directory"), dir);
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Starting with the URL *TOP_DST_URL which is also the root of
RA_SESSION, work up through its parents until an existing node is
found. Push each nonexistent URL onto the array NEW_DIRS,
allocating in POOL. Raise an error if the existing node is not a
directory.
Set *TOP_DST_URL and the RA session's root to the existing node's URL.
### Multiple requests for HEAD (SVN_INVALID_REVNUM) make this
### implementation susceptible to race conditions. */
static svn_error_t *
find_absent_parents2(svn_ra_session_t *ra_session,
const char **top_dst_url,
apr_array_header_t *new_dirs,
apr_pool_t *pool)
{
const char *root_url = *top_dst_url;
svn_node_kind_t kind;
SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM, &kind,
pool));
while (kind == svn_node_none)
{
APR_ARRAY_PUSH(new_dirs, const char *) = root_url;
root_url = svn_uri_dirname(root_url, pool);
SVN_ERR(svn_ra_reparent(ra_session, root_url, pool));
SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM, &kind,
pool));
}
if (kind != svn_node_dir)
return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
_("Path '%s' already exists, but is not a directory"),
root_url);
*top_dst_url = root_url;
return SVN_NO_ERROR;
}
static svn_error_t *
repos_to_repos_copy(const apr_array_header_t *copy_pairs,
svn_boolean_t make_parents,
const apr_hash_t *revprop_table,
svn_commit_callback2_t commit_callback,
void *commit_baton,
svn_client_ctx_t *ctx,
svn_boolean_t is_move,
apr_pool_t *pool)
{
svn_error_t *err;
apr_array_header_t *paths = apr_array_make(pool, 2 * copy_pairs->nelts,
sizeof(const char *));
apr_hash_t *action_hash = apr_hash_make(pool);
apr_array_header_t *path_infos;
const char *top_url, *top_url_all, *top_url_dst;
const char *message, *repos_root;
svn_ra_session_t *ra_session = NULL;
const svn_delta_editor_t *editor;
void *edit_baton;
struct path_driver_cb_baton cb_baton;
apr_array_header_t *new_dirs = NULL;
apr_hash_t *commit_revprops;
int i;
svn_client__copy_pair_t *first_pair =
APR_ARRAY_IDX(copy_pairs, 0, svn_client__copy_pair_t *);
/* Open an RA session to the first copy pair's destination. We'll
be verifying that every one of our copy source and destination
URLs is or is beneath this sucker's repository root URL as a form
of a cheap(ish) sanity check. */
SVN_ERR(svn_client_open_ra_session2(&ra_session,
first_pair->src_abspath_or_url, NULL,
ctx, pool, pool));
SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root, pool));
/* Verify that sources and destinations are all at or under
REPOS_ROOT. While here, create a path_info struct for each
src/dst pair and initialize portions of it with normalized source
location information. */
path_infos = apr_array_make(pool, copy_pairs->nelts,
sizeof(path_driver_info_t *));
for (i = 0; i < copy_pairs->nelts; i++)
{
path_driver_info_t *info = apr_pcalloc(pool, sizeof(*info));
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
apr_hash_t *mergeinfo;
/* Are the source and destination URLs at or under REPOS_ROOT? */
if (! (svn_uri__is_ancestor(repos_root, pair->src_abspath_or_url)
&& svn_uri__is_ancestor(repos_root, pair->dst_abspath_or_url)))
return svn_error_create
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Source and destination URLs appear not to point to the "
"same repository."));
/* Run the history function to get the source's URL and revnum in the
operational revision. */
SVN_ERR(svn_ra_reparent(ra_session, pair->src_abspath_or_url, pool));
SVN_ERR(svn_client__repos_locations(&pair->src_abspath_or_url,
&pair->src_revnum,
NULL, NULL,
ra_session,
pair->src_abspath_or_url,
&pair->src_peg_revision,
&pair->src_op_revision, NULL,
ctx, pool));
/* Go ahead and grab mergeinfo from the source, too. */
SVN_ERR(svn_ra_reparent(ra_session, pair->src_abspath_or_url, pool));
SVN_ERR(svn_client__get_repos_mergeinfo(
&mergeinfo, ra_session,
pair->src_abspath_or_url, pair->src_revnum,
svn_mergeinfo_inherited, TRUE /*squelch_incapable*/, pool));
if (mergeinfo)
SVN_ERR(svn_mergeinfo_to_string(&info->mergeinfo, mergeinfo, pool));
/* Plop an INFO structure onto our array thereof. */
info->src_url = pair->src_abspath_or_url;
info->src_revnum = pair->src_revnum;
info->resurrection = FALSE;
APR_ARRAY_PUSH(path_infos, path_driver_info_t *) = info;
}
/* If this is a move, we have to open our session to the longest
path common to all SRC_URLS and DST_URLS in the repository so we
can do existence checks on all paths, and so we can operate on
all paths in the case of a move. But if this is *not* a move,
then opening our session at the longest path common to sources
*and* destinations might be an optimization when the user is
authorized to access all that stuff, but could cause the
operation to fail altogether otherwise. See issue #3242. */
SVN_ERR(get_copy_pair_ancestors(copy_pairs, NULL, &top_url_dst, &top_url_all,
pool));
top_url = is_move ? top_url_all : top_url_dst;
/* Check each src/dst pair for resurrection, and verify that TOP_URL
is anchored high enough to cover all the editor_t activities
required for this operation. */
for (i = 0; i < copy_pairs->nelts; i++)
{
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
path_driver_info_t *info = APR_ARRAY_IDX(path_infos, i,
path_driver_info_t *);
/* Source and destination are the same? It's a resurrection. */
if (strcmp(pair->src_abspath_or_url, pair->dst_abspath_or_url) == 0)
info->resurrection = TRUE;
/* We need to add each dst_URL, and (in a move) we'll need to
delete each src_URL. Our selection of TOP_URL so far ensures
that all our destination URLs (and source URLs, for moves)
are at least as deep as TOP_URL, but we need to make sure
that TOP_URL is an *ancestor* of all our to-be-edited paths.
Issue #683 is demonstrates this scenario. If you're
resurrecting a deleted item like this: 'svn cp -rN src_URL
dst_URL', then src_URL == dst_URL == top_url. In this
situation, we want to open an RA session to be at least the
*parent* of all three. */
if ((strcmp(top_url, pair->dst_abspath_or_url) == 0)
&& (strcmp(top_url, repos_root) != 0))
{
top_url = svn_uri_dirname(top_url, pool);
}
if (is_move
&& (strcmp(top_url, pair->src_abspath_or_url) == 0)
&& (strcmp(top_url, repos_root) != 0))
{
top_url = svn_uri_dirname(top_url, pool);
}
}
/* Point the RA session to our current TOP_URL. */
SVN_ERR(svn_ra_reparent(ra_session, top_url, pool));
/* If we're allowed to create nonexistent parent directories of our
destinations, then make a list in NEW_DIRS of the parent
directories of the destination that don't yet exist. */
if (make_parents)
{
new_dirs = apr_array_make(pool, 0, sizeof(const char *));
/* If this is a move, TOP_URL is at least the common ancestor of
all the paths (sources and destinations) involved. Assuming
the sources exist (which is fair, because if they don't, this
whole operation will fail anyway), TOP_URL must also exist.
So it's the paths between TOP_URL and the destinations which
we have to check for existence. But here, we take advantage
of the knowledge of our caller. We know that if there are
multiple copy/move operations being requested, then the
destinations of the copies/moves will all be siblings of one
another. Therefore, we need only to check for the
nonexistent paths between TOP_URL and *one* of our
destinations to find nonexistent parents of all of them. */
if (is_move)
{
/* Imagine a situation where the user tries to copy an
existing source directory to nonexistent directory with
--parents options specified:
svn copy --parents URL/src URL/dst
where src exists and dst does not. If the dirname of the
destination path is equal to TOP_URL,
do not try to add dst to the NEW_DIRS list since it
will be added to the commit items array later in this
function. */
const char *dir = svn_uri_skip_ancestor(
top_url,
svn_uri_dirname(first_pair->dst_abspath_or_url,
pool),
pool);
if (dir && *dir)
SVN_ERR(find_absent_parents1(ra_session, dir, new_dirs, pool));
}
/* If, however, this is *not* a move, TOP_URL only points to the
common ancestor of our destination path(s), or possibly one
level higher. We'll need to do an existence crawl toward the
root of the repository, starting with one of our destinations
(see "... take advantage of the knowledge of our caller ..."
above), and possibly adjusting TOP_URL as we go. */
else
{
apr_array_header_t *new_urls =
apr_array_make(pool, 0, sizeof(const char *));
SVN_ERR(find_absent_parents2(ra_session, &top_url, new_urls, pool));
/* Convert absolute URLs into relpaths relative to TOP_URL. */
for (i = 0; i < new_urls->nelts; i++)
{
const char *new_url = APR_ARRAY_IDX(new_urls, i, const char *);
const char *dir = svn_uri_skip_ancestor(top_url, new_url, pool);
APR_ARRAY_PUSH(new_dirs, const char *) = dir;
}
}
}
/* For each src/dst pair, check to see if that SRC_URL is a child of
the DST_URL (excepting the case where DST_URL is the repo root).
If it is, and the parent of DST_URL is the current TOP_URL, then we
need to reparent the session one directory higher, the parent of
the DST_URL. */
for (i = 0; i < copy_pairs->nelts; i++)
{
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
path_driver_info_t *info = APR_ARRAY_IDX(path_infos, i,
path_driver_info_t *);
const char *relpath = svn_uri_skip_ancestor(pair->dst_abspath_or_url,
pair->src_abspath_or_url,
pool);
if ((strcmp(pair->dst_abspath_or_url, repos_root) != 0)
&& (relpath != NULL && *relpath != '\0'))
{
info->resurrection = TRUE;
- top_url = svn_uri_dirname(top_url, pool);
+ top_url = svn_uri_get_longest_ancestor(
+ top_url,
+ svn_uri_dirname(pair->dst_abspath_or_url, pool),
+ pool);
SVN_ERR(svn_ra_reparent(ra_session, top_url, pool));
}
}
/* Get the portions of the SRC and DST URLs that are relative to
TOP_URL (URI-decoding them while we're at it), verify that the
source exists and the proposed destination does not, and toss
what we've learned into the INFO array. (For copies -- that is,
non-moves -- the relative source URL NULL because it isn't a
child of the TOP_URL at all. That's okay, we'll deal with
it.) */
for (i = 0; i < copy_pairs->nelts; i++)
{
svn_client__copy_pair_t *pair =
APR_ARRAY_IDX(copy_pairs, i, svn_client__copy_pair_t *);
path_driver_info_t *info =
APR_ARRAY_IDX(path_infos, i, path_driver_info_t *);
svn_node_kind_t dst_kind;
const char *src_rel, *dst_rel;
src_rel = svn_uri_skip_ancestor(top_url, pair->src_abspath_or_url, pool);
if (src_rel)
{
SVN_ERR(svn_ra_check_path(ra_session, src_rel, pair->src_revnum,
&info->src_kind, pool));
}
else
{
const char *old_url;
src_rel = NULL;
SVN_ERR_ASSERT(! is_move);
SVN_ERR(svn_client__ensure_ra_session_url(&old_url, ra_session,
pair->src_abspath_or_url,
pool));
SVN_ERR(svn_ra_check_path(ra_session, "", pair->src_revnum,
&info->src_kind, pool));
SVN_ERR(svn_ra_reparent(ra_session, old_url, pool));
}
if (info->src_kind == svn_node_none)
return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
_("Path '%s' does not exist in revision %ld"),
pair->src_abspath_or_url, pair->src_revnum);
/* Figure out the basename that will result from this operation,
and ensure that we aren't trying to overwrite existing paths. */
dst_rel = svn_uri_skip_ancestor(top_url, pair->dst_abspath_or_url, pool);
SVN_ERR(svn_ra_check_path(ra_session, dst_rel, SVN_INVALID_REVNUM,
&dst_kind, pool));
if (dst_kind != svn_node_none)
return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
_("Path '%s' already exists"), dst_rel);
/* More info for our INFO structure. */
info->src_path = src_rel;
info->dst_path = dst_rel;
svn_hash_sets(action_hash, info->dst_path, info);
if (is_move && (! info->resurrection))
svn_hash_sets(action_hash, info->src_path, info);
}
if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
{
/* Produce a list of new paths to add, and provide it to the
mechanism used to acquire a log message. */
svn_client_commit_item3_t *item;
const char *tmp_file;
apr_array_header_t *commit_items
= apr_array_make(pool, 2 * copy_pairs->nelts, sizeof(item));
/* Add any intermediate directories to the message */
if (make_parents)
{
for (i = 0; i < new_dirs->nelts; i++)
{
const char *relpath = APR_ARRAY_IDX(new_dirs, i, const char *);
item = svn_client_commit_item3_create(pool);
item->url = svn_path_url_add_component2(top_url, relpath, pool);
item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
}
}
for (i = 0; i < path_infos->nelts; i++)
{
path_driver_info_t *info = APR_ARRAY_IDX(path_infos, i,
path_driver_info_t *);
item = svn_client_commit_item3_create(pool);
item->url = svn_path_url_add_component2(top_url, info->dst_path,
pool);
item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
if (is_move && (! info->resurrection))
{
item = apr_pcalloc(pool, sizeof(*item));
item->url = svn_path_url_add_component2(top_url, info->src_path,
pool);
item->state_flags = SVN_CLIENT_COMMIT_ITEM_DELETE;
APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
}
}
SVN_ERR(svn_client__get_log_msg(&message, &tmp_file, commit_items,
ctx, pool));
if (! message)
return SVN_NO_ERROR;
}
else
message = "";
/* Setup our PATHS for the path-based editor drive. */
/* First any intermediate directories. */
if (make_parents)
{
for (i = 0; i < new_dirs->nelts; i++)
{
const char *relpath = APR_ARRAY_IDX(new_dirs, i, const char *);
path_driver_info_t *info = apr_pcalloc(pool, sizeof(*info));
info->dst_path = relpath;
info->dir_add = TRUE;
APR_ARRAY_PUSH(paths, const char *) = relpath;
svn_hash_sets(action_hash, relpath, info);
}
}
/* Then our copy destinations and move sources (if any). */
for (i = 0; i < path_infos->nelts; i++)
{
path_driver_info_t *info = APR_ARRAY_IDX(path_infos, i,
path_driver_info_t *);
APR_ARRAY_PUSH(paths, const char *) = info->dst_path;
if (is_move && (! info->resurrection))
APR_ARRAY_PUSH(paths, const char *) = info->src_path;
}
SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table,
message, ctx, pool));
/* Fetch RA commit editor. */
SVN_ERR(svn_ra__register_editor_shim_callbacks(ra_session,
svn_client__get_shim_callbacks(ctx->wc_ctx,
NULL, pool)));
SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
commit_revprops,
commit_callback,
commit_baton,
NULL, TRUE, /* No lock tokens */
pool));
/* Setup the callback baton. */
cb_baton.editor = editor;
cb_baton.edit_baton = edit_baton;
cb_baton.action_hash = action_hash;
cb_baton.is_move = is_move;
/* Call the path-based editor driver. */
err = svn_delta_path_driver2(editor, edit_baton, paths, TRUE,
path_driver_cb_func, &cb_baton, pool);
if (err)
{
/* At least try to abort the edit (and fs txn) before throwing err. */
return svn_error_compose_create(
err,
editor->abort_edit(edit_baton, pool));
}
/* Close the edit. */
return svn_error_trace(editor->close_edit(edit_baton, pool));
}
/* Baton for check_url_kind */
struct check_url_kind_baton
{
svn_ra_session_t *session;
const char *repos_root_url;
svn_boolean_t should_reparent;
};
/* Implements svn_client__check_url_kind_t for wc_to_repos_copy */
static svn_error_t *
check_url_kind(void *baton,
svn_node_kind_t *kind,
const char *url,
svn_revnum_t revision,
apr_pool_t *scratch_pool)
{
struct check_url_kind_baton *cukb = baton;
/* If we don't have a session or can't use the session, get one */
if (!svn_uri__is_ancestor(cukb->repos_root_url, url))
*kind = svn_node_none;
else
{
cukb->should_reparent = TRUE;
SVN_ERR(svn_ra_reparent(cukb->session, url, scratch_pool));
SVN_ERR(svn_ra_check_path(cukb->session, "", revision,
kind, scratch_pool));
}
return SVN_NO_ERROR;
}
/* ### Copy ...
* COMMIT_INFO_P is ...
* COPY_PAIRS is ... such that each 'src_abspath_or_url' is a local abspath
* and each 'dst_abspath_or_url' is a URL.
* MAKE_PARENTS is ...
* REVPROP_TABLE is ...
* CTX is ... */
static svn_error_t *
wc_to_repos_copy(const apr_array_header_t *copy_pairs,
svn_boolean_t make_parents,
const apr_hash_t *revprop_table,
svn_commit_callback2_t commit_callback,
void *commit_baton,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
const char *message;
const char *top_src_path, *top_dst_url;
struct check_url_kind_baton cukb;
const char *top_src_abspath;
svn_ra_session_t *ra_session;
const svn_delta_editor_t *editor;
apr_hash_t *relpath_map = NULL;
void *edit_baton;
svn_client__committables_t *committables;
apr_array_header_t *commit_items;
apr_pool_t *iterpool;
apr_array_header_t *new_dirs = NULL;
apr_hash_t *commit_revprops;
svn_client__copy_pair_t *first_pair;
apr_pool_t *session_pool = svn_pool_create(scratch_pool);
int i;
/* Find the common root of all the source paths */
SVN_ERR(get_copy_pair_ancestors(copy_pairs, &top_src_path, NULL, NULL,
scratch_pool));
/* Do we need to lock the working copy? 1.6 didn't take a write
lock, but what happens if the working copy changes during the copy
operation? */
iterpool = svn_pool_create(scratch_pool);
/* Determine the longest common ancestor for the destinations, and open an RA
session to that location. */
/* ### But why start by getting the _parent_ of the first one? */
/* --- That works because multiple destinations always point to the same
* directory. I'm rather wondering why we need to find a common
* destination parent here at all, instead of simply getting
* top_dst_url from get_copy_pair_ancestors() above?
* It looks like the entire block of code hanging off this comment
* is redundant. */
first_pair = APR_ARRAY_IDX(copy_pairs, 0, svn_client__copy_pair_t *);
top_dst_url = svn_uri_dirname(first_pair->dst_abspath_or_url, scratch_pool);
for (i = 1; i < copy_pairs->nelts; i++)
{
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
top_dst_url = svn_uri_get_longest_ancestor(top_dst_url,
pair->dst_abspath_or_url,
scratch_pool);
}
SVN_ERR(svn_dirent_get_absolute(&top_src_abspath, top_src_path, scratch_pool));
/* Open a session to help while determining the exact targets */
SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, top_dst_url,
top_src_abspath, NULL,
FALSE /* write_dav_props */,
TRUE /* read_dav_props */,
ctx,
session_pool, session_pool));
/* If requested, determine the nearest existing parent of the destination,
and reparent the ra session there. */
if (make_parents)
{
new_dirs = apr_array_make(scratch_pool, 0, sizeof(const char *));
SVN_ERR(find_absent_parents2(ra_session, &top_dst_url, new_dirs,
scratch_pool));
}
/* Figure out the basename that will result from each copy and check to make
sure it doesn't exist already. */
for (i = 0; i < copy_pairs->nelts; i++)
{
svn_node_kind_t dst_kind;
const char *dst_rel;
svn_client__copy_pair_t *pair =
APR_ARRAY_IDX(copy_pairs, i, svn_client__copy_pair_t *);
svn_pool_clear(iterpool);
dst_rel = svn_uri_skip_ancestor(top_dst_url, pair->dst_abspath_or_url,
iterpool);
SVN_ERR(svn_ra_check_path(ra_session, dst_rel, SVN_INVALID_REVNUM,
&dst_kind, iterpool));
if (dst_kind != svn_node_none)
{
return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
_("Path '%s' already exists"),
pair->dst_abspath_or_url);
}
}
if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
{
/* Produce a list of new paths to add, and provide it to the
mechanism used to acquire a log message. */
svn_client_commit_item3_t *item;
const char *tmp_file;
commit_items = apr_array_make(scratch_pool, copy_pairs->nelts,
sizeof(item));
/* Add any intermediate directories to the message */
if (make_parents)
{
for (i = 0; i < new_dirs->nelts; i++)
{
const char *url = APR_ARRAY_IDX(new_dirs, i, const char *);
item = svn_client_commit_item3_create(scratch_pool);
item->url = url;
item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
}
}
for (i = 0; i < copy_pairs->nelts; i++)
{
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
item = svn_client_commit_item3_create(scratch_pool);
item->url = pair->dst_abspath_or_url;
item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
}
SVN_ERR(svn_client__get_log_msg(&message, &tmp_file, commit_items,
ctx, scratch_pool));
if (! message)
{
svn_pool_destroy(iterpool);
svn_pool_destroy(session_pool);
return SVN_NO_ERROR;
}
}
else
message = "";
cukb.session = ra_session;
SVN_ERR(svn_ra_get_repos_root2(ra_session, &cukb.repos_root_url, session_pool));
cukb.should_reparent = FALSE;
/* Crawl the working copy for commit items. */
/* ### TODO: Pass check_url_func for issue #3314 handling */
SVN_ERR(svn_client__get_copy_committables(&committables,
copy_pairs,
check_url_kind, &cukb,
ctx, scratch_pool, iterpool));
/* The committables are keyed by the repository root */
commit_items = svn_hash_gets(committables->by_repository,
cukb.repos_root_url);
SVN_ERR_ASSERT(commit_items != NULL);
if (cukb.should_reparent)
SVN_ERR(svn_ra_reparent(ra_session, top_dst_url, session_pool));
/* If we are creating intermediate directories, tack them onto the list
of committables. */
if (make_parents)
{
for (i = 0; i < new_dirs->nelts; i++)
{
const char *url = APR_ARRAY_IDX(new_dirs, i, const char *);
svn_client_commit_item3_t *item;
item = svn_client_commit_item3_create(scratch_pool);
item->url = url;
item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
item->incoming_prop_changes = apr_array_make(scratch_pool, 1,
sizeof(svn_prop_t *));
APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
}
}
/* ### TODO: This extra loop would be unnecessary if this code lived
### in svn_client__get_copy_committables(), which is incidentally
### only used above (so should really be in this source file). */
for (i = 0; i < copy_pairs->nelts; i++)
{
apr_hash_t *mergeinfo, *wc_mergeinfo;
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
svn_client_commit_item3_t *item =
APR_ARRAY_IDX(commit_items, i, svn_client_commit_item3_t *);
svn_client__pathrev_t *src_origin;
svn_pool_clear(iterpool);
SVN_ERR(svn_client__wc_node_get_origin(&src_origin,
pair->src_abspath_or_url,
ctx, iterpool, iterpool));
/* Set the mergeinfo for the destination to the combined merge
info known to the WC and the repository. */
item->outgoing_prop_changes = apr_array_make(scratch_pool, 1,
sizeof(svn_prop_t *));
/* Repository mergeinfo (or NULL if it's locally added)... */
if (src_origin)
SVN_ERR(svn_client__get_repos_mergeinfo(
&mergeinfo, ra_session, src_origin->url, src_origin->rev,
svn_mergeinfo_inherited, TRUE /*sqelch_inc.*/, iterpool));
else
mergeinfo = NULL;
/* ... and WC mergeinfo. */
SVN_ERR(svn_client__parse_mergeinfo(&wc_mergeinfo, ctx->wc_ctx,
pair->src_abspath_or_url,
iterpool, iterpool));
if (wc_mergeinfo && mergeinfo)
SVN_ERR(svn_mergeinfo_merge2(mergeinfo, wc_mergeinfo, iterpool,
iterpool));
else if (! mergeinfo)
mergeinfo = wc_mergeinfo;
if (mergeinfo)
{
/* Push a mergeinfo prop representing MERGEINFO onto the
* OUTGOING_PROP_CHANGES array. */
svn_prop_t *mergeinfo_prop
= apr_palloc(item->outgoing_prop_changes->pool,
sizeof(svn_prop_t));
svn_string_t *prop_value;
SVN_ERR(svn_mergeinfo_to_string(&prop_value, mergeinfo,
item->outgoing_prop_changes->pool));
mergeinfo_prop->name = SVN_PROP_MERGEINFO;
mergeinfo_prop->value = prop_value;
APR_ARRAY_PUSH(item->outgoing_prop_changes, svn_prop_t *)
= mergeinfo_prop;
}
}
/* Sort and condense our COMMIT_ITEMS. */
SVN_ERR(svn_client__condense_commit_items(&top_dst_url,
commit_items, scratch_pool));
#ifdef ENABLE_EV2_SHIMS
if (commit_items)
{
relpath_map = apr_hash_make(pool);
for (i = 0; i < commit_items->nelts; i++)
{
svn_client_commit_item3_t *item = APR_ARRAY_IDX(commit_items, i,
svn_client_commit_item3_t *);
const char *relpath;
if (!item->path)
continue;
svn_pool_clear(iterpool);
SVN_ERR(svn_wc__node_get_origin(NULL, NULL, &relpath, NULL, NULL, NULL,
ctx->wc_ctx, item->path, FALSE,
scratch_pool, iterpool));
if (relpath)
svn_hash_sets(relpath_map, relpath, item->path);
}
}
#endif
/* Close the initial session, to reopen a new session with commit handling */
svn_pool_clear(session_pool);
/* Open a new RA session to DST_URL. */
SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, top_dst_url,
NULL, commit_items,
FALSE, FALSE, ctx,
session_pool, session_pool));
SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table,
message, ctx, session_pool));
/* Fetch RA commit editor. */
SVN_ERR(svn_ra__register_editor_shim_callbacks(ra_session,
svn_client__get_shim_callbacks(ctx->wc_ctx, relpath_map,
session_pool)));
SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
commit_revprops,
commit_callback,
commit_baton, NULL,
TRUE, /* No lock tokens */
session_pool));
/* Perform the commit. */
SVN_ERR_W(svn_client__do_commit(top_dst_url, commit_items,
editor, edit_baton,
0, /* ### any notify_path_offset needed? */
NULL, ctx, session_pool, session_pool),
_("Commit failed (details follow):"));
svn_pool_destroy(iterpool);
svn_pool_destroy(session_pool);
return SVN_NO_ERROR;
}
/* A baton for notification_adjust_func(). */
struct notification_adjust_baton
{
svn_wc_notify_func2_t inner_func;
void *inner_baton;
const char *checkout_abspath;
const char *final_abspath;
};
/* A svn_wc_notify_func2_t function that wraps BATON->inner_func (whose
* baton is BATON->inner_baton) and adjusts the notification paths that
* start with BATON->checkout_abspath to start instead with
* BATON->final_abspath. */
static void
notification_adjust_func(void *baton,
const svn_wc_notify_t *notify,
apr_pool_t *pool)
{
struct notification_adjust_baton *nb = baton;
svn_wc_notify_t *inner_notify = svn_wc_dup_notify(notify, pool);
const char *relpath;
relpath = svn_dirent_skip_ancestor(nb->checkout_abspath, notify->path);
inner_notify->path = svn_dirent_join(nb->final_abspath, relpath, pool);
if (nb->inner_func)
nb->inner_func(nb->inner_baton, inner_notify, pool);
}
/* Peform each individual copy operation for a repos -> wc copy. A
helper for repos_to_wc_copy().
Resolve PAIR->src_revnum to a real revision number if it isn't already. */
static svn_error_t *
repos_to_wc_copy_single(svn_boolean_t *timestamp_sleep,
svn_client__copy_pair_t *pair,
svn_boolean_t same_repositories,
svn_boolean_t ignore_externals,
svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
apr_hash_t *src_mergeinfo;
const char *dst_abspath = pair->dst_abspath_or_url;
SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
if (!same_repositories && ctx->notify_func2)
{
svn_wc_notify_t *notify;
notify = svn_wc_create_notify_url(
pair->src_abspath_or_url,
svn_wc_notify_foreign_copy_begin,
pool);
notify->kind = pair->src_kind;
ctx->notify_func2(ctx->notify_baton2, notify, pool);
/* Allow a theoretical cancel to get through. */
if (ctx->cancel_func)
SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
}
if (pair->src_kind == svn_node_dir)
{
if (same_repositories)
{
svn_boolean_t sleep_needed = FALSE;
const char *tmpdir_abspath, *tmp_abspath;
/* Find a temporary location in which to check out the copy source. */
SVN_ERR(svn_wc__get_tmpdir(&tmpdir_abspath, ctx->wc_ctx, dst_abspath,
pool, pool));
SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_abspath, tmpdir_abspath,
svn_io_file_del_on_close, pool, pool));
/* Make a new checkout of the requested source. While doing so,
* resolve pair->src_revnum to an actual revision number in case it
* was until now 'invalid' meaning 'head'. Ask this function not to
* sleep for timestamps, by passing a sleep_needed output param.
* Send notifications for all nodes except the root node, and adjust
* them to refer to the destination rather than this temporary path. */
{
svn_wc_notify_func2_t old_notify_func2 = ctx->notify_func2;
void *old_notify_baton2 = ctx->notify_baton2;
struct notification_adjust_baton nb;
svn_error_t *err;
nb.inner_func = ctx->notify_func2;
nb.inner_baton = ctx->notify_baton2;
nb.checkout_abspath = tmp_abspath;
nb.final_abspath = dst_abspath;
ctx->notify_func2 = notification_adjust_func;
ctx->notify_baton2 = &nb;
err = svn_client__checkout_internal(&pair->src_revnum,
pair->src_original,
tmp_abspath,
&pair->src_peg_revision,
&pair->src_op_revision,
svn_depth_infinity,
ignore_externals, FALSE,
&sleep_needed, ctx, pool);
ctx->notify_func2 = old_notify_func2;
ctx->notify_baton2 = old_notify_baton2;
SVN_ERR(err);
}
*timestamp_sleep = TRUE;
/* Schedule dst_path for addition in parent, with copy history.
Don't send any notification here.
Then remove the temporary checkout's .svn dir in preparation for
moving the rest of it into the final destination. */
SVN_ERR(svn_wc_copy3(ctx->wc_ctx, tmp_abspath, dst_abspath,
TRUE /* metadata_only */,
ctx->cancel_func, ctx->cancel_baton,
NULL, NULL, pool));
SVN_ERR(svn_wc__acquire_write_lock(NULL, ctx->wc_ctx, tmp_abspath,
FALSE, pool, pool));
SVN_ERR(svn_wc_remove_from_revision_control2(ctx->wc_ctx,
tmp_abspath,
FALSE, FALSE,
ctx->cancel_func,
ctx->cancel_baton,
pool));
/* Move the temporary disk tree into place. */
SVN_ERR(svn_io_file_rename(tmp_abspath, dst_abspath, pool));
}
else
{
*timestamp_sleep = TRUE;
SVN_ERR(svn_client__copy_foreign(pair->src_abspath_or_url,
dst_abspath,
&pair->src_peg_revision,
&pair->src_op_revision,
svn_depth_infinity,
FALSE /* make_parents */,
TRUE /* already_locked */,
ctx, pool));
return SVN_NO_ERROR;
}
} /* end directory case */
else if (pair->src_kind == svn_node_file)
{
apr_hash_t *new_props;
const char *src_rel;
svn_stream_t *new_base_contents = svn_stream_buffered(pool);
SVN_ERR(svn_ra_get_path_relative_to_session(ra_session, &src_rel,
pair->src_abspath_or_url,
pool));
/* Fetch the file content. While doing so, resolve pair->src_revnum
* to an actual revision number if it's 'invalid' meaning 'head'. */
SVN_ERR(svn_ra_get_file(ra_session, src_rel, pair->src_revnum,
new_base_contents,
&pair->src_revnum, &new_props, pool));
if (new_props && ! same_repositories)
svn_hash_sets(new_props, SVN_PROP_MERGEINFO, NULL);
*timestamp_sleep = TRUE;
SVN_ERR(svn_wc_add_repos_file4(
ctx->wc_ctx, dst_abspath,
new_base_contents, NULL, new_props, NULL,
same_repositories ? pair->src_abspath_or_url : NULL,
same_repositories ? pair->src_revnum : SVN_INVALID_REVNUM,
ctx->cancel_func, ctx->cancel_baton,
pool));
}
/* Record the implied mergeinfo (before the notification callback
is invoked for the root node). */
SVN_ERR(svn_client__get_repos_mergeinfo(
&src_mergeinfo, ra_session,
pair->src_abspath_or_url, pair->src_revnum,
svn_mergeinfo_inherited, TRUE /*squelch_incapable*/, pool));
SVN_ERR(extend_wc_mergeinfo(dst_abspath, src_mergeinfo, ctx, pool));
/* Do our own notification for the root node, even if we could possibly
have delegated it. See also issue #1552.
### Maybe this notification should mention the mergeinfo change. */
if (ctx->notify_func2)
{
svn_wc_notify_t *notify = svn_wc_create_notify(
dst_abspath, svn_wc_notify_add, pool);
notify->kind = pair->src_kind;
(*ctx->notify_func2)(ctx->notify_baton2, notify, pool);
}
return SVN_NO_ERROR;
}
static svn_error_t *
repos_to_wc_copy_locked(svn_boolean_t *timestamp_sleep,
const apr_array_header_t *copy_pairs,
const char *top_dst_path,
svn_boolean_t ignore_externals,
svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
int i;
svn_boolean_t same_repositories;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
/* We've already checked for physical obstruction by a working file.
But there could also be logical obstruction by an entry whose
working file happens to be missing.*/
SVN_ERR(verify_wc_dsts(copy_pairs, FALSE, FALSE, FALSE /* metadata_only */,
ctx, scratch_pool, iterpool));
/* Decide whether the two repositories are the same or not. */
{
svn_error_t *src_err, *dst_err;
const char *parent;
const char *parent_abspath;
const char *src_uuid, *dst_uuid;
/* Get the repository uuid of SRC_URL */
src_err = svn_ra_get_uuid2(ra_session, &src_uuid, iterpool);
if (src_err && src_err->apr_err != SVN_ERR_RA_NO_REPOS_UUID)
return svn_error_trace(src_err);
/* Get repository uuid of dst's parent directory, since dst may
not exist. ### TODO: we should probably walk up the wc here,
in case the parent dir has an imaginary URL. */
if (copy_pairs->nelts == 1)
parent = svn_dirent_dirname(top_dst_path, scratch_pool);
else
parent = top_dst_path;
SVN_ERR(svn_dirent_get_absolute(&parent_abspath, parent, scratch_pool));
dst_err = svn_client_get_repos_root(NULL /* root_url */, &dst_uuid,
parent_abspath, ctx,
iterpool, iterpool);
if (dst_err && dst_err->apr_err != SVN_ERR_RA_NO_REPOS_UUID)
return dst_err;
/* If either of the UUIDs are nonexistent, then at least one of
the repositories must be very old. Rather than punish the
user, just assume the repositories are different, so no
copy-history is attempted. */
if (src_err || dst_err || (! src_uuid) || (! dst_uuid))
same_repositories = FALSE;
else
same_repositories = (strcmp(src_uuid, dst_uuid) == 0);
}
/* Perform the move for each of the copy_pairs. */
for (i = 0; i < copy_pairs->nelts; i++)
{
/* Check for cancellation */
if (ctx->cancel_func)
SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
svn_pool_clear(iterpool);
SVN_ERR(repos_to_wc_copy_single(timestamp_sleep,
APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *),
same_repositories,
ignore_externals,
ra_session, ctx, iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_error_t *
repos_to_wc_copy(svn_boolean_t *timestamp_sleep,
const apr_array_header_t *copy_pairs,
svn_boolean_t make_parents,
svn_boolean_t ignore_externals,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
svn_ra_session_t *ra_session;
const char *top_src_url, *top_dst_path;
apr_pool_t *iterpool = svn_pool_create(pool);
const char *lock_abspath;
int i;
/* Get the real path for the source, based upon its peg revision. */
for (i = 0; i < copy_pairs->nelts; i++)
{
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
const char *src;
svn_pool_clear(iterpool);
SVN_ERR(svn_client__repos_locations(&src, &pair->src_revnum, NULL, NULL,
NULL,
pair->src_abspath_or_url,
&pair->src_peg_revision,
&pair->src_op_revision, NULL,
ctx, iterpool));
pair->src_original = pair->src_abspath_or_url;
pair->src_abspath_or_url = apr_pstrdup(pool, src);
}
SVN_ERR(get_copy_pair_ancestors(copy_pairs, &top_src_url, &top_dst_path,
NULL, pool));
lock_abspath = top_dst_path;
if (copy_pairs->nelts == 1)
{
top_src_url = svn_uri_dirname(top_src_url, pool);
lock_abspath = svn_dirent_dirname(top_dst_path, pool);
}
/* Open a repository session to the longest common src ancestor. We do not
(yet) have a working copy, so we don't have a corresponding path and
tempfiles cannot go into the admin area. */
SVN_ERR(svn_client_open_ra_session2(&ra_session, top_src_url, lock_abspath,
ctx, pool, pool));
/* Get the correct src path for the peg revision used, and verify that we
aren't overwriting an existing path. */
for (i = 0; i < copy_pairs->nelts; i++)
{
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
svn_node_kind_t dst_parent_kind, dst_kind;
const char *dst_parent;
const char *src_rel;
svn_pool_clear(iterpool);
/* Next, make sure that the path exists in the repository. */
SVN_ERR(svn_ra_get_path_relative_to_session(ra_session, &src_rel,
pair->src_abspath_or_url,
iterpool));
SVN_ERR(svn_ra_check_path(ra_session, src_rel, pair->src_revnum,
&pair->src_kind, pool));
if (pair->src_kind == svn_node_none)
{
if (SVN_IS_VALID_REVNUM(pair->src_revnum))
return svn_error_createf
(SVN_ERR_FS_NOT_FOUND, NULL,
_("Path '%s' not found in revision %ld"),
pair->src_abspath_or_url, pair->src_revnum);
else
return svn_error_createf
(SVN_ERR_FS_NOT_FOUND, NULL,
_("Path '%s' not found in head revision"),
pair->src_abspath_or_url);
}
/* Figure out about dst. */
SVN_ERR(svn_io_check_path(pair->dst_abspath_or_url, &dst_kind,
iterpool));
if (dst_kind != svn_node_none)
{
return svn_error_createf(
SVN_ERR_ENTRY_EXISTS, NULL,
_("Path '%s' already exists"),
svn_dirent_local_style(pair->dst_abspath_or_url, pool));
}
/* Make sure the destination parent is a directory and produce a clear
error message if it is not. */
dst_parent = svn_dirent_dirname(pair->dst_abspath_or_url, iterpool);
SVN_ERR(svn_io_check_path(dst_parent, &dst_parent_kind, iterpool));
if (make_parents && dst_parent_kind == svn_node_none)
{
SVN_ERR(svn_client__make_local_parents(dst_parent, TRUE, ctx,
iterpool));
}
else if (dst_parent_kind != svn_node_dir)
{
return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
_("Path '%s' is not a directory"),
svn_dirent_local_style(dst_parent, pool));
}
}
svn_pool_destroy(iterpool);
SVN_WC__CALL_WITH_WRITE_LOCK(
repos_to_wc_copy_locked(timestamp_sleep,
copy_pairs, top_dst_path, ignore_externals,
ra_session, ctx, pool),
ctx->wc_ctx, lock_abspath, FALSE, pool);
return SVN_NO_ERROR;
}
#define NEED_REPOS_REVNUM(revision) \
((revision.kind != svn_opt_revision_unspecified) \
&& (revision.kind != svn_opt_revision_working))
/* ...
*
* Set *TIMESTAMP_SLEEP to TRUE if a sleep is required; otherwise do not
* change *TIMESTAMP_SLEEP. This output will be valid even if the
* function returns an error.
*
* Perform all allocations in POOL.
*/
static svn_error_t *
try_copy(svn_boolean_t *timestamp_sleep,
const apr_array_header_t *sources,
const char *dst_path_in,
svn_boolean_t is_move,
svn_boolean_t allow_mixed_revisions,
svn_boolean_t metadata_only,
svn_boolean_t make_parents,
svn_boolean_t ignore_externals,
const apr_hash_t *revprop_table,
svn_commit_callback2_t commit_callback,
void *commit_baton,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
apr_array_header_t *copy_pairs =
apr_array_make(pool, sources->nelts,
sizeof(svn_client__copy_pair_t *));
svn_boolean_t srcs_are_urls, dst_is_url;
int i;
/* Are either of our paths URLs? Just check the first src_path. If
there are more than one, we'll check for homogeneity among them
down below. */
srcs_are_urls = svn_path_is_url(APR_ARRAY_IDX(sources, 0,
svn_client_copy_source_t *)->path);
dst_is_url = svn_path_is_url(dst_path_in);
if (!dst_is_url)
SVN_ERR(svn_dirent_get_absolute(&dst_path_in, dst_path_in, pool));
/* If we have multiple source paths, it implies the dst_path is a
directory we are moving or copying into. Populate the COPY_PAIRS
array to contain a destination path for each of the source paths. */
if (sources->nelts > 1)
{
apr_pool_t *iterpool = svn_pool_create(pool);
for (i = 0; i < sources->nelts; i++)
{
svn_client_copy_source_t *source = APR_ARRAY_IDX(sources, i,
svn_client_copy_source_t *);
svn_client__copy_pair_t *pair = apr_palloc(pool, sizeof(*pair));
const char *src_basename;
svn_boolean_t src_is_url = svn_path_is_url(source->path);
svn_pool_clear(iterpool);
if (src_is_url)
{
pair->src_abspath_or_url = apr_pstrdup(pool, source->path);
src_basename = svn_uri_basename(pair->src_abspath_or_url,
iterpool);
}
else
{
SVN_ERR(svn_dirent_get_absolute(&pair->src_abspath_or_url,
source->path, pool));
src_basename = svn_dirent_basename(pair->src_abspath_or_url,
iterpool);
}
pair->src_op_revision = *source->revision;
pair->src_peg_revision = *source->peg_revision;
SVN_ERR(svn_opt_resolve_revisions(&pair->src_peg_revision,
&pair->src_op_revision,
src_is_url,
TRUE,
iterpool));
/* Check to see if all the sources are urls or all working copy
* paths. */
if (src_is_url != srcs_are_urls)
return svn_error_create
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Cannot mix repository and working copy sources"));
if (dst_is_url)
pair->dst_abspath_or_url =
svn_path_url_add_component2(dst_path_in, src_basename, pool);
else
pair->dst_abspath_or_url = svn_dirent_join(dst_path_in,
src_basename, pool);
APR_ARRAY_PUSH(copy_pairs, svn_client__copy_pair_t *) = pair;
}
svn_pool_destroy(iterpool);
}
else
{
/* Only one source path. */
svn_client__copy_pair_t *pair = apr_palloc(pool, sizeof(*pair));
svn_client_copy_source_t *source =
APR_ARRAY_IDX(sources, 0, svn_client_copy_source_t *);
svn_boolean_t src_is_url = svn_path_is_url(source->path);
if (src_is_url)
pair->src_abspath_or_url = apr_pstrdup(pool, source->path);
else
SVN_ERR(svn_dirent_get_absolute(&pair->src_abspath_or_url,
source->path, pool));
pair->src_op_revision = *source->revision;
pair->src_peg_revision = *source->peg_revision;
SVN_ERR(svn_opt_resolve_revisions(&pair->src_peg_revision,
&pair->src_op_revision,
src_is_url, TRUE, pool));
pair->dst_abspath_or_url = dst_path_in;
APR_ARRAY_PUSH(copy_pairs, svn_client__copy_pair_t *) = pair;
}
if (!srcs_are_urls && !dst_is_url)
{
apr_pool_t *iterpool = svn_pool_create(pool);
for (i = 0; i < copy_pairs->nelts; i++)
{
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
svn_pool_clear(iterpool);
if (svn_dirent_is_child(pair->src_abspath_or_url,
pair->dst_abspath_or_url, iterpool))
return svn_error_createf
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Cannot copy path '%s' into its own child '%s'"),
svn_dirent_local_style(pair->src_abspath_or_url, pool),
svn_dirent_local_style(pair->dst_abspath_or_url, pool));
}
svn_pool_destroy(iterpool);
}
/* A file external should not be moved since the file external is
implemented as a switched file and it would delete the file the
file external is switched to, which is not the behavior the user
would probably want. */
if (is_move && !srcs_are_urls)
{
apr_pool_t *iterpool = svn_pool_create(pool);
for (i = 0; i < copy_pairs->nelts; i++)
{
svn_client__copy_pair_t *pair =
APR_ARRAY_IDX(copy_pairs, i, svn_client__copy_pair_t *);
svn_node_kind_t external_kind;
const char *defining_abspath;
svn_pool_clear(iterpool);
SVN_ERR_ASSERT(svn_dirent_is_absolute(pair->src_abspath_or_url));
SVN_ERR(svn_wc__read_external_info(&external_kind, &defining_abspath,
NULL, NULL, NULL, ctx->wc_ctx,
pair->src_abspath_or_url,
pair->src_abspath_or_url, TRUE,
iterpool, iterpool));
if (external_kind != svn_node_none)
return svn_error_createf(
SVN_ERR_WC_CANNOT_MOVE_FILE_EXTERNAL,
NULL,
_("Cannot move the external at '%s'; please "
"edit the svn:externals property on '%s'."),
svn_dirent_local_style(pair->src_abspath_or_url, pool),
svn_dirent_local_style(defining_abspath, pool));
}
svn_pool_destroy(iterpool);
}
if (is_move)
{
/* Disallow moves between the working copy and the repository. */
if (srcs_are_urls != dst_is_url)
{
return svn_error_create
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Moves between the working copy and the repository are not "
"supported"));
}
/* Disallow moving any path/URL onto or into itself. */
for (i = 0; i < copy_pairs->nelts; i++)
{
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
if (strcmp(pair->src_abspath_or_url,
pair->dst_abspath_or_url) == 0)
return svn_error_createf(
SVN_ERR_UNSUPPORTED_FEATURE, NULL,
srcs_are_urls ?
_("Cannot move URL '%s' into itself") :
_("Cannot move path '%s' into itself"),
srcs_are_urls ?
pair->src_abspath_or_url :
svn_dirent_local_style(pair->src_abspath_or_url, pool));
}
}
else
{
if (!srcs_are_urls)
{
/* If we are doing a wc->* copy, but with an operational revision
other than the working copy revision, we are really doing a
repo->* copy, because we're going to need to get the rev from the
repo. */
svn_boolean_t need_repos_op_rev = FALSE;
svn_boolean_t need_repos_peg_rev = FALSE;
/* Check to see if any revision is something other than
svn_opt_revision_unspecified or svn_opt_revision_working. */
for (i = 0; i < copy_pairs->nelts; i++)
{
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
if (NEED_REPOS_REVNUM(pair->src_op_revision))
need_repos_op_rev = TRUE;
if (NEED_REPOS_REVNUM(pair->src_peg_revision))
need_repos_peg_rev = TRUE;
if (need_repos_op_rev || need_repos_peg_rev)
break;
}
if (need_repos_op_rev || need_repos_peg_rev)
{
apr_pool_t *iterpool = svn_pool_create(pool);
for (i = 0; i < copy_pairs->nelts; i++)
{
const char *copyfrom_repos_root_url;
const char *copyfrom_repos_relpath;
const char *url;
svn_revnum_t copyfrom_rev;
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
svn_pool_clear(iterpool);
SVN_ERR_ASSERT(svn_dirent_is_absolute(pair->src_abspath_or_url));
SVN_ERR(svn_wc__node_get_origin(NULL, &copyfrom_rev,
&copyfrom_repos_relpath,
&copyfrom_repos_root_url,
NULL, NULL,
ctx->wc_ctx,
pair->src_abspath_or_url,
TRUE, iterpool, iterpool));
if (copyfrom_repos_relpath)
url = svn_path_url_add_component2(copyfrom_repos_root_url,
copyfrom_repos_relpath,
pool);
else
return svn_error_createf
(SVN_ERR_ENTRY_MISSING_URL, NULL,
_("'%s' does not have a URL associated with it"),
svn_dirent_local_style(pair->src_abspath_or_url, pool));
pair->src_abspath_or_url = url;
if (!need_repos_peg_rev
|| pair->src_peg_revision.kind == svn_opt_revision_base)
{
/* Default the peg revision to that of the WC entry. */
pair->src_peg_revision.kind = svn_opt_revision_number;
pair->src_peg_revision.value.number = copyfrom_rev;
}
if (pair->src_op_revision.kind == svn_opt_revision_base)
{
/* Use the entry's revision as the operational rev. */
pair->src_op_revision.kind = svn_opt_revision_number;
pair->src_op_revision.value.number = copyfrom_rev;
}
}
svn_pool_destroy(iterpool);
srcs_are_urls = TRUE;
}
}
}
/* Now, call the right handler for the operation. */
if ((! srcs_are_urls) && (! dst_is_url))
{
SVN_ERR(verify_wc_srcs_and_dsts(copy_pairs, make_parents, is_move,
metadata_only, ctx, pool, pool));
/* Copy or move all targets. */
if (is_move)
return svn_error_trace(do_wc_to_wc_moves(timestamp_sleep,
copy_pairs, dst_path_in,
allow_mixed_revisions,
metadata_only,
ctx, pool));
else
{
/* We ignore these values, so assert the default value */
SVN_ERR_ASSERT(allow_mixed_revisions && !metadata_only);
return svn_error_trace(do_wc_to_wc_copies(timestamp_sleep,
copy_pairs, ctx, pool));
}
}
else if ((! srcs_are_urls) && (dst_is_url))
{
return svn_error_trace(
wc_to_repos_copy(copy_pairs, make_parents, revprop_table,
commit_callback, commit_baton, ctx, pool));
}
else if ((srcs_are_urls) && (! dst_is_url))
{
return svn_error_trace(
repos_to_wc_copy(timestamp_sleep,
copy_pairs, make_parents, ignore_externals,
ctx, pool));
}
else
{
return svn_error_trace(
repos_to_repos_copy(copy_pairs, make_parents, revprop_table,
commit_callback, commit_baton, ctx, is_move,
pool));
}
}
/* Public Interfaces */
svn_error_t *
svn_client_copy6(const apr_array_header_t *sources,
const char *dst_path,
svn_boolean_t copy_as_child,
svn_boolean_t make_parents,
svn_boolean_t ignore_externals,
const apr_hash_t *revprop_table,
svn_commit_callback2_t commit_callback,
void *commit_baton,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
svn_error_t *err;
svn_boolean_t timestamp_sleep = FALSE;
apr_pool_t *subpool = svn_pool_create(pool);
if (sources->nelts > 1 && !copy_as_child)
return svn_error_create(SVN_ERR_CLIENT_MULTIPLE_SOURCES_DISALLOWED,
NULL, NULL);
err = try_copy(&timestamp_sleep,
sources, dst_path,
FALSE /* is_move */,
TRUE /* allow_mixed_revisions */,
FALSE /* metadata_only */,
make_parents,
ignore_externals,
revprop_table,
commit_callback, commit_baton,
ctx,
subpool);
/* If the destination exists, try to copy the sources as children of the
destination. */
if (copy_as_child && err && (sources->nelts == 1)
&& (err->apr_err == SVN_ERR_ENTRY_EXISTS
|| err->apr_err == SVN_ERR_FS_ALREADY_EXISTS))
{
const char *src_path = APR_ARRAY_IDX(sources, 0,
svn_client_copy_source_t *)->path;
const char *src_basename;
svn_boolean_t src_is_url = svn_path_is_url(src_path);
svn_boolean_t dst_is_url = svn_path_is_url(dst_path);
svn_error_clear(err);
svn_pool_clear(subpool);
src_basename = src_is_url ? svn_uri_basename(src_path, subpool)
: svn_dirent_basename(src_path, subpool);
dst_path
= dst_is_url ? svn_path_url_add_component2(dst_path, src_basename,
subpool)
: svn_dirent_join(dst_path, src_basename, subpool);
err = try_copy(&timestamp_sleep,
sources, dst_path,
FALSE /* is_move */,
TRUE /* allow_mixed_revisions */,
FALSE /* metadata_only */,
make_parents,
ignore_externals,
revprop_table,
commit_callback, commit_baton,
ctx,
subpool);
}
/* Sleep if required. DST_PATH is not a URL in these cases. */
if (timestamp_sleep)
svn_io_sleep_for_timestamps(dst_path, subpool);
svn_pool_destroy(subpool);
return svn_error_trace(err);
}
svn_error_t *
svn_client_move7(const apr_array_header_t *src_paths,
const char *dst_path,
svn_boolean_t move_as_child,
svn_boolean_t make_parents,
svn_boolean_t allow_mixed_revisions,
svn_boolean_t metadata_only,
const apr_hash_t *revprop_table,
svn_commit_callback2_t commit_callback,
void *commit_baton,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
const svn_opt_revision_t head_revision
= { svn_opt_revision_head, { 0 } };
svn_error_t *err;
svn_boolean_t timestamp_sleep = FALSE;
int i;
apr_pool_t *subpool = svn_pool_create(pool);
apr_array_header_t *sources = apr_array_make(pool, src_paths->nelts,
sizeof(const svn_client_copy_source_t *));
if (src_paths->nelts > 1 && !move_as_child)
return svn_error_create(SVN_ERR_CLIENT_MULTIPLE_SOURCES_DISALLOWED,
NULL, NULL);
for (i = 0; i < src_paths->nelts; i++)
{
const char *src_path = APR_ARRAY_IDX(src_paths, i, const char *);
svn_client_copy_source_t *copy_source = apr_palloc(pool,
sizeof(*copy_source));
copy_source->path = src_path;
copy_source->revision = &head_revision;
copy_source->peg_revision = &head_revision;
APR_ARRAY_PUSH(sources, svn_client_copy_source_t *) = copy_source;
}
err = try_copy(&timestamp_sleep,
sources, dst_path,
TRUE /* is_move */,
allow_mixed_revisions,
metadata_only,
make_parents,
FALSE /* ignore_externals */,
revprop_table,
commit_callback, commit_baton,
ctx,
subpool);
/* If the destination exists, try to move the sources as children of the
destination. */
if (move_as_child && err && (src_paths->nelts == 1)
&& (err->apr_err == SVN_ERR_ENTRY_EXISTS
|| err->apr_err == SVN_ERR_FS_ALREADY_EXISTS))
{
const char *src_path = APR_ARRAY_IDX(src_paths, 0, const char *);
const char *src_basename;
svn_boolean_t src_is_url = svn_path_is_url(src_path);
svn_boolean_t dst_is_url = svn_path_is_url(dst_path);
svn_error_clear(err);
svn_pool_clear(subpool);
src_basename = src_is_url ? svn_uri_basename(src_path, pool)
: svn_dirent_basename(src_path, pool);
dst_path
= dst_is_url ? svn_path_url_add_component2(dst_path, src_basename,
subpool)
: svn_dirent_join(dst_path, src_basename, subpool);
err = try_copy(&timestamp_sleep,
sources, dst_path,
TRUE /* is_move */,
allow_mixed_revisions,
metadata_only,
make_parents,
FALSE /* ignore_externals */,
revprop_table,
commit_callback, commit_baton,
ctx,
subpool);
}
/* Sleep if required. DST_PATH is not a URL in these cases. */
if (timestamp_sleep)
svn_io_sleep_for_timestamps(dst_path, subpool);
svn_pool_destroy(subpool);
return svn_error_trace(err);
}
Index: vendor/subversion/dist/subversion/libsvn_client/externals.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_client/externals.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_client/externals.c (revision 286501)
@@ -1,1150 +1,1209 @@
/*
* externals.c: handle the svn:externals property
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
/* ==================================================================== */
/*** Includes. ***/
#include <apr_uri.h>
#include "svn_hash.h"
#include "svn_wc.h"
#include "svn_pools.h"
#include "svn_client.h"
#include "svn_types.h"
#include "svn_error.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_props.h"
#include "svn_config.h"
#include "client.h"
#include "svn_private_config.h"
#include "private/svn_wc_private.h"
/* Remove the directory at LOCAL_ABSPATH from revision control, and do the
* same to any revision controlled directories underneath LOCAL_ABSPATH
* (including directories not referred to by parent svn administrative areas);
* then if LOCAL_ABSPATH is empty afterwards, remove it, else rename it to a
* unique name in the same parent directory.
*
* Pass CANCEL_FUNC, CANCEL_BATON to svn_wc_remove_from_revision_control.
*
* Use SCRATCH_POOL for all temporary allocation.
*/
static svn_error_t *
relegate_dir_external(svn_wc_context_t *wc_ctx,
const char *wri_abspath,
const char *local_abspath,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool)
{
svn_error_t *err = SVN_NO_ERROR;
SVN_ERR(svn_wc__acquire_write_lock(NULL, wc_ctx, local_abspath,
FALSE, scratch_pool, scratch_pool));
err = svn_wc__external_remove(wc_ctx, wri_abspath, local_abspath, FALSE,
cancel_func, cancel_baton, scratch_pool);
if (err && (err->apr_err == SVN_ERR_WC_LEFT_LOCAL_MOD))
{
const char *parent_dir;
const char *dirname;
const char *new_path;
svn_error_clear(err);
err = SVN_NO_ERROR;
svn_dirent_split(&parent_dir, &dirname, local_abspath, scratch_pool);
/* Reserve the new dir name. */
SVN_ERR(svn_io_open_uniquely_named(NULL, &new_path,
parent_dir, dirname, ".OLD",
svn_io_file_del_none,
scratch_pool, scratch_pool));
/* Sigh... We must fall ever so slightly from grace.
Ideally, there would be no window, however brief, when we
don't have a reservation on the new name. Unfortunately,
at least in the Unix (Linux?) version of apr_file_rename(),
you can't rename a directory over a file, because it's just
calling stdio rename(), which says:
ENOTDIR
A component used as a directory in oldpath or newpath
path is not, in fact, a directory. Or, oldpath is
a directory, and newpath exists but is not a directory
So instead, we get the name, then remove the file (ugh), then
rename the directory, hoping that nobody has gotten that name
in the meantime -- which would never happen in real life, so
no big deal.
*/
/* Do our best, but no biggy if it fails. The rename will fail. */
svn_error_clear(svn_io_remove_file2(new_path, TRUE, scratch_pool));
/* Rename. If this is still a working copy we should use the working
copy rename function (to release open handles) */
err = svn_wc__rename_wc(wc_ctx, local_abspath, new_path,
scratch_pool);
if (err && err->apr_err == SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
{
svn_error_clear(err);
/* And if it is no longer a working copy, we should just rename
it */
err = svn_io_file_rename(local_abspath, new_path, scratch_pool);
}
/* ### TODO: We should notify the user about the rename */
if (notify_func)
{
svn_wc_notify_t *notify;
notify = svn_wc_create_notify(err ? local_abspath : new_path,
svn_wc_notify_left_local_modifications,
scratch_pool);
notify->kind = svn_node_dir;
notify->err = err;
notify_func(notify_baton, notify, scratch_pool);
}
}
return svn_error_trace(err);
}
/* Try to update a directory external at PATH to URL at REVISION.
Use POOL for temporary allocations, and use the client context CTX. */
static svn_error_t *
switch_dir_external(const char *local_abspath,
const char *url,
+ const char *url_from_externals_definition,
const svn_opt_revision_t *peg_revision,
const svn_opt_revision_t *revision,
const char *defining_abspath,
svn_boolean_t *timestamp_sleep,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
svn_node_kind_t kind;
svn_error_t *err;
svn_revnum_t external_peg_rev = SVN_INVALID_REVNUM;
svn_revnum_t external_rev = SVN_INVALID_REVNUM;
apr_pool_t *subpool = svn_pool_create(pool);
const char *repos_root_url;
const char *repos_uuid;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
if (peg_revision->kind == svn_opt_revision_number)
external_peg_rev = peg_revision->value.number;
if (revision->kind == svn_opt_revision_number)
external_rev = revision->value.number;
+ /*
+ * The code below assumes existing versioned paths are *not* part of
+ * the external's defining working copy.
+ * The working copy library does not support registering externals
+ * on top of existing BASE nodes and will error out if we try.
+ * So if the external target is part of the defining working copy's
+ * BASE tree, don't attempt to create the external. Doing so would
+ * leave behind a switched path instead of an external (since the
+ * switch succeeds but registration of the external in the DB fails).
+ * The working copy then cannot be updated until the path is switched back.
+ * See issue #4085.
+ */
+ SVN_ERR(svn_wc__node_get_base(&kind, NULL, NULL,
+ &repos_root_url, &repos_uuid,
+ NULL, ctx->wc_ctx, local_abspath,
+ TRUE, /* ignore_enoent */
+ TRUE, /* show hidden */
+ pool, pool));
+ if (kind != svn_node_unknown)
+ {
+ const char *wcroot_abspath;
+ const char *defining_wcroot_abspath;
+
+ SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath, ctx->wc_ctx,
+ local_abspath, pool, pool));
+ SVN_ERR(svn_wc__get_wcroot(&defining_wcroot_abspath, ctx->wc_ctx,
+ defining_abspath, pool, pool));
+ if (strcmp(wcroot_abspath, defining_wcroot_abspath) == 0)
+ return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
+ _("The external '%s' defined in %s at '%s' "
+ "cannot be checked out because '%s' is "
+ "already a versioned path."),
+ url_from_externals_definition,
+ SVN_PROP_EXTERNALS,
+ svn_dirent_local_style(defining_abspath,
+ pool),
+ svn_dirent_local_style(local_abspath,
+ pool));
+ }
+
/* If path is a directory, try to update/switch to the correct URL
and revision. */
SVN_ERR(svn_io_check_path(local_abspath, &kind, pool));
if (kind == svn_node_dir)
{
const char *node_url;
/* Doubles as an "is versioned" check. */
err = svn_wc__node_get_url(&node_url, ctx->wc_ctx, local_abspath,
pool, subpool);
if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
{
svn_error_clear(err);
err = SVN_NO_ERROR;
goto relegate;
}
else if (err)
return svn_error_trace(err);
if (node_url)
{
/* If we have what appears to be a version controlled
subdir, and its top-level URL matches that of our
externals definition, perform an update. */
if (strcmp(node_url, url) == 0)
{
SVN_ERR(svn_client__update_internal(NULL, local_abspath,
revision, svn_depth_unknown,
FALSE, FALSE, FALSE, TRUE,
FALSE, TRUE,
timestamp_sleep,
ctx, subpool));
+
+ /* We just decided that this existing directory is an external,
+ so update the external registry with this information, like
+ when checking out an external */
+ SVN_ERR(svn_wc__external_register(ctx->wc_ctx,
+ defining_abspath,
+ local_abspath, svn_node_dir,
+ repos_root_url, repos_uuid,
+ svn_uri_skip_ancestor(repos_root_url,
+ url, pool),
+ external_peg_rev,
+ external_rev,
+ pool));
+
svn_pool_destroy(subpool);
goto cleanup;
}
/* We'd really prefer not to have to do a brute-force
relegation -- blowing away the current external working
copy and checking it out anew -- so we'll first see if we
can get away with a generally cheaper relocation (if
required) and switch-style update.
To do so, we need to know the repository root URL of the
external working copy as it currently sits. */
err = svn_wc__node_get_repos_info(NULL, NULL,
&repos_root_url, &repos_uuid,
ctx->wc_ctx, local_abspath,
pool, subpool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND
&& err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
return svn_error_trace(err);
svn_error_clear(err);
repos_root_url = NULL;
repos_uuid = NULL;
}
if (repos_root_url)
{
/* If the new external target URL is not obviously a
child of the external working copy's current
repository root URL... */
if (! svn_uri__is_ancestor(repos_root_url, url))
{
const char *repos_root;
/* ... then figure out precisely which repository
root URL that target URL *is* a child of ... */
SVN_ERR(svn_client_get_repos_root(&repos_root, NULL, url,
ctx, subpool, subpool));
/* ... and use that to try to relocate the external
working copy to the target location. */
err = svn_client_relocate2(local_abspath, repos_root_url,
repos_root, FALSE, ctx, subpool);
/* If the relocation failed because the new URL
points to a totally different repository, we've
no choice but to relegate and check out a new WC. */
if (err
&& (err->apr_err == SVN_ERR_WC_INVALID_RELOCATION
|| (err->apr_err
== SVN_ERR_CLIENT_INVALID_RELOCATION)))
{
svn_error_clear(err);
goto relegate;
}
else if (err)
return svn_error_trace(err);
/* If the relocation went without a hitch, we should
have a new repository root URL. */
repos_root_url = repos_root;
}
SVN_ERR(svn_client__switch_internal(NULL, local_abspath, url,
peg_revision, revision,
svn_depth_infinity,
TRUE, FALSE, FALSE,
TRUE /* ignore_ancestry */,
timestamp_sleep,
ctx, subpool));
SVN_ERR(svn_wc__external_register(ctx->wc_ctx,
defining_abspath,
local_abspath, svn_node_dir,
repos_root_url, repos_uuid,
svn_uri_skip_ancestor(
repos_root_url,
url, subpool),
external_peg_rev,
external_rev,
subpool));
svn_pool_destroy(subpool);
goto cleanup;
}
}
}
relegate:
/* Fall back on removing the WC and checking out a new one. */
/* Ensure that we don't have any RA sessions or WC locks from failed
operations above. */
svn_pool_destroy(subpool);
if (kind == svn_node_dir)
{
/* Buh-bye, old and busted ... */
SVN_ERR(relegate_dir_external(ctx->wc_ctx, defining_abspath,
local_abspath,
ctx->cancel_func, ctx->cancel_baton,
ctx->notify_func2, ctx->notify_baton2,
pool));
}
else
{
/* The target dir might have multiple components. Guarantee
the path leading down to the last component. */
const char *parent = svn_dirent_dirname(local_abspath, pool);
SVN_ERR(svn_io_make_dir_recursively(parent, pool));
}
/* ... Hello, new hotness. */
SVN_ERR(svn_client__checkout_internal(NULL, url, local_abspath, peg_revision,
revision, svn_depth_infinity,
FALSE, FALSE, timestamp_sleep,
ctx, pool));
SVN_ERR(svn_wc__node_get_repos_info(NULL, NULL,
&repos_root_url,
&repos_uuid,
ctx->wc_ctx, local_abspath,
pool, pool));
SVN_ERR(svn_wc__external_register(ctx->wc_ctx,
defining_abspath,
local_abspath, svn_node_dir,
repos_root_url, repos_uuid,
svn_uri_skip_ancestor(repos_root_url,
url, pool),
external_peg_rev,
external_rev,
pool));
cleanup:
/* Issues #4123 and #4130: We don't need to keep the newly checked
out external's DB open. */
SVN_ERR(svn_wc__close_db(local_abspath, ctx->wc_ctx, pool));
return SVN_NO_ERROR;
}
/* Try to update a file external at LOCAL_ABSPATH to URL at REVISION using a
access baton that has a write lock. Use SCRATCH_POOL for temporary
allocations, and use the client context CTX. */
static svn_error_t *
switch_file_external(const char *local_abspath,
const char *url,
const svn_opt_revision_t *peg_revision,
const svn_opt_revision_t *revision,
const char *def_dir_abspath,
svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
svn_config_t *cfg = ctx->config
? svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_CONFIG)
: NULL;
svn_boolean_t use_commit_times;
const char *diff3_cmd;
const char *preserved_exts_str;
const apr_array_header_t *preserved_exts;
svn_node_kind_t kind, external_kind;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
/* See if the user wants last-commit timestamps instead of current ones. */
SVN_ERR(svn_config_get_bool(cfg, &use_commit_times,
SVN_CONFIG_SECTION_MISCELLANY,
SVN_CONFIG_OPTION_USE_COMMIT_TIMES, FALSE));
/* Get the external diff3, if any. */
svn_config_get(cfg, &diff3_cmd, SVN_CONFIG_SECTION_HELPERS,
SVN_CONFIG_OPTION_DIFF3_CMD, NULL);
if (diff3_cmd != NULL)
SVN_ERR(svn_path_cstring_to_utf8(&diff3_cmd, diff3_cmd, scratch_pool));
/* See which files the user wants to preserve the extension of when
conflict files are made. */
svn_config_get(cfg, &preserved_exts_str, SVN_CONFIG_SECTION_MISCELLANY,
SVN_CONFIG_OPTION_PRESERVED_CF_EXTS, "");
preserved_exts = *preserved_exts_str
? svn_cstring_split(preserved_exts_str, "\n\r\t\v ", FALSE, scratch_pool)
: NULL;
{
const char *wcroot_abspath;
SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath, ctx->wc_ctx, local_abspath,
scratch_pool, scratch_pool));
/* File externals can only be installed inside the current working copy.
So verify if the working copy that contains/will contain the target
is the defining abspath, or one of its ancestors */
if (!svn_dirent_is_ancestor(wcroot_abspath, def_dir_abspath))
return svn_error_createf(
SVN_ERR_WC_BAD_PATH, NULL,
_("Cannot insert a file external defined on '%s' "
"into the working copy '%s'."),
svn_dirent_local_style(def_dir_abspath,
scratch_pool),
svn_dirent_local_style(wcroot_abspath,
scratch_pool));
}
SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, local_abspath,
TRUE, FALSE, scratch_pool));
SVN_ERR(svn_wc__read_external_info(&external_kind, NULL, NULL, NULL, NULL,
ctx->wc_ctx, local_abspath, local_abspath,
TRUE, scratch_pool, scratch_pool));
/* If there is a versioned item with this name, ensure it's a file
external before working with it. If there is no entry in the
working copy, then create an empty file and add it to the working
copy. */
if (kind != svn_node_none && kind != svn_node_unknown)
{
if (external_kind != svn_node_file)
{
return svn_error_createf(
SVN_ERR_CLIENT_FILE_EXTERNAL_OVERWRITE_VERSIONED, 0,
_("The file external from '%s' cannot overwrite the existing "
"versioned item at '%s'"),
url, svn_dirent_local_style(local_abspath, scratch_pool));
}
}
else
{
svn_node_kind_t disk_kind;
SVN_ERR(svn_io_check_path(local_abspath, &disk_kind, scratch_pool));
if (kind == svn_node_file || kind == svn_node_dir)
return svn_error_createf(SVN_ERR_WC_PATH_FOUND, NULL,
_("The file external '%s' can not be "
"created because the node exists."),
svn_dirent_local_style(local_abspath,
scratch_pool));
}
{
const svn_ra_reporter3_t *reporter;
void *report_baton;
const svn_delta_editor_t *switch_editor;
void *switch_baton;
svn_client__pathrev_t *switch_loc;
svn_revnum_t revnum;
apr_array_header_t *inherited_props;
const char *dir_abspath;
const char *target;
svn_dirent_split(&dir_abspath, &target, local_abspath, scratch_pool);
- /* Open an RA session to 'source' URL */
+ /* ### Why do we open a new session? RA_SESSION is a valid
+ ### session -- the caller used it to call svn_ra_check_path on
+ ### this very URL, the caller also did the resolving and
+ ### reparenting that is repeated here. */
SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &switch_loc,
url, dir_abspath,
peg_revision, revision,
ctx, scratch_pool));
/* Get the external file's iprops. */
SVN_ERR(svn_ra_get_inherited_props(ra_session, &inherited_props, "",
switch_loc->rev,
scratch_pool, scratch_pool));
SVN_ERR(svn_ra_reparent(ra_session, svn_uri_dirname(url, scratch_pool),
scratch_pool));
SVN_ERR(svn_wc__get_file_external_editor(&switch_editor, &switch_baton,
&revnum, ctx->wc_ctx,
local_abspath,
def_dir_abspath,
switch_loc->url,
switch_loc->repos_root_url,
switch_loc->repos_uuid,
inherited_props,
use_commit_times,
diff3_cmd, preserved_exts,
def_dir_abspath,
url, peg_revision, revision,
ctx->conflict_func2,
ctx->conflict_baton2,
ctx->cancel_func,
ctx->cancel_baton,
ctx->notify_func2,
ctx->notify_baton2,
scratch_pool, scratch_pool));
/* Tell RA to do an update of URL+TARGET to REVISION; if we pass an
invalid revnum, that means RA will use the latest revision. */
SVN_ERR(svn_ra_do_switch3(ra_session, &reporter, &report_baton,
switch_loc->rev,
- target, svn_depth_unknown, url,
+ target, svn_depth_unknown, switch_loc->url,
FALSE /* send_copyfrom */,
TRUE /* ignore_ancestry */,
switch_editor, switch_baton,
scratch_pool, scratch_pool));
SVN_ERR(svn_wc__crawl_file_external(ctx->wc_ctx, local_abspath,
reporter, report_baton,
TRUE, use_commit_times,
ctx->cancel_func, ctx->cancel_baton,
ctx->notify_func2, ctx->notify_baton2,
scratch_pool));
if (ctx->notify_func2)
{
svn_wc_notify_t *notify
= svn_wc_create_notify(local_abspath, svn_wc_notify_update_completed,
scratch_pool);
notify->kind = svn_node_none;
notify->content_state = notify->prop_state
= svn_wc_notify_state_inapplicable;
notify->lock_state = svn_wc_notify_lock_state_inapplicable;
notify->revision = revnum;
(*ctx->notify_func2)(ctx->notify_baton2, notify, scratch_pool);
}
}
return SVN_NO_ERROR;
}
/* Wrappers around svn_wc__external_remove, obtaining and releasing a lock for
directory externals */
static svn_error_t *
remove_external2(svn_boolean_t *removed,
svn_wc_context_t *wc_ctx,
const char *wri_abspath,
const char *local_abspath,
svn_node_kind_t external_kind,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
SVN_ERR(svn_wc__external_remove(wc_ctx, wri_abspath,
local_abspath,
(external_kind == svn_node_none),
cancel_func, cancel_baton,
scratch_pool));
*removed = TRUE;
return SVN_NO_ERROR;
}
static svn_error_t *
remove_external(svn_boolean_t *removed,
svn_wc_context_t *wc_ctx,
const char *wri_abspath,
const char *local_abspath,
svn_node_kind_t external_kind,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
*removed = FALSE;
switch (external_kind)
{
case svn_node_dir:
SVN_WC__CALL_WITH_WRITE_LOCK(
remove_external2(removed,
wc_ctx, wri_abspath,
local_abspath, external_kind,
cancel_func, cancel_baton,
scratch_pool),
wc_ctx, local_abspath, FALSE, scratch_pool);
break;
case svn_node_file:
default:
SVN_ERR(remove_external2(removed,
wc_ctx, wri_abspath,
local_abspath, external_kind,
cancel_func, cancel_baton,
scratch_pool));
break;
}
return SVN_NO_ERROR;
}
/* Called when an external that is in the EXTERNALS table is no longer
referenced from an svn:externals property */
static svn_error_t *
handle_external_item_removal(const svn_client_ctx_t *ctx,
const char *defining_abspath,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
svn_error_t *err;
svn_node_kind_t external_kind;
svn_node_kind_t kind;
svn_boolean_t removed = FALSE;
/* local_abspath should be a wcroot or a file external */
SVN_ERR(svn_wc__read_external_info(&external_kind, NULL, NULL, NULL, NULL,
ctx->wc_ctx, defining_abspath,
local_abspath, FALSE,
scratch_pool, scratch_pool));
SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, local_abspath, TRUE, FALSE,
scratch_pool));
if (external_kind != kind)
external_kind = svn_node_none; /* Only remove the registration */
err = remove_external(&removed,
ctx->wc_ctx, defining_abspath, local_abspath,
external_kind,
ctx->cancel_func, ctx->cancel_baton,
scratch_pool);
if (err && err->apr_err == SVN_ERR_WC_NOT_LOCKED && removed)
{
svn_error_clear(err);
err = NULL; /* We removed the working copy, so we can't release the
lock that was stored inside */
}
if (ctx->notify_func2)
{
svn_wc_notify_t *notify =
svn_wc_create_notify(local_abspath,
svn_wc_notify_update_external_removed,
scratch_pool);
notify->kind = kind;
notify->err = err;
(ctx->notify_func2)(ctx->notify_baton2, notify, scratch_pool);
if (err && err->apr_err == SVN_ERR_WC_LEFT_LOCAL_MOD)
{
notify = svn_wc_create_notify(local_abspath,
svn_wc_notify_left_local_modifications,
scratch_pool);
notify->kind = svn_node_dir;
notify->err = err;
(ctx->notify_func2)(ctx->notify_baton2, notify, scratch_pool);
}
}
if (err && err->apr_err == SVN_ERR_WC_LEFT_LOCAL_MOD)
{
svn_error_clear(err);
err = NULL;
}
return svn_error_trace(err);
}
static svn_error_t *
handle_external_item_change(svn_client_ctx_t *ctx,
const char *repos_root_url,
const char *parent_dir_abspath,
const char *parent_dir_url,
const char *local_abspath,
const char *old_defining_abspath,
const svn_wc_external_item2_t *new_item,
svn_boolean_t *timestamp_sleep,
apr_pool_t *scratch_pool)
{
svn_ra_session_t *ra_session;
svn_client__pathrev_t *new_loc;
const char *new_url;
svn_node_kind_t ext_kind;
SVN_ERR_ASSERT(repos_root_url && parent_dir_url);
SVN_ERR_ASSERT(new_item != NULL);
/* Don't bother to check status, since we'll get that for free by
attempting to retrieve the hash values anyway. */
/* When creating the absolute URL, use the pool and not the
iterpool, since the hash table values outlive the iterpool and
any pointers they have should also outlive the iterpool. */
SVN_ERR(svn_wc__resolve_relative_external_url(&new_url,
new_item, repos_root_url,
parent_dir_url,
scratch_pool, scratch_pool));
/* Determine if the external is a file or directory. */
/* Get the RA connection. */
SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &new_loc,
new_url, NULL,
&(new_item->peg_revision),
&(new_item->revision), ctx,
scratch_pool));
SVN_ERR(svn_ra_check_path(ra_session, "", new_loc->rev, &ext_kind,
scratch_pool));
if (svn_node_none == ext_kind)
return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
_("URL '%s' at revision %ld doesn't exist"),
new_loc->url, new_loc->rev);
if (svn_node_dir != ext_kind && svn_node_file != ext_kind)
return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
_("URL '%s' at revision %ld is not a file "
"or a directory"),
new_loc->url, new_loc->rev);
/* Not protecting against recursive externals. Detecting them in
the global case is hard, and it should be pretty obvious to a
user when it happens. Worst case: your disk fills up :-). */
/* First notify that we're about to handle an external. */
if (ctx->notify_func2)
{
(*ctx->notify_func2)(
ctx->notify_baton2,
svn_wc_create_notify(local_abspath,
svn_wc_notify_update_external,
scratch_pool),
scratch_pool);
}
if (! old_defining_abspath)
{
/* The target dir might have multiple components. Guarantee the path
leading down to the last component. */
SVN_ERR(svn_io_make_dir_recursively(svn_dirent_dirname(local_abspath,
scratch_pool),
scratch_pool));
}
switch (ext_kind)
{
case svn_node_dir:
SVN_ERR(switch_dir_external(local_abspath, new_loc->url,
+ new_item->url,
&(new_item->peg_revision),
&(new_item->revision),
parent_dir_abspath,
timestamp_sleep, ctx,
scratch_pool));
break;
case svn_node_file:
if (strcmp(repos_root_url, new_loc->repos_root_url))
{
const char *local_repos_root_url;
const char *local_repos_uuid;
const char *ext_repos_relpath;
svn_error_t *err;
/*
* The working copy library currently requires that all files
* in the working copy have the same repository root URL.
* The URL from the file external's definition differs from the
* one used by the working copy. As a workaround, replace the
* root URL portion of the file external's URL, after making
* sure both URLs point to the same repository. See issue #4087.
*/
err = svn_wc__node_get_repos_info(NULL, NULL,
&local_repos_root_url,
&local_repos_uuid,
ctx->wc_ctx, parent_dir_abspath,
scratch_pool, scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND
&& err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
return svn_error_trace(err);
svn_error_clear(err);
local_repos_root_url = NULL;
local_repos_uuid = NULL;
}
ext_repos_relpath = svn_uri_skip_ancestor(new_loc->repos_root_url,
new_url, scratch_pool);
if (local_repos_uuid == NULL || local_repos_root_url == NULL ||
ext_repos_relpath == NULL ||
strcmp(local_repos_uuid, new_loc->repos_uuid) != 0)
return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Unsupported external: URL of file external '%s' "
"is not in repository '%s'"),
new_url, repos_root_url);
new_url = svn_path_url_add_component2(local_repos_root_url,
ext_repos_relpath,
scratch_pool);
SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &new_loc,
new_url,
NULL,
&(new_item->peg_revision),
&(new_item->revision),
ctx, scratch_pool));
}
SVN_ERR(switch_file_external(local_abspath,
new_url,
&new_item->peg_revision,
&new_item->revision,
parent_dir_abspath,
ra_session,
ctx,
scratch_pool));
break;
default:
SVN_ERR_MALFUNCTION();
break;
}
return SVN_NO_ERROR;
}
static svn_error_t *
wrap_external_error(const svn_client_ctx_t *ctx,
const char *target_abspath,
svn_error_t *err,
apr_pool_t *scratch_pool)
{
if (err && err->apr_err != SVN_ERR_CANCELLED)
{
if (ctx->notify_func2)
{
svn_wc_notify_t *notifier = svn_wc_create_notify(
target_abspath,
svn_wc_notify_failed_external,
scratch_pool);
notifier->err = err;
ctx->notify_func2(ctx->notify_baton2, notifier, scratch_pool);
}
svn_error_clear(err);
return SVN_NO_ERROR;
}
return err;
}
static svn_error_t *
handle_externals_change(svn_client_ctx_t *ctx,
const char *repos_root_url,
svn_boolean_t *timestamp_sleep,
const char *local_abspath,
const char *new_desc_text,
apr_hash_t *old_externals,
svn_depth_t ambient_depth,
svn_depth_t requested_depth,
apr_pool_t *scratch_pool)
{
apr_array_header_t *new_desc;
int i;
apr_pool_t *iterpool;
const char *url;
iterpool = svn_pool_create(scratch_pool);
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
/* Bag out if the depth here is too shallow for externals action. */
if ((requested_depth < svn_depth_infinity
&& requested_depth != svn_depth_unknown)
|| (ambient_depth < svn_depth_infinity
&& requested_depth < svn_depth_infinity))
return SVN_NO_ERROR;
if (new_desc_text)
SVN_ERR(svn_wc_parse_externals_description3(&new_desc, local_abspath,
new_desc_text,
FALSE, scratch_pool));
else
new_desc = NULL;
SVN_ERR(svn_wc__node_get_url(&url, ctx->wc_ctx, local_abspath,
scratch_pool, iterpool));
SVN_ERR_ASSERT(url);
for (i = 0; new_desc && (i < new_desc->nelts); i++)
{
const char *old_defining_abspath;
svn_wc_external_item2_t *new_item;
const char *target_abspath;
svn_boolean_t under_root;
new_item = APR_ARRAY_IDX(new_desc, i, svn_wc_external_item2_t *);
svn_pool_clear(iterpool);
if (ctx->cancel_func)
SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
SVN_ERR(svn_dirent_is_under_root(&under_root, &target_abspath,
local_abspath, new_item->target_dir,
iterpool));
if (! under_root)
{
return svn_error_createf(
SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
_("Path '%s' is not in the working copy"),
svn_dirent_local_style(
svn_dirent_join(local_abspath, new_item->target_dir,
iterpool),
iterpool));
}
old_defining_abspath = svn_hash_gets(old_externals, target_abspath);
SVN_ERR(wrap_external_error(
ctx, target_abspath,
handle_external_item_change(ctx,
repos_root_url,
local_abspath, url,
target_abspath,
old_defining_abspath,
new_item,
timestamp_sleep,
iterpool),
iterpool));
/* And remove already processed items from the to-remove hash */
if (old_defining_abspath)
svn_hash_sets(old_externals, target_abspath, NULL);
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_client__handle_externals(apr_hash_t *externals_new,
apr_hash_t *ambient_depths,
const char *repos_root_url,
const char *target_abspath,
svn_depth_t requested_depth,
svn_boolean_t *timestamp_sleep,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
apr_hash_t *old_external_defs;
apr_hash_index_t *hi;
apr_pool_t *iterpool;
SVN_ERR_ASSERT(repos_root_url);
iterpool = svn_pool_create(scratch_pool);
SVN_ERR(svn_wc__externals_defined_below(&old_external_defs,
ctx->wc_ctx, target_abspath,
scratch_pool, iterpool));
for (hi = apr_hash_first(scratch_pool, externals_new);
hi;
hi = apr_hash_next(hi))
{
const char *local_abspath = svn__apr_hash_index_key(hi);
const char *desc_text = svn__apr_hash_index_val(hi);
svn_depth_t ambient_depth = svn_depth_infinity;
svn_pool_clear(iterpool);
if (ambient_depths)
{
const char *ambient_depth_w;
ambient_depth_w = apr_hash_get(ambient_depths, local_abspath,
svn__apr_hash_index_klen(hi));
if (ambient_depth_w == NULL)
{
return svn_error_createf(
SVN_ERR_WC_CORRUPT, NULL,
_("Traversal of '%s' found no ambient depth"),
svn_dirent_local_style(local_abspath, scratch_pool));
}
else
{
ambient_depth = svn_depth_from_word(ambient_depth_w);
}
}
SVN_ERR(handle_externals_change(ctx, repos_root_url, timestamp_sleep,
local_abspath,
desc_text, old_external_defs,
ambient_depth, requested_depth,
iterpool));
}
/* Remove the remaining externals */
for (hi = apr_hash_first(scratch_pool, old_external_defs);
hi;
hi = apr_hash_next(hi))
{
const char *item_abspath = svn__apr_hash_index_key(hi);
const char *defining_abspath = svn__apr_hash_index_val(hi);
const char *parent_abspath;
svn_pool_clear(iterpool);
SVN_ERR(wrap_external_error(
ctx, item_abspath,
handle_external_item_removal(ctx, defining_abspath,
item_abspath, iterpool),
iterpool));
/* Are there any unversioned directories between the removed
* external and the DEFINING_ABSPATH which we can remove? */
parent_abspath = item_abspath;
do {
svn_node_kind_t kind;
parent_abspath = svn_dirent_dirname(parent_abspath, iterpool);
SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, parent_abspath,
FALSE /* show_deleted*/,
FALSE /* show_hidden */,
iterpool));
if (kind == svn_node_none)
{
svn_error_t *err;
err = svn_io_dir_remove_nonrecursive(parent_abspath, iterpool);
if (err)
{
if (APR_STATUS_IS_ENOTEMPTY(err->apr_err))
{
svn_error_clear(err);
break; /* No parents to delete */
}
else if (APR_STATUS_IS_ENOENT(err->apr_err)
|| APR_STATUS_IS_ENOTDIR(err->apr_err))
{
svn_error_clear(err);
/* Fall through; parent dir might be unversioned */
}
else
return svn_error_trace(err);
}
}
} while (strcmp(parent_abspath, defining_abspath) != 0);
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_client__export_externals(apr_hash_t *externals,
const char *from_url,
const char *to_abspath,
const char *repos_root_url,
svn_depth_t requested_depth,
const char *native_eol,
svn_boolean_t ignore_keywords,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
apr_pool_t *sub_iterpool = svn_pool_create(scratch_pool);
apr_hash_index_t *hi;
SVN_ERR_ASSERT(svn_dirent_is_absolute(to_abspath));
for (hi = apr_hash_first(scratch_pool, externals);
hi;
hi = apr_hash_next(hi))
{
const char *local_abspath = svn__apr_hash_index_key(hi);
const char *desc_text = svn__apr_hash_index_val(hi);
const char *local_relpath;
const char *dir_url;
apr_array_header_t *items;
int i;
svn_pool_clear(iterpool);
SVN_ERR(svn_wc_parse_externals_description3(&items, local_abspath,
desc_text, FALSE,
iterpool));
if (! items->nelts)
continue;
local_relpath = svn_dirent_skip_ancestor(to_abspath, local_abspath);
dir_url = svn_path_url_add_component2(from_url, local_relpath,
scratch_pool);
for (i = 0; i < items->nelts; i++)
{
const char *item_abspath;
const char *new_url;
svn_boolean_t under_root;
svn_wc_external_item2_t *item = APR_ARRAY_IDX(items, i,
svn_wc_external_item2_t *);
svn_pool_clear(sub_iterpool);
SVN_ERR(svn_dirent_is_under_root(&under_root, &item_abspath,
local_abspath, item->target_dir,
sub_iterpool));
if (! under_root)
{
return svn_error_createf(
SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
_("Path '%s' is not in the working copy"),
svn_dirent_local_style(
svn_dirent_join(local_abspath, item->target_dir,
sub_iterpool),
sub_iterpool));
}
SVN_ERR(svn_wc__resolve_relative_external_url(&new_url, item,
repos_root_url,
dir_url, sub_iterpool,
sub_iterpool));
/* The target dir might have multiple components. Guarantee
the path leading down to the last component. */
SVN_ERR(svn_io_make_dir_recursively(svn_dirent_dirname(item_abspath,
sub_iterpool),
sub_iterpool));
SVN_ERR(wrap_external_error(
ctx, item_abspath,
svn_client_export5(NULL, new_url, item_abspath,
&item->peg_revision,
&item->revision,
TRUE, FALSE, ignore_keywords,
svn_depth_infinity,
native_eol,
ctx, sub_iterpool),
sub_iterpool));
}
}
svn_pool_destroy(sub_iterpool);
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
Index: vendor/subversion/dist/subversion/libsvn_client/log.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_client/log.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_client/log.c (revision 286501)
@@ -1,899 +1,912 @@
/*
* log.c: return log messages
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#define APR_WANT_STRFUNC
#include <apr_want.h>
#include <apr_strings.h>
#include <apr_pools.h>
#include "svn_pools.h"
#include "svn_client.h"
#include "svn_compat.h"
#include "svn_error.h"
#include "svn_dirent_uri.h"
#include "svn_hash.h"
#include "svn_path.h"
#include "svn_sorts.h"
#include "svn_props.h"
#include "client.h"
#include "svn_private_config.h"
#include "private/svn_wc_private.h"
#include <assert.h>
/*** Getting misc. information ***/
/* The baton for use with copyfrom_info_receiver(). */
typedef struct copyfrom_info_t
{
svn_boolean_t is_first;
const char *path;
svn_revnum_t rev;
apr_pool_t *pool;
} copyfrom_info_t;
/* A location segment callback for obtaining the copy source of
a node at a path and storing it in *BATON (a struct copyfrom_info_t *).
Implements svn_location_segment_receiver_t. */
static svn_error_t *
copyfrom_info_receiver(svn_location_segment_t *segment,
void *baton,
apr_pool_t *pool)
{
copyfrom_info_t *copyfrom_info = baton;
/* If we've already identified the copy source, there's nothing more
to do.
### FIXME: We *should* be able to send */
if (copyfrom_info->path)
return SVN_NO_ERROR;
/* If this is the first segment, it's not of interest to us. Otherwise
(so long as this segment doesn't represent a history gap), it holds
our path's previous location (from which it was last copied). */
if (copyfrom_info->is_first)
{
copyfrom_info->is_first = FALSE;
}
else if (segment->path)
{
/* The end of the second non-gap segment is the location copied from. */
copyfrom_info->path = apr_pstrdup(copyfrom_info->pool, segment->path);
copyfrom_info->rev = segment->range_end;
/* ### FIXME: We *should* be able to return SVN_ERR_CEASE_INVOCATION
### here so we don't get called anymore. */
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_client__get_copy_source(const char **original_repos_relpath,
svn_revnum_t *original_revision,
const char *path_or_url,
const svn_opt_revision_t *revision,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_error_t *err;
copyfrom_info_t copyfrom_info = { 0 };
apr_pool_t *sesspool = svn_pool_create(scratch_pool);
svn_ra_session_t *ra_session;
svn_client__pathrev_t *at_loc;
copyfrom_info.is_first = TRUE;
copyfrom_info.path = NULL;
copyfrom_info.rev = SVN_INVALID_REVNUM;
copyfrom_info.pool = result_pool;
SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &at_loc,
path_or_url, NULL,
revision, revision,
ctx, sesspool));
/* Find the copy source. Walk the location segments to find the revision
at which this node was created (copied or added). */
err = svn_ra_get_location_segments(ra_session, "", at_loc->rev, at_loc->rev,
SVN_INVALID_REVNUM,
copyfrom_info_receiver, &copyfrom_info,
scratch_pool);
svn_pool_destroy(sesspool);
if (err)
{
if (err->apr_err == SVN_ERR_FS_NOT_FOUND ||
err->apr_err == SVN_ERR_RA_DAV_REQUEST_FAILED)
{
/* A locally-added but uncommitted versioned resource won't
exist in the repository. */
svn_error_clear(err);
err = SVN_NO_ERROR;
*original_repos_relpath = NULL;
*original_revision = SVN_INVALID_REVNUM;
}
return svn_error_trace(err);
}
*original_repos_relpath = copyfrom_info.path;
*original_revision = copyfrom_info.rev;
return SVN_NO_ERROR;
}
/* compatibility with pre-1.5 servers, which send only author/date/log
*revprops in log entries */
typedef struct pre_15_receiver_baton_t
{
svn_client_ctx_t *ctx;
/* ra session for retrieving revprops from old servers */
svn_ra_session_t *ra_session;
/* caller's list of requested revprops, receiver, and baton */
const char *ra_session_url;
apr_pool_t *ra_session_pool;
const apr_array_header_t *revprops;
svn_log_entry_receiver_t receiver;
void *baton;
} pre_15_receiver_baton_t;
static svn_error_t *
pre_15_receiver(void *baton, svn_log_entry_t *log_entry, apr_pool_t *pool)
{
pre_15_receiver_baton_t *rb = baton;
if (log_entry->revision == SVN_INVALID_REVNUM)
return rb->receiver(rb->baton, log_entry, pool);
/* If only some revprops are requested, get them one at a time on the
second ra connection. If all are requested, get them all with
svn_ra_rev_proplist. This avoids getting unrequested revprops (which
may be arbitrarily large), but means one round-trip per requested
revprop. epg isn't entirely sure which should be optimized for. */
if (rb->revprops)
{
int i;
svn_boolean_t want_author, want_date, want_log;
want_author = want_date = want_log = FALSE;
for (i = 0; i < rb->revprops->nelts; i++)
{
const char *name = APR_ARRAY_IDX(rb->revprops, i, const char *);
svn_string_t *value;
/* If a standard revprop is requested, we know it is already in
log_entry->revprops if available. */
if (strcmp(name, SVN_PROP_REVISION_AUTHOR) == 0)
{
want_author = TRUE;
continue;
}
if (strcmp(name, SVN_PROP_REVISION_DATE) == 0)
{
want_date = TRUE;
continue;
}
if (strcmp(name, SVN_PROP_REVISION_LOG) == 0)
{
want_log = TRUE;
continue;
}
if (rb->ra_session == NULL)
SVN_ERR(svn_client_open_ra_session2(&rb->ra_session,
rb->ra_session_url, NULL,
rb->ctx, rb->ra_session_pool,
pool));
SVN_ERR(svn_ra_rev_prop(rb->ra_session, log_entry->revision,
name, &value, pool));
if (log_entry->revprops == NULL)
log_entry->revprops = apr_hash_make(pool);
svn_hash_sets(log_entry->revprops, name, value);
}
if (log_entry->revprops)
{
/* Pre-1.5 servers send the standard revprops unconditionally;
clear those the caller doesn't want. */
if (!want_author)
svn_hash_sets(log_entry->revprops, SVN_PROP_REVISION_AUTHOR, NULL);
if (!want_date)
svn_hash_sets(log_entry->revprops, SVN_PROP_REVISION_DATE, NULL);
if (!want_log)
svn_hash_sets(log_entry->revprops, SVN_PROP_REVISION_LOG, NULL);
}
}
else
{
if (rb->ra_session == NULL)
SVN_ERR(svn_client_open_ra_session2(&rb->ra_session,
rb->ra_session_url, NULL,
rb->ctx, rb->ra_session_pool,
pool));
SVN_ERR(svn_ra_rev_proplist(rb->ra_session, log_entry->revision,
&log_entry->revprops, pool));
}
return rb->receiver(rb->baton, log_entry, pool);
}
/* limit receiver */
typedef struct limit_receiver_baton_t
{
int limit;
svn_log_entry_receiver_t receiver;
void *baton;
} limit_receiver_baton_t;
static svn_error_t *
limit_receiver(void *baton, svn_log_entry_t *log_entry, apr_pool_t *pool)
{
limit_receiver_baton_t *rb = baton;
rb->limit--;
return rb->receiver(rb->baton, log_entry, pool);
}
/* Resolve the URLs or WC path in TARGETS as per the svn_client_log5 API.
The limitations on TARGETS specified by svn_client_log5 are enforced here.
So TARGETS can only contain a single WC path or a URL and zero or more
relative paths -- anything else will raise an error.
PEG_REVISION, TARGETS, and CTX are as per svn_client_log5.
If TARGETS contains a single WC path then set *RA_TARGET to the absolute
path of that single path if PEG_REVISION is dependent on the working copy
(e.g. PREV). Otherwise set *RA_TARGET to the corresponding URL for the
single WC path. Set *RELATIVE_TARGETS to an array with a single
element "".
If TARGETS contains only a single URL, then set *RA_TARGET to a copy of
that URL and *RELATIVE_TARGETS to an array with a single element "".
If TARGETS contains a single URL and one or more relative paths, then
set *RA_TARGET to a copy of that URL and *RELATIVE_TARGETS to a copy of
each relative path after the URL.
If *PEG_REVISION is svn_opt_revision_unspecified, then *PEG_REVISION is
set to svn_opt_revision_head for URLs or svn_opt_revision_working for a
WC path.
*RA_TARGET and *RELATIVE_TARGETS are allocated in RESULT_POOL. */
static svn_error_t *
resolve_log_targets(apr_array_header_t **relative_targets,
const char **ra_target,
svn_opt_revision_t *peg_revision,
const apr_array_header_t *targets,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
int i;
svn_boolean_t url_targets;
/* Per svn_client_log5, TARGETS contains either a URL followed by zero or
more relative paths, or one working copy path. */
const char *url_or_path = APR_ARRAY_IDX(targets, 0, const char *);
/* svn_client_log5 requires at least one target. */
if (targets->nelts == 0)
return svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL,
_("No valid target found"));
/* Initialize the output array. At a minimum, we need room for one
(possibly empty) relpath. Otherwise, we have to hold a relpath
for every item in TARGETS except the first. */
*relative_targets = apr_array_make(result_pool,
MAX(1, targets->nelts - 1),
sizeof(const char *));
if (svn_path_is_url(url_or_path))
{
/* An unspecified PEG_REVISION for a URL path defaults
to svn_opt_revision_head. */
if (peg_revision->kind == svn_opt_revision_unspecified)
peg_revision->kind = svn_opt_revision_head;
/* The logic here is this: If we get passed one argument, we assume
it is the full URL to a file/dir we want log info for. If we get
a URL plus some paths, then we assume that the URL is the base,
and that the paths passed are relative to it. */
if (targets->nelts > 1)
{
/* We have some paths, let's use them. Start after the URL. */
for (i = 1; i < targets->nelts; i++)
{
const char *target;
target = APR_ARRAY_IDX(targets, i, const char *);
if (svn_path_is_url(target) || svn_dirent_is_absolute(target))
return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
_("'%s' is not a relative path"),
target);
APR_ARRAY_PUSH(*relative_targets, const char *) =
apr_pstrdup(result_pool, target);
}
}
else
{
/* If we have a single URL, then the session will be rooted at
it, so just send an empty string for the paths we are
interested in. */
APR_ARRAY_PUSH(*relative_targets, const char *) = "";
}
/* Remember that our targets are URLs. */
url_targets = TRUE;
}
else /* WC path target. */
{
const char *target;
const char *target_abspath;
url_targets = FALSE;
if (targets->nelts > 1)
return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("When specifying working copy paths, only "
"one target may be given"));
/* An unspecified PEG_REVISION for a working copy path defaults
to svn_opt_revision_working. */
if (peg_revision->kind == svn_opt_revision_unspecified)
peg_revision->kind = svn_opt_revision_working;
/* Get URLs for each target */
target = APR_ARRAY_IDX(targets, 0, const char *);
SVN_ERR(svn_dirent_get_absolute(&target_abspath, target, scratch_pool));
SVN_ERR(svn_wc__node_get_url(&url_or_path, ctx->wc_ctx, target_abspath,
scratch_pool, scratch_pool));
APR_ARRAY_PUSH(*relative_targets, const char *) = "";
}
/* If this is a revision type that requires access to the working copy,
* we use our initial target path to figure out where to root the RA
* session, otherwise we use our URL. */
if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind))
{
if (url_targets)
return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
_("PREV, BASE, or COMMITTED revision "
"keywords are invalid for URL"));
else
SVN_ERR(svn_dirent_get_absolute(
ra_target, APR_ARRAY_IDX(targets, 0, const char *), result_pool));
}
else
{
*ra_target = apr_pstrdup(result_pool, url_or_path);
}
return SVN_NO_ERROR;
}
/* Keep track of oldest and youngest opt revs found.
If REV is younger than *YOUNGEST_REV, or *YOUNGEST_REV is
svn_opt_revision_unspecified, then set *YOUNGEST_REV equal to REV.
If REV is older than *OLDEST_REV, or *OLDEST_REV is
svn_opt_revision_unspecified, then set *OLDEST_REV equal to REV. */
static void
find_youngest_and_oldest_revs(svn_revnum_t *youngest_rev,
svn_revnum_t *oldest_rev,
svn_revnum_t rev)
{
/* Is REV younger than YOUNGEST_REV? */
if (! SVN_IS_VALID_REVNUM(*youngest_rev)
|| rev > *youngest_rev)
*youngest_rev = rev;
if (! SVN_IS_VALID_REVNUM(*oldest_rev)
|| rev < *oldest_rev)
*oldest_rev = rev;
}
typedef struct rev_range_t
{
svn_revnum_t range_start;
svn_revnum_t range_end;
} rev_range_t;
/* Convert array of svn_opt_revision_t ranges to an array of svn_revnum_t
ranges.
Given a log target URL_OR_ABSPATH@PEG_REV and an array of
svn_opt_revision_range_t's OPT_REV_RANGES, resolve the opt revs in
OPT_REV_RANGES to svn_revnum_t's and return these in *REVISION_RANGES, an
array of rev_range_t *.
Set *YOUNGEST_REV and *OLDEST_REV to the youngest and oldest revisions
found in *REVISION_RANGES.
If the repository needs to be contacted to resolve svn_opt_revision_date or
svn_opt_revision_head revisions, then the session used to do this is
RA_SESSION; it must be an open session to any URL in the right repository.
*/
static svn_error_t*
convert_opt_rev_array_to_rev_range_array(
apr_array_header_t **revision_ranges,
svn_revnum_t *youngest_rev,
svn_revnum_t *oldest_rev,
svn_ra_session_t *ra_session,
const char *url_or_abspath,
const apr_array_header_t *opt_rev_ranges,
const svn_opt_revision_t *peg_rev,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
int i;
svn_revnum_t head_rev = SVN_INVALID_REVNUM;
/* Initialize the input/output parameters. */
*youngest_rev = *oldest_rev = SVN_INVALID_REVNUM;
/* Convert OPT_REV_RANGES to an array of rev_range_t and find the youngest
and oldest revision range that spans all of OPT_REV_RANGES. */
*revision_ranges = apr_array_make(result_pool, opt_rev_ranges->nelts,
sizeof(rev_range_t *));
for (i = 0; i < opt_rev_ranges->nelts; i++)
{
svn_opt_revision_range_t *range;
rev_range_t *rev_range;
svn_boolean_t start_same_as_end = FALSE;
range = APR_ARRAY_IDX(opt_rev_ranges, i, svn_opt_revision_range_t *);
/* Right now RANGE can be any valid pair of svn_opt_revision_t's. We
will now convert all RANGEs in place to the corresponding
svn_opt_revision_number kind. */
if ((range->start.kind != svn_opt_revision_unspecified)
&& (range->end.kind == svn_opt_revision_unspecified))
{
/* If the user specified exactly one revision, then start rev is
* set but end is not. We show the log message for just that
* revision by making end equal to start.
*
* Note that if the user requested a single dated revision, then
* this will cause the same date to be resolved twice. The
* extra code complexity to get around this slight inefficiency
* doesn't seem worth it, however. */
range->end = range->start;
}
else if (range->start.kind == svn_opt_revision_unspecified)
{
/* Default to any specified peg revision. Otherwise, if the
* first target is a URL, then we default to HEAD:0. Lastly,
* the default is BASE:0 since WC@HEAD may not exist. */
if (peg_rev->kind == svn_opt_revision_unspecified)
{
if (svn_path_is_url(url_or_abspath))
range->start.kind = svn_opt_revision_head;
else
range->start.kind = svn_opt_revision_base;
}
else
range->start = *peg_rev;
if (range->end.kind == svn_opt_revision_unspecified)
{
range->end.kind = svn_opt_revision_number;
range->end.value.number = 0;
}
}
if ((range->start.kind == svn_opt_revision_unspecified)
|| (range->end.kind == svn_opt_revision_unspecified))
{
return svn_error_create
(SVN_ERR_CLIENT_BAD_REVISION, NULL,
_("Missing required revision specification"));
}
/* Does RANGE describe a single svn_opt_revision_t? */
if (range->start.kind == range->end.kind)
{
if (range->start.kind == svn_opt_revision_number)
{
if (range->start.value.number == range->end.value.number)
start_same_as_end = TRUE;
}
else if (range->start.kind == svn_opt_revision_date)
{
if (range->start.value.date == range->end.value.date)
start_same_as_end = TRUE;
}
else
{
start_same_as_end = TRUE;
}
}
rev_range = apr_palloc(result_pool, sizeof(*rev_range));
SVN_ERR(svn_client__get_revision_number(
&rev_range->range_start, &head_rev,
ctx->wc_ctx, url_or_abspath, ra_session,
&range->start, scratch_pool));
if (start_same_as_end)
rev_range->range_end = rev_range->range_start;
else
SVN_ERR(svn_client__get_revision_number(
&rev_range->range_end, &head_rev,
ctx->wc_ctx, url_or_abspath, ra_session,
&range->end, scratch_pool));
/* Possibly update the oldest and youngest revisions requested. */
find_youngest_and_oldest_revs(youngest_rev,
oldest_rev,
rev_range->range_start);
find_youngest_and_oldest_revs(youngest_rev,
oldest_rev,
rev_range->range_end);
APR_ARRAY_PUSH(*revision_ranges, rev_range_t *) = rev_range;
}
return SVN_NO_ERROR;
}
static int
compare_rev_to_segment(const void *key_p,
const void *element_p)
{
svn_revnum_t rev =
* (svn_revnum_t *)key_p;
const svn_location_segment_t *segment =
*((const svn_location_segment_t * const *) element_p);
if (rev < segment->range_start)
return -1;
else if (rev > segment->range_end)
return 1;
else
return 0;
}
/* Run svn_ra_get_log2 for PATHS, one or more paths relative to RA_SESSION's
common parent, for each revision in REVISION_RANGES, an array of
rev_range_t.
RA_SESSION is an open session pointing to ACTUAL_LOC.
LOG_SEGMENTS is an array of svn_location_segment_t * items representing the
history of PATHS from the oldest to youngest revisions found in
REVISION_RANGES.
The TARGETS, LIMIT, DISCOVER_CHANGED_PATHS, STRICT_NODE_HISTORY,
INCLUDE_MERGED_REVISIONS, REVPROPS, REAL_RECEIVER, and REAL_RECEIVER_BATON
parameters are all as per the svn_client_log5 API. */
static svn_error_t *
run_ra_get_log(apr_array_header_t *revision_ranges,
apr_array_header_t *paths,
apr_array_header_t *log_segments,
svn_client__pathrev_t *actual_loc,
svn_ra_session_t *ra_session,
/* The following are as per svn_client_log5. */
const apr_array_header_t *targets,
int limit,
svn_boolean_t discover_changed_paths,
svn_boolean_t strict_node_history,
svn_boolean_t include_merged_revisions,
const apr_array_header_t *revprops,
svn_log_entry_receiver_t real_receiver,
void *real_receiver_baton,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
int i;
pre_15_receiver_baton_t rb = {0};
apr_pool_t *iterpool;
svn_boolean_t has_log_revprops;
SVN_ERR(svn_ra_has_capability(ra_session, &has_log_revprops,
SVN_RA_CAPABILITY_LOG_REVPROPS,
scratch_pool));
if (!has_log_revprops)
{
/* See above pre-1.5 notes. */
rb.ctx = ctx;
/* Create ra session on first use */
rb.ra_session_pool = scratch_pool;
rb.ra_session_url = actual_loc->url;
}
/* It's a bit complex to correctly handle the special revision words
* such as "BASE", "COMMITTED", and "PREV". For example, if the
* user runs
*
* $ svn log -rCOMMITTED foo.txt bar.c
*
* which committed rev should be used? The younger of the two? The
* first one? Should we just error?
*
* None of the above, I think. Rather, the committed rev of each
* target in turn should be used. This is what most users would
* expect, and is the most useful interpretation. Of course, this
* goes for the other dynamic (i.e., local) revision words too.
*
* Note that the code to do this is a bit more complex than a simple
* loop, because the user might run
*
* $ svn log -rCOMMITTED:42 foo.txt bar.c
*
* in which case we want to avoid recomputing the static revision on
* every iteration.
*
* ### FIXME: However, we can't yet handle multiple wc targets anyway.
*
* We used to iterate over each target in turn, getting the logs for
* the named range. This led to revisions being printed in strange
* order or being printed more than once. This is issue 1550.
*
* In r851673, jpieper blocked multiple wc targets in svn/log-cmd.c,
* meaning this block not only doesn't work right in that case, but isn't
* even testable that way (svn has no unit test suite; we can only test
* via the svn command). So, that check is now moved into this function
* (see above).
*
* kfogel ponders future enhancements in r844260:
* I think that's okay behavior, since the sense of the command is
* that one wants a particular range of logs for *this* file, then
* another range for *that* file, and so on. But we should
* probably put some sort of separator header between the log
* groups. Of course, libsvn_client can't just print stuff out --
* it has to take a callback from the client to do that. So we
* need to define that callback interface, then have the command
* line client pass one down here.
*
* epg wonders if the repository could send a unified stream of log
* entries if the paths and revisions were passed down.
*/
iterpool = svn_pool_create(scratch_pool);
for (i = 0; i < revision_ranges->nelts; i++)
{
const char *old_session_url;
const char *path = APR_ARRAY_IDX(targets, 0, const char *);
const char *local_abspath_or_url;
rev_range_t *range;
limit_receiver_baton_t lb;
svn_log_entry_receiver_t passed_receiver;
void *passed_receiver_baton;
const apr_array_header_t *passed_receiver_revprops;
svn_location_segment_t **matching_segment;
svn_revnum_t younger_rev;
svn_pool_clear(iterpool);
if (!svn_path_is_url(path))
SVN_ERR(svn_dirent_get_absolute(&local_abspath_or_url, path,
iterpool));
else
local_abspath_or_url = path;
range = APR_ARRAY_IDX(revision_ranges, i, rev_range_t *);
/* Issue #4355: Account for renames spanning requested
revision ranges. */
younger_rev = MAX(range->range_start, range->range_end);
matching_segment = bsearch(&younger_rev, log_segments->elts,
log_segments->nelts, log_segments->elt_size,
compare_rev_to_segment);
/* LOG_SEGMENTS is supposed to represent the history of PATHS from
the oldest to youngest revs in REVISION_RANGES. This function's
current sole caller svn_client_log5 *should* be providing
LOG_SEGMENTS that span the oldest to youngest revs in
REVISION_RANGES, even if one or more of the svn_location_segment_t's
returned have NULL path members indicating a gap in the history. So
MATCHING_SEGMENT should never be NULL, but clearly sometimes it is,
see http://svn.haxx.se/dev/archive-2013-06/0522.shtml
So to be safe we handle that case. */
if (matching_segment == NULL)
continue;
/* A segment with a NULL path means there is gap in the history.
We'll just proceed and let svn_ra_get_log2 fail with a useful
error...*/
if ((*matching_segment)->path != NULL)
{
/* ...but if there is history, then we must account for issue
#4355 and make sure our RA session is pointing at the correct
location. */
const char *segment_url = svn_path_url_add_component2(
actual_loc->repos_root_url, (*matching_segment)->path,
scratch_pool);
SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url,
ra_session,
segment_url,
scratch_pool));
}
if (has_log_revprops)
{
passed_receiver = real_receiver;
passed_receiver_baton = real_receiver_baton;
passed_receiver_revprops = revprops;
}
else
{
rb.revprops = revprops;
rb.receiver = real_receiver;
rb.baton = real_receiver_baton;
passed_receiver = pre_15_receiver;
passed_receiver_baton = &rb;
passed_receiver_revprops = svn_compat_log_revprops_in(iterpool);
}
if (limit && revision_ranges->nelts > 1)
{
lb.limit = limit;
lb.receiver = passed_receiver;
lb.baton = passed_receiver_baton;
passed_receiver = limit_receiver;
passed_receiver_baton = &lb;
}
SVN_ERR(svn_ra_get_log2(ra_session,
paths,
range->range_start,
range->range_end,
limit,
discover_changed_paths,
strict_node_history,
include_merged_revisions,
passed_receiver_revprops,
passed_receiver,
passed_receiver_baton,
iterpool));
if (limit && revision_ranges->nelts > 1)
{
limit = lb.limit;
if (limit == 0)
{
return SVN_NO_ERROR;
}
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/*** Public Interface. ***/
svn_error_t *
svn_client_log5(const apr_array_header_t *targets,
const svn_opt_revision_t *peg_revision,
const apr_array_header_t *opt_rev_ranges,
int limit,
svn_boolean_t discover_changed_paths,
svn_boolean_t strict_node_history,
svn_boolean_t include_merged_revisions,
const apr_array_header_t *revprops,
svn_log_entry_receiver_t real_receiver,
void *real_receiver_baton,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
svn_ra_session_t *ra_session;
const char *old_session_url;
const char *ra_target;
+ const char *path_or_url;
svn_opt_revision_t youngest_opt_rev;
svn_revnum_t youngest_rev;
svn_revnum_t oldest_rev;
svn_opt_revision_t peg_rev;
+ svn_client__pathrev_t *ra_session_loc;
svn_client__pathrev_t *actual_loc;
apr_array_header_t *log_segments;
apr_array_header_t *revision_ranges;
apr_array_header_t *relative_targets;
if (opt_rev_ranges->nelts == 0)
{
return svn_error_create
(SVN_ERR_CLIENT_BAD_REVISION, NULL,
_("Missing required revision specification"));
}
/* Make a copy of PEG_REVISION, we may need to change it to a
default value. */
peg_rev = *peg_revision;
SVN_ERR(resolve_log_targets(&relative_targets, &ra_target, &peg_rev,
targets, ctx, pool, pool));
- SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &actual_loc,
+ SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &ra_session_loc,
ra_target, NULL, &peg_rev, &peg_rev,
ctx, pool));
/* Convert OPT_REV_RANGES to an array of rev_range_t and find the youngest
and oldest revision range that spans all of OPT_REV_RANGES. */
SVN_ERR(convert_opt_rev_array_to_rev_range_array(&revision_ranges,
&youngest_rev,
&oldest_rev,
ra_session,
ra_target,
opt_rev_ranges, &peg_rev,
ctx, pool, pool));
+ /* For some peg revisions we must resolve revision and url via a local path
+ so use the original RA_TARGET. For others, use the potentially corrected
+ (redirected) ra session URL. */
+ if (peg_rev.kind == svn_opt_revision_previous ||
+ peg_rev.kind == svn_opt_revision_base ||
+ peg_rev.kind == svn_opt_revision_committed ||
+ peg_rev.kind == svn_opt_revision_working)
+ path_or_url = ra_target;
+ else
+ path_or_url = ra_session_loc->url;
+
/* Make ACTUAL_LOC and RA_SESSION point to the youngest operative rev. */
youngest_opt_rev.kind = svn_opt_revision_number;
youngest_opt_rev.value.number = youngest_rev;
SVN_ERR(svn_client__resolve_rev_and_url(&actual_loc, ra_session,
- ra_target, &peg_rev,
+ path_or_url, &peg_rev,
&youngest_opt_rev, ctx, pool));
SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url, ra_session,
actual_loc->url, pool));
/* Save us an RA layer round trip if we are on the repository root and
know the result in advance, or if we don't need multiple ranges.
All the revision data has already been validated.
*/
if (strcmp(actual_loc->url, actual_loc->repos_root_url) == 0
|| opt_rev_ranges->nelts <= 1)
{
svn_location_segment_t *segment = apr_pcalloc(pool, sizeof(*segment));
log_segments = apr_array_make(pool, 1, sizeof(segment));
segment->range_start = oldest_rev;
segment->range_end = actual_loc->rev;
segment->path = svn_uri_skip_ancestor(actual_loc->repos_root_url,
actual_loc->url, pool);
APR_ARRAY_PUSH(log_segments, svn_location_segment_t *) = segment;
}
else
{
/* Get the svn_location_segment_t's representing the requested log
* ranges. */
SVN_ERR(svn_client__repos_location_segments(&log_segments, ra_session,
actual_loc->url,
actual_loc->rev, /* peg */
actual_loc->rev, /* start */
oldest_rev, /* end */
ctx, pool));
}
SVN_ERR(run_ra_get_log(revision_ranges, relative_targets, log_segments,
actual_loc, ra_session, targets, limit,
discover_changed_paths, strict_node_history,
include_merged_revisions, revprops, real_receiver,
real_receiver_baton, ctx, pool));
return SVN_NO_ERROR;
}
Index: vendor/subversion/dist/subversion/libsvn_client/merge.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_client/merge.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_client/merge.c (revision 286501)
@@ -1,12737 +1,12751 @@
/*
* merge.c: merging
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
/* ==================================================================== */
/*** Includes ***/
#include <assert.h>
#include <apr_strings.h>
#include <apr_tables.h>
#include <apr_hash.h>
#include "svn_types.h"
#include "svn_hash.h"
#include "svn_wc.h"
#include "svn_delta.h"
#include "svn_diff.h"
#include "svn_mergeinfo.h"
#include "svn_client.h"
#include "svn_string.h"
#include "svn_error.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_io.h"
#include "svn_utf.h"
#include "svn_pools.h"
#include "svn_config.h"
#include "svn_props.h"
#include "svn_time.h"
#include "svn_sorts.h"
#include "svn_subst.h"
#include "svn_ra.h"
#include "client.h"
#include "mergeinfo.h"
#include "private/svn_opt_private.h"
#include "private/svn_wc_private.h"
#include "private/svn_mergeinfo_private.h"
#include "private/svn_fspath.h"
#include "private/svn_ra_private.h"
#include "private/svn_client_private.h"
#include "private/svn_subr_private.h"
#include "svn_private_config.h"
/*-----------------------------------------------------------------------*/
/* MERGEINFO MERGE SOURCE NORMALIZATION
*
* Nearly any helper function herein that accepts two URL/revision
* pairs (or equivalent struct merge_source_t) expects one of two things
* to be true:
*
* 1. that mergeinfo is not being recorded at all for this
* operation, or
*
* 2. that the pairs represent two locations along a single line
* of version history such that there are no copies in the
* history of the object between the locations when treating
* the oldest of the two locations as non-inclusive. In other
* words, if there is a copy at all between them, there is only
* one copy and its source was the oldest of the two locations.
*
* We use svn_ra_get_location_segments() to split a given range of
* revisions across an object's history into several which obey these
* rules. For example, an extract from the log of Subversion's own
* /subversion/tags/1.4.5 directory shows the following copies between
* r859500 and r866500 (omitting the '/subversion' prefix for clarity):
*
* r859598:
* A /branches/1.4.x (from /trunk:859597)
*
* r865417:
* A /tags/1.4.4 (from /branches/1.4.x:865262)
* # Notice that this copy leaves a gap between 865262 and 865417.
*
* r866420:
* A /branches/1.4.5 (from /tags/1.4.4:866419)
*
* r866425:
* D /branches/1.4.5
* A /tags/1.4.5 (from /branches/1.4.5:866424)
*
* In graphical form:
*
* 859500 859597 865262 866419 866424 866500
* . . . . . .
* trunk ------------------------------------------------
* \ . . .
* branches/1.4.x A-------------------------------------
* . \______ . .
* . \ . .
* tags/1.4.4 . A-----------------------
* . . \ .
* branches/1.4.5 . . A------D
* . . . \.
* tags/1.4.5 . . . A---------
* . . . .
* 859598 865417 866420 866425
*
* A merge of the difference between r859500 and r866500 of this directory
* gets split into sequential merges of the following location pairs.
*
* 859500 859597 865262 865416 866419 866424 866500
* . . . . . . .
* trunk (======] . . . . .
* . . . . .
* trunk ( . . . . .
* branches/1.4.x ======] . . . .
* . . . .
* branches/1.4.x ( . . . .
* tags/1.4.4 =============] . .
* implicit_src_gap (======] . . .
* . . .
* tags/1.4.4 ( . .
* branches/1.4.5 ======] .
* . .
* branches/1.4.5 ( .
* tags/1.4.5 ======]
*
* which are represented in merge_source_t as:
*
* [/trunk:859500, /trunk:859597]
* (recorded in svn:mergeinfo as /trunk:859501-859597)
*
* [/trunk:859597, /branches/1.4.x:865262]
* (recorded in svn:mergeinfo as /branches/1.4.x:859598-865262)
*
* [/branches/1.4.x:865262, /tags/1.4.4@866419]
* (recorded in svn:mergeinfo as /tags/1.4.4:865263-866419)
* (and there is a gap, the revision range [865262, 865416])
*
* [/tags/1.4.4@866419, /branches/1.4.5@866424]
* (recorded in svn:mergeinfo as /branches/1.4.5:866420-866424)
*
* [/branches/1.4.5@866424, /tags/1.4.5@866500]
* (recorded in svn:mergeinfo as /tags/1.4.5:866425-866500)
*
* Our helper functions would then operate on one of these location
* pairs at a time.
*/
/* WHICH SVN_CLIENT_MERGE* API DO I WANT?
*
* libsvn_client has three public merge APIs; they are all wrappers
* around the do_merge engine. Which one to use depends on the number
* of URLs passed as arguments and whether or not specific merge
* ranges (-c/-r) are specified.
*
* 1 URL 2 URLs
* +----+--------------------------------+---------------------+
* | -c | mergeinfo-driven | |
* | or | cherrypicking | |
* | -r | (svn_client_merge_peg) | |
* |----+--------------------------------+ |
* | | mergeinfo-driven | unsupported |
* | | 'cherry harvest', i.e. merge | |
* | | all revisions from URL that | |
* | no | have not already been merged | |
* | -c | (svn_client_merge_peg) | |
* | or +--------------------------------+---------------------+
* | -r | mergeinfo-driven | mergeinfo-writing |
* | | whole-branch | diff-and-apply |
* | | heuristic merge | (svn_client_merge) |
* | | (svn_client_merge_reintegrate) | |
* +----+--------------------------------+---------------------+
*
*
*/
/* THE CHILDREN_WITH_MERGEINFO ARRAY
*
* Many of the helper functions in this file pass around an
* apr_array_header_t *CHILDREN_WITH_MERGEINFO. This is a depth first
* sorted array filled with svn_client__merge_path_t * describing the
* merge target and any of its subtrees which have explicit mergeinfo
* or otherwise need special attention during a merge.
*
* During mergeinfo unaware merges, CHILDREN_WITH_MERGEINFO contains
* contains only one element (added by do_mergeinfo_unaware_dir_merge)
* describing a contiguous range to be merged to the WC merge target.
*
* During mergeinfo aware merges CHILDREN_WITH_MERGEINFO is created
* by get_mergeinfo_paths() and outside of that function and its helpers
* should always meet the criteria dictated in get_mergeinfo_paths()'s doc
* string. The elements of CHILDREN_WITH_MERGEINFO should never be NULL.
*
* For clarification on mergeinfo aware vs. mergeinfo unaware merges, see
* the doc string for HONOR_MERGEINFO().
*/
/*-----------------------------------------------------------------------*/
/*** Repos-Diff Editor Callbacks ***/
/* */
typedef struct merge_source_t
{
/* "left" side URL and revision (inclusive iff youngest) */
const svn_client__pathrev_t *loc1;
/* "right" side URL and revision (inclusive iff youngest) */
const svn_client__pathrev_t *loc2;
/* True iff LOC1 is an ancestor of LOC2 or vice-versa (history-wise). */
svn_boolean_t ancestral;
} merge_source_t;
/* Description of the merge target root node (a WC working node) */
typedef struct merge_target_t
{
/* Absolute path to the WC node */
const char *abspath;
/* The repository location of the base node of the target WC. If the node
* is locally added, then URL & REV are NULL & SVN_INVALID_REVNUM.
* REPOS_ROOT_URL and REPOS_UUID are always valid. */
svn_client__pathrev_t loc;
} merge_target_t;
typedef struct merge_cmd_baton_t {
svn_boolean_t force_delete; /* Delete a file/dir even if modified */
svn_boolean_t dry_run;
svn_boolean_t record_only; /* Whether to merge only mergeinfo
differences. */
svn_boolean_t same_repos; /* Whether the merge source repository
is the same repository as the
target. Defaults to FALSE if DRY_RUN
is TRUE.*/
svn_boolean_t mergeinfo_capable; /* Whether the merge source server
is capable of Merge Tracking. */
svn_boolean_t ignore_mergeinfo; /* Don't honor mergeinfo; see
doc string of do_merge(). FALSE if
MERGE_SOURCE->ancestral is FALSE. */
svn_boolean_t diff_ignore_ancestry; /* Diff unrelated nodes as if related; see
doc string of do_merge(). FALSE if
MERGE_SOURCE->ancestral is FALSE. */
svn_boolean_t reintegrate_merge; /* Whether this is a --reintegrate
merge or not. */
const merge_target_t *target; /* Description of merge target node */
/* The left and right URLs and revs. The value of this field changes to
reflect the merge_source_t *currently* being merged by do_merge(). */
merge_source_t merge_source;
/* Rangelist containing single range which describes the gap, if any,
in the natural history of the merge source currently being processed.
See http://subversion.tigris.org/issues/show_bug.cgi?id=3432.
Updated during each call to do_directory_merge(). May be NULL if there
is no gap. */
svn_rangelist_t *implicit_src_gap;
svn_client_ctx_t *ctx; /* Client context for callbacks, etc. */
/* The list of any paths which remained in conflict after a
resolution attempt was made. We track this in-memory, rather
than just using WC entry state, since the latter doesn't help us
when in dry_run mode.
### And because we only want to resolve conflicts that were
generated by this merge, not pre-existing ones? */
apr_hash_t *conflicted_paths;
/* A list of absolute paths which had no explicit mergeinfo prior to the
merge but got explicit mergeinfo added by the merge. This is populated
by merge_change_props() and is allocated in POOL so it is subject to the
lifetime limitations of POOL. Is NULL if no paths are found which
meet the criteria or DRY_RUN is true. */
apr_hash_t *paths_with_new_mergeinfo;
/* A list of absolute paths whose mergeinfo doesn't need updating after
the merge. This can be caused by the removal of mergeinfo by the merge
or by deleting the node itself. This is populated by merge_change_props()
and the delete callbacks and is allocated in POOL so it is subject to the
lifetime limitations of POOL. Is NULL if no paths are found which
meet the criteria or DRY_RUN is true. */
apr_hash_t *paths_with_deleted_mergeinfo;
/* The list of absolute skipped paths, which should be examined and
cleared after each invocation of the callback. The paths
are absolute. Is NULL if MERGE_B->MERGE_SOURCE->ancestral and
MERGE_B->REINTEGRATE_MERGE are both false. */
apr_hash_t *skipped_abspaths;
/* The list of absolute merged paths. Unused if MERGE_B->MERGE_SOURCE->ancestral
and MERGE_B->REINTEGRATE_MERGE are both false. */
apr_hash_t *merged_abspaths;
/* A hash of (const char *) absolute WC paths mapped to the same which
represent the roots of subtrees added by the merge. */
apr_hash_t *added_abspaths;
/* A list of tree conflict victim absolute paths which may be NULL. */
apr_hash_t *tree_conflicted_abspaths;
/* The diff3_cmd in ctx->config, if any, else null. We could just
extract this as needed, but since more than one caller uses it,
we just set it up when this baton is created. */
const char *diff3_cmd;
const apr_array_header_t *merge_options;
/* Array of file extension patterns to preserve as extensions in
generated conflict files. */
const apr_array_header_t *ext_patterns;
/* RA sessions used throughout a merge operation. Opened/re-parented
as needed.
NOTE: During the actual merge editor drive, RA_SESSION1 is used
for the primary editing and RA_SESSION2 for fetching additional
information -- as necessary -- from the repository. So during
this phase of the merge, you *must not* reparent RA_SESSION1; use
(temporarily reparenting if you must) RA_SESSION2 instead. */
svn_ra_session_t *ra_session1;
svn_ra_session_t *ra_session2;
/* During the merge, *USE_SLEEP is set to TRUE if a sleep will be required
afterwards to ensure timestamp integrity, or unchanged if not. */
svn_boolean_t *use_sleep;
/* Pool which has a lifetime limited to one iteration over a given
merge source, i.e. it is cleared on every call to do_directory_merge()
or do_file_merge() in do_merge(). */
apr_pool_t *pool;
/* State for notify_merge_begin() */
struct notify_begin_state_t
{
/* Cache of which abspath was last notified. */
const char *last_abspath;
/* Reference to the one-and-only CHILDREN_WITH_MERGEINFO (see global
comment) or a similar list for single-file-merges */
const apr_array_header_t *nodes_with_mergeinfo;
} notify_begin;
} merge_cmd_baton_t;
/* Return TRUE iff we should be taking account of mergeinfo in deciding what
changes to merge, for the merge described by MERGE_B. Specifically, that
is if the merge source server is capable of merge tracking, the left-side
merge source is an ancestor of the right-side (or vice-versa), the merge
source is in the same repository as the merge target, and we are not
ignoring mergeinfo. */
#define HONOR_MERGEINFO(merge_b) ((merge_b)->mergeinfo_capable \
&& (merge_b)->merge_source.ancestral \
&& (merge_b)->same_repos \
&& (! (merge_b)->ignore_mergeinfo))
/* Return TRUE iff we should be recording mergeinfo for the merge described
by MERGE_B. Specifically, that is if we are honoring mergeinfo and the
merge is not a dry run. */
#define RECORD_MERGEINFO(merge_b) (HONOR_MERGEINFO(merge_b) \
&& !(merge_b)->dry_run)
/*-----------------------------------------------------------------------*/
/*** Utilities ***/
/* Return TRUE iff the session URL of RA_SESSION is equal to URL. Useful in
* asserting preconditions. */
static svn_boolean_t
session_url_is(svn_ra_session_t *ra_session,
const char *url,
apr_pool_t *scratch_pool)
{
const char *session_url;
svn_error_t *err
= svn_ra_get_session_url(ra_session, &session_url, scratch_pool);
SVN_ERR_ASSERT_NO_RETURN(! err);
return strcmp(url, session_url) == 0;
}
/* Return a new merge_source_t structure, allocated in RESULT_POOL,
* initialized with deep copies of LOC1 and LOC2 and ANCESTRAL. */
static merge_source_t *
merge_source_create(const svn_client__pathrev_t *loc1,
const svn_client__pathrev_t *loc2,
svn_boolean_t ancestral,
apr_pool_t *result_pool)
{
merge_source_t *s
= apr_palloc(result_pool, sizeof(*s));
s->loc1 = svn_client__pathrev_dup(loc1, result_pool);
s->loc2 = svn_client__pathrev_dup(loc2, result_pool);
s->ancestral = ancestral;
return s;
}
/* Return a deep copy of SOURCE, allocated in RESULT_POOL. */
static merge_source_t *
merge_source_dup(const merge_source_t *source,
apr_pool_t *result_pool)
{
merge_source_t *s = apr_palloc(result_pool, sizeof(*s));
s->loc1 = svn_client__pathrev_dup(source->loc1, result_pool);
s->loc2 = svn_client__pathrev_dup(source->loc2, result_pool);
s->ancestral = source->ancestral;
return s;
}
/* Return SVN_ERR_UNSUPPORTED_FEATURE if URL is not inside the repository
of LOCAL_ABSPATH. Use SCRATCH_POOL for temporary allocations. */
static svn_error_t *
check_repos_match(const merge_target_t *target,
const char *local_abspath,
const char *url,
apr_pool_t *scratch_pool)
{
if (!svn_uri__is_ancestor(target->loc.repos_root_url, url))
return svn_error_createf(
SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("URL '%s' of '%s' is not in repository '%s'"),
url, svn_dirent_local_style(local_abspath, scratch_pool),
target->loc.repos_root_url);
return SVN_NO_ERROR;
}
/* Return TRUE iff the repository of LOCATION1 is the same as
* that of LOCATION2. If STRICT_URLS is true, the URLs must
* match (and the UUIDs, just to be sure), otherwise just the UUIDs must
* match and the URLs can differ (a common case is http versus https). */
static svn_boolean_t
is_same_repos(const svn_client__pathrev_t *location1,
const svn_client__pathrev_t *location2,
svn_boolean_t strict_urls)
{
if (strict_urls)
return (strcmp(location1->repos_root_url, location2->repos_root_url) == 0
&& strcmp(location1->repos_uuid, location2->repos_uuid) == 0);
else
return (strcmp(location1->repos_uuid, location2->repos_uuid) == 0);
}
/* If the repository identified of LOCATION1 is not the same as that
* of LOCATION2, throw a SVN_ERR_CLIENT_UNRELATED_RESOURCES
* error mentioning PATH1 and PATH2. For STRICT_URLS, see is_same_repos().
*/
static svn_error_t *
check_same_repos(const svn_client__pathrev_t *location1,
const char *path1,
const svn_client__pathrev_t *location2,
const char *path2,
svn_boolean_t strict_urls,
apr_pool_t *scratch_pool)
{
if (! is_same_repos(location1, location2, strict_urls))
return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
_("'%s' must be from the same repository as "
"'%s'"), path1, path2);
return SVN_NO_ERROR;
}
/* Store LOCAL_ABSPATH in PATH_HASH after duplicating it into the pool
containing PATH_HASH. */
static APR_INLINE void
store_path(apr_hash_t *path_hash, const char *local_abspath)
{
const char *dup_path = apr_pstrdup(apr_hash_pool_get(path_hash),
local_abspath);
svn_hash_sets(path_hash, dup_path, dup_path);
}
/* Store LOCAL_ABSPATH in *PATH_HASH_P after duplicating it into the pool
containing *PATH_HASH_P. If *PATH_HASH_P is NULL, then first set
*PATH_HASH_P to a new hash allocated from POOL. */
static APR_INLINE void
alloc_and_store_path(apr_hash_t **path_hash_p,
const char *local_abspath,
apr_pool_t *pool)
{
if (! *path_hash_p)
*path_hash_p = apr_hash_make(pool);
store_path(*path_hash_p, local_abspath);
}
/* Return whether any WC path was put in conflict by the merge
operation corresponding to MERGE_B. */
static APR_INLINE svn_boolean_t
is_path_conflicted_by_merge(merge_cmd_baton_t *merge_b)
{
return (merge_b->conflicted_paths &&
apr_hash_count(merge_b->conflicted_paths) > 0);
}
/* Return a state indicating whether the WC metadata matches the
* node kind on disk of the local path LOCAL_ABSPATH.
* Use MERGE_B to determine the dry-run details; particularly, if a dry run
* noted that it deleted this path, assume matching node kinds (as if both
* kinds were svn_node_none).
*
* - Return svn_wc_notify_state_inapplicable if the node kind matches.
* - Return 'obstructed' if there is a node on disk where none or a
* different kind is expected, or if the disk node cannot be read.
* - Return 'missing' if there is no node on disk but one is expected.
* Also return 'missing' for server-excluded nodes (not here due to
* authz or other reasons determined by the server).
*
* Optionally return a bit more info for interested users.
**/
static svn_error_t *
perform_obstruction_check(svn_wc_notify_state_t *obstruction_state,
svn_boolean_t *deleted,
svn_boolean_t *excluded,
svn_node_kind_t *kind,
svn_depth_t *parent_depth,
const merge_cmd_baton_t *merge_b,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
svn_wc_context_t *wc_ctx = merge_b->ctx->wc_ctx;
svn_node_kind_t wc_kind;
svn_boolean_t check_root;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
*obstruction_state = svn_wc_notify_state_inapplicable;
if (deleted)
*deleted = FALSE;
if (kind)
*kind = svn_node_none;
if (kind == NULL)
kind = &wc_kind;
check_root = ! strcmp(local_abspath, merge_b->target->abspath);
SVN_ERR(svn_wc__check_for_obstructions(obstruction_state,
kind,
deleted,
excluded,
parent_depth,
wc_ctx, local_abspath,
check_root,
scratch_pool));
return SVN_NO_ERROR;
}
/* Create *LEFT and *RIGHT conflict versions for conflict victim
* at VICTIM_ABSPATH, with kind NODE_KIND, using information obtained
* from MERGE_SOURCE and TARGET.
* Allocate returned conflict versions in RESULT_POOL. */
static svn_error_t *
make_conflict_versions(const svn_wc_conflict_version_t **left,
const svn_wc_conflict_version_t **right,
const char *victim_abspath,
svn_node_kind_t node_kind,
const merge_source_t *merge_source,
const merge_target_t *target,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *child = svn_dirent_skip_ancestor(target->abspath,
victim_abspath);
const char *left_relpath, *right_relpath;
SVN_ERR_ASSERT(child != NULL);
left_relpath = svn_client__pathrev_relpath(merge_source->loc1,
scratch_pool);
right_relpath = svn_client__pathrev_relpath(merge_source->loc2,
scratch_pool);
*left = svn_wc_conflict_version_create2(
merge_source->loc1->repos_root_url,
merge_source->loc1->repos_uuid,
svn_relpath_join(left_relpath, child, scratch_pool),
merge_source->loc1->rev, node_kind, result_pool);
*right = svn_wc_conflict_version_create2(
merge_source->loc2->repos_root_url,
merge_source->loc2->repos_uuid,
svn_relpath_join(right_relpath, child, scratch_pool),
merge_source->loc2->rev, node_kind, result_pool);
return SVN_NO_ERROR;
}
/* Helper for filter_self_referential_mergeinfo()
*MERGEINFO is a non-empty, non-null collection of mergeinfo.
Remove all mergeinfo from *MERGEINFO that describes revision ranges
greater than REVISION. Put a copy of any removed mergeinfo, allocated
in POOL, into *YOUNGER_MERGEINFO.
If no mergeinfo is removed from *MERGEINFO then *YOUNGER_MERGEINFO is set
to NULL. If all mergeinfo is removed from *MERGEINFO then *MERGEINFO is
set to NULL.
*/
static svn_error_t*
split_mergeinfo_on_revision(svn_mergeinfo_t *younger_mergeinfo,
svn_mergeinfo_t *mergeinfo,
svn_revnum_t revision,
apr_pool_t *pool)
{
apr_hash_index_t *hi;
apr_pool_t *iterpool = svn_pool_create(pool);
*younger_mergeinfo = NULL;
for (hi = apr_hash_first(pool, *mergeinfo); hi; hi = apr_hash_next(hi))
{
int i;
const char *merge_source_path = svn__apr_hash_index_key(hi);
svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
svn_pool_clear(iterpool);
for (i = 0; i < rangelist->nelts; i++)
{
svn_merge_range_t *range =
APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
if (range->end <= revision)
{
/* This entirely of this range is as old or older than
REVISION, so leave it in *MERGEINFO. */
continue;
}
else
{
/* Since the rangelists in svn_mergeinfo_t's are sorted in
increasing order we know that part or all of *this* range
and *all* of the remaining ranges in *RANGELIST are younger
than REVISION. Remove the younger rangelists from
*MERGEINFO and put them in *YOUNGER_MERGEINFO. */
int j;
svn_rangelist_t *younger_rangelist =
apr_array_make(pool, 1, sizeof(svn_merge_range_t *));
for (j = i; j < rangelist->nelts; j++)
{
svn_merge_range_t *younger_range = svn_merge_range_dup(
APR_ARRAY_IDX(rangelist, j, svn_merge_range_t *), pool);
/* REVISION might intersect with the first range where
range->end > REVISION. If that is the case then split
the current range into two, putting the younger half
into *YOUNGER_MERGEINFO and leaving the older half in
*MERGEINFO. */
if (j == i && range->start + 1 <= revision)
younger_range->start = range->end = revision;
APR_ARRAY_PUSH(younger_rangelist, svn_merge_range_t *) =
younger_range;
}
/* So far we've only been manipulating rangelists, now we
actually create *YOUNGER_MERGEINFO and then remove the older
ranges from *MERGEINFO */
if (!(*younger_mergeinfo))
*younger_mergeinfo = apr_hash_make(pool);
svn_hash_sets(*younger_mergeinfo, merge_source_path,
younger_rangelist);
SVN_ERR(svn_mergeinfo_remove2(mergeinfo, *younger_mergeinfo,
*mergeinfo, TRUE, pool, iterpool));
break; /* ...out of for (i = 0; i < rangelist->nelts; i++) */
}
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Make a copy of PROPCHANGES (array of svn_prop_t) into *TRIMMED_PROPCHANGES,
omitting any svn:mergeinfo changes. */
static svn_error_t *
omit_mergeinfo_changes(apr_array_header_t **trimmed_propchanges,
const apr_array_header_t *propchanges,
apr_pool_t *result_pool)
{
int i;
*trimmed_propchanges = apr_array_make(result_pool,
propchanges->nelts,
sizeof(svn_prop_t));
for (i = 0; i < propchanges->nelts; ++i)
{
const svn_prop_t *change = &APR_ARRAY_IDX(propchanges, i, svn_prop_t);
/* If this property is not svn:mergeinfo, then copy it. */
if (strcmp(change->name, SVN_PROP_MERGEINFO) != 0)
APR_ARRAY_PUSH(*trimmed_propchanges, svn_prop_t) = *change;
}
return SVN_NO_ERROR;
}
/* Helper for merge_props_changed().
*PROPS is an array of svn_prop_t structures representing regular properties
to be added to the working copy TARGET_ABSPATH.
The merge source and target are assumed to be in the same repository.
Filter out mergeinfo property additions to TARGET_ABSPATH when
those additions refer to the same line of history as TARGET_ABSPATH as
described below.
Examine the added mergeinfo, looking at each range (or single rev)
of each source path. If a source_path/range refers to the same line of
history as TARGET_ABSPATH (pegged at its base revision), then filter out
that range. If the entire rangelist for a given path is filtered then
filter out the path as well.
RA_SESSION is an open RA session to the repository
in which both the source and target live, else RA_SESSION is not used. It
may be temporarily reparented as needed by this function.
Use CTX for any further client operations.
If any filtering occurs, set outgoing *PROPS to a shallow copy (allocated
in POOL) of incoming *PROPS minus the filtered mergeinfo. */
static svn_error_t *
filter_self_referential_mergeinfo(apr_array_header_t **props,
const char *target_abspath,
svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
apr_array_header_t *adjusted_props;
int i;
apr_pool_t *iterpool;
svn_boolean_t is_copy;
const char *repos_relpath;
svn_client__pathrev_t target_base;
/* If PATH itself has been added there is no need to filter. */
SVN_ERR(svn_wc__node_get_origin(&is_copy, &target_base.rev, &repos_relpath,
&target_base.repos_root_url,
&target_base.repos_uuid, NULL,
ctx->wc_ctx, target_abspath, FALSE,
pool, pool));
if (is_copy || !repos_relpath)
return SVN_NO_ERROR; /* A copy or a local addition */
target_base.url = svn_path_url_add_component2(target_base.repos_root_url,
repos_relpath, pool);
adjusted_props = apr_array_make(pool, (*props)->nelts, sizeof(svn_prop_t));
iterpool = svn_pool_create(pool);
for (i = 0; i < (*props)->nelts; ++i)
{
svn_prop_t *prop = &APR_ARRAY_IDX((*props), i, svn_prop_t);
svn_mergeinfo_t mergeinfo, younger_mergeinfo;
svn_mergeinfo_t filtered_mergeinfo = NULL;
svn_mergeinfo_t filtered_younger_mergeinfo = NULL;
svn_error_t *err;
/* If this property isn't mergeinfo or is NULL valued (i.e. prop removal)
or empty mergeinfo it does not require any special handling. There
is nothing to filter out of empty mergeinfo and the concept of
filtering doesn't apply if we are trying to remove mergeinfo
entirely. */
if ((strcmp(prop->name, SVN_PROP_MERGEINFO) != 0)
|| (! prop->value) /* Removal of mergeinfo */
|| (! prop->value->len)) /* Empty mergeinfo */
{
APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *prop;
continue;
}
svn_pool_clear(iterpool);
/* Non-empty mergeinfo; filter self-referential mergeinfo out. */
/* Parse the incoming mergeinfo to allow easier manipulation. */
err = svn_mergeinfo_parse(&mergeinfo, prop->value->data, iterpool);
if (err)
{
/* Issue #3896: If we can't parse it, we certainly can't
filter it. */
if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
{
svn_error_clear(err);
APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *prop;
continue;
}
else
{
return svn_error_trace(err);
}
}
/* The working copy target PATH is at BASE_REVISION. Divide the
incoming mergeinfo into two groups. One where all revision ranges
are as old or older than BASE_REVISION and one where all revision
ranges are younger.
Note: You may be wondering why we do this.
For the incoming mergeinfo "older" than target's base revision we
can filter out self-referential mergeinfo efficiently using
svn_client__get_history_as_mergeinfo(). We simply look at PATH's
natural history as mergeinfo and remove that from any incoming
mergeinfo.
For mergeinfo "younger" than the base revision we can't use
svn_ra_get_location_segments() to look into PATH's future
history. Instead we must use svn_client__repos_locations() and
look at each incoming source/range individually and see if PATH
at its base revision and PATH at the start of the incoming range
exist on the same line of history. If they do then we can filter
out the incoming range. But since we have to do this for each
range there is a substantial performance penalty to pay if the
incoming ranges are not contiguous, i.e. we call
svn_client__repos_locations for each discrete range and incur
the cost of a roundtrip communication with the repository. */
SVN_ERR(split_mergeinfo_on_revision(&younger_mergeinfo,
&mergeinfo,
target_base.rev,
iterpool));
/* Filter self-referential mergeinfo from younger_mergeinfo. */
if (younger_mergeinfo)
{
apr_hash_index_t *hi;
const char *merge_source_root_url;
SVN_ERR(svn_ra_get_repos_root2(ra_session,
&merge_source_root_url, iterpool));
for (hi = apr_hash_first(iterpool, younger_mergeinfo);
hi; hi = apr_hash_next(hi))
{
int j;
const char *source_path = svn__apr_hash_index_key(hi);
svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
const char *merge_source_url;
svn_rangelist_t *adjusted_rangelist =
apr_array_make(iterpool, 0, sizeof(svn_merge_range_t *));
merge_source_url =
svn_path_url_add_component2(merge_source_root_url,
source_path + 1, iterpool);
for (j = 0; j < rangelist->nelts; j++)
{
svn_error_t *err2;
svn_client__pathrev_t *start_loc;
svn_merge_range_t *range =
APR_ARRAY_IDX(rangelist, j, svn_merge_range_t *);
/* Because the merge source normalization code
ensures mergeinfo refers to real locations on
the same line of history, there's no need to
look at the whole range, just the start. */
/* Check if PATH@BASE_REVISION exists at
RANGE->START on the same line of history.
(start+1 because RANGE->start is not inclusive.) */
err2 = svn_client__repos_location(&start_loc, ra_session,
&target_base,
range->start + 1,
ctx, iterpool, iterpool);
if (err2)
{
if (err2->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES
|| err2->apr_err == SVN_ERR_FS_NOT_FOUND
|| err2->apr_err == SVN_ERR_FS_NO_SUCH_REVISION)
{
/* PATH@BASE_REVISION didn't exist at
RANGE->START + 1 or is unrelated to the
resource PATH@RANGE->START. Some of the
requested revisions may not even exist in
the repository; a real possibility since
mergeinfo is hand editable. In all of these
cases clear and ignore the error and don't
do any filtering.
Note: In this last case it is possible that
we will allow self-referential mergeinfo to
be applied, but fixing it here is potentially
very costly in terms of finding what part of
a range is actually valid. Simply allowing
the merge to proceed without filtering the
offending range seems the least worst
option. */
svn_error_clear(err2);
err2 = NULL;
APR_ARRAY_PUSH(adjusted_rangelist,
svn_merge_range_t *) = range;
}
else
{
return svn_error_trace(err2);
}
}
else
{
/* PATH@BASE_REVISION exists on the same
line of history at RANGE->START and RANGE->END.
Now check that PATH@BASE_REVISION's path
names at RANGE->START and RANGE->END are the same.
If the names are not the same then the mergeinfo
describing PATH@RANGE->START through
PATH@RANGE->END actually belong to some other
line of history and we want to record this
mergeinfo, not filter it. */
if (strcmp(start_loc->url, merge_source_url) != 0)
{
APR_ARRAY_PUSH(adjusted_rangelist,
svn_merge_range_t *) = range;
}
}
/* else no need to add, this mergeinfo is
all on the same line of history. */
} /* for (j = 0; j < rangelist->nelts; j++) */
/* Add any rangelists for source_path that are not
self-referential. */
if (adjusted_rangelist->nelts)
{
if (!filtered_younger_mergeinfo)
filtered_younger_mergeinfo = apr_hash_make(iterpool);
svn_hash_sets(filtered_younger_mergeinfo, source_path,
adjusted_rangelist);
}
} /* Iteration over each merge source in younger_mergeinfo. */
} /* if (younger_mergeinfo) */
/* Filter self-referential mergeinfo from "older" mergeinfo. */
if (mergeinfo)
{
svn_mergeinfo_t implicit_mergeinfo;
SVN_ERR(svn_client__get_history_as_mergeinfo(
&implicit_mergeinfo, NULL,
&target_base, target_base.rev, SVN_INVALID_REVNUM,
ra_session, ctx, iterpool));
/* Remove PATH's implicit mergeinfo from the incoming mergeinfo. */
SVN_ERR(svn_mergeinfo_remove2(&filtered_mergeinfo,
implicit_mergeinfo,
mergeinfo, TRUE, iterpool, iterpool));
}
/* Combine whatever older and younger filtered mergeinfo exists
into filtered_mergeinfo. */
if (filtered_mergeinfo && filtered_younger_mergeinfo)
SVN_ERR(svn_mergeinfo_merge2(filtered_mergeinfo,
filtered_younger_mergeinfo, iterpool,
iterpool));
else if (filtered_younger_mergeinfo)
filtered_mergeinfo = filtered_younger_mergeinfo;
/* If there is any incoming mergeinfo remaining after filtering
then put it in adjusted_props. */
if (filtered_mergeinfo && apr_hash_count(filtered_mergeinfo))
{
/* Convert filtered_mergeinfo to a svn_prop_t and put it
back in the array. */
svn_string_t *filtered_mergeinfo_str;
svn_prop_t *adjusted_prop = apr_pcalloc(pool,
sizeof(*adjusted_prop));
SVN_ERR(svn_mergeinfo_to_string(&filtered_mergeinfo_str,
filtered_mergeinfo,
pool));
adjusted_prop->name = SVN_PROP_MERGEINFO;
adjusted_prop->value = filtered_mergeinfo_str;
APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *adjusted_prop;
}
}
svn_pool_destroy(iterpool);
*props = adjusted_props;
return SVN_NO_ERROR;
}
/* Prepare a set of property changes PROPCHANGES to be used for a merge
operation on LOCAL_ABSPATH.
Remove all non-regular prop-changes (entry-props and WC-props).
Remove all non-mergeinfo prop-changes if it's a record-only merge.
Remove self-referential mergeinfo (### in some cases...)
Remove foreign-repository mergeinfo (### in some cases...)
Store the resulting property changes in *PROP_UPDATES.
Store information on where mergeinfo is updated in MERGE_B.
Used for both file and directory property merges. */
static svn_error_t *
prepare_merge_props_changed(const apr_array_header_t **prop_updates,
const char *local_abspath,
const apr_array_header_t *propchanges,
merge_cmd_baton_t *merge_b,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_array_header_t *props;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
/* We only want to merge "regular" version properties: by
definition, 'svn merge' shouldn't touch any data within .svn/ */
SVN_ERR(svn_categorize_props(propchanges, NULL, NULL, &props,
result_pool));
/* If we are only applying mergeinfo changes then we need to do
additional filtering of PROPS so it contains only mergeinfo changes. */
if (merge_b->record_only && props->nelts)
{
apr_array_header_t *mergeinfo_props =
apr_array_make(result_pool, 1, sizeof(svn_prop_t));
int i;
for (i = 0; i < props->nelts; i++)
{
svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
if (strcmp(prop->name, SVN_PROP_MERGEINFO) == 0)
{
APR_ARRAY_PUSH(mergeinfo_props, svn_prop_t) = *prop;
break;
}
}
props = mergeinfo_props;
}
if (props->nelts)
{
/* Issue #3383: We don't want mergeinfo from a foreign repos.
If this is a merge from a foreign repository we must strip all
incoming mergeinfo (including mergeinfo deletions). */
if (! merge_b->same_repos)
SVN_ERR(omit_mergeinfo_changes(&props, props, result_pool));
/* If this is a forward merge then don't add new mergeinfo to
PATH that is already part of PATH's own history, see
http://svn.haxx.se/dev/archive-2008-09/0006.shtml. If the
merge sources are not ancestral then there is no concept of a
'forward' or 'reverse' merge and we filter unconditionally. */
if (merge_b->merge_source.loc1->rev < merge_b->merge_source.loc2->rev
|| !merge_b->merge_source.ancestral)
{
if (HONOR_MERGEINFO(merge_b) || merge_b->reintegrate_merge)
SVN_ERR(filter_self_referential_mergeinfo(&props,
local_abspath,
merge_b->ra_session2,
merge_b->ctx,
result_pool));
}
}
*prop_updates = props;
/* Make a record in BATON if we find a PATH where mergeinfo is added
where none existed previously or PATH is having its existing
mergeinfo deleted. */
if (props->nelts)
{
int i;
for (i = 0; i < props->nelts; ++i)
{
svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
if (strcmp(prop->name, SVN_PROP_MERGEINFO) == 0)
{
/* Does LOCAL_ABSPATH have any pristine mergeinfo? */
svn_boolean_t has_pristine_mergeinfo = FALSE;
apr_hash_t *pristine_props;
SVN_ERR(svn_wc_get_pristine_props(&pristine_props,
merge_b->ctx->wc_ctx,
local_abspath,
scratch_pool,
scratch_pool));
if (pristine_props
&& svn_hash_gets(pristine_props, SVN_PROP_MERGEINFO))
has_pristine_mergeinfo = TRUE;
if (!has_pristine_mergeinfo && prop->value)
{
alloc_and_store_path(&merge_b->paths_with_new_mergeinfo,
local_abspath, merge_b->pool);
}
else if (has_pristine_mergeinfo && !prop->value)
{
alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo,
local_abspath, merge_b->pool);
}
}
}
}
return SVN_NO_ERROR;
}
#define CONFLICT_REASON_NONE ((svn_wc_conflict_reason_t)-1)
#define CONFLICT_REASON_SKIP ((svn_wc_conflict_reason_t)-2)
#define CONFLICT_REASON_SKIP_WC ((svn_wc_conflict_reason_t)-3)
/* Baton used for testing trees for being editted while performing tree
conflict detection for incoming deletes */
struct dir_delete_baton_t
{
/* Reference to dir baton of directory that is the root of the deletion */
struct merge_dir_baton_t *del_root;
/* Boolean indicating that some edit is found. Allows avoiding more work */
svn_boolean_t found_edit;
/* A list of paths that are compared. Kept up to date until FOUND_EDIT is
set to TRUE */
apr_hash_t *compared_abspaths;
};
/* Baton for the merge_dir_*() functions. Initialized in merge_dir_opened() */
struct merge_dir_baton_t
{
/* Reference to the parent baton, unless the parent is the anchor, in which
case PARENT_BATON is NULL */
struct merge_dir_baton_t *parent_baton;
/* The pool containing this baton. Use for RESULT_POOL for storing in this
baton */
apr_pool_t *pool;
/* This directory doesn't have a representation in the working copy, so any
operation on it will be skipped and possibly cause a tree conflict on the
shadow root */
svn_boolean_t shadowed;
/* This node or one of its descendants received operational changes from the
merge. If this node is the shadow root its tree conflict status has been
applied */
svn_boolean_t edited;
/* If a tree conflict will be installed once edited, it's reason. If a skip
should be produced its reason. Otherwise CONFLICT_REASON_NONE for no tree
conflict.
Special values:
CONFLICT_REASON_SKIP:
The node will be skipped with content and property state as stored in
SKIP_REASON.
CONFLICT_REASON_SKIP_WC:
The node will be skipped as an obstructing working copy.
*/
svn_wc_conflict_reason_t tree_conflict_reason;
svn_wc_conflict_action_t tree_conflict_action;
/* When TREE_CONFLICT_REASON is CONFLICT_REASON_SKIP, the skip state to
add to the notification */
svn_wc_notify_state_t skip_reason;
/* TRUE if the node was added by this merge. Otherwise FALSE */
svn_boolean_t added;
svn_boolean_t add_is_replace; /* Add is second part of replace */
/* TRUE if we are taking over an existing directory as addition, otherwise
FALSE. */
svn_boolean_t add_existing;
/* NULL, or an hashtable mapping const char * local_abspaths to
const char *kind mapping, containing deleted nodes that still need a delete
notification (which may be a replaced notification if the node is not just
deleted) */
apr_hash_t *pending_deletes;
/* NULL, or an hashtable mapping const char * LOCAL_ABSPATHs to
a const svn_wc_conflict_description2_t * instance, describing the just
installed conflict */
apr_hash_t *new_tree_conflicts;
/* If not NULL, a reference to the information of the delete test that is
currently in progress. Allocated in the root-directory baton, referenced
from all descendants */
struct dir_delete_baton_t *delete_state;
};
/* Baton for the merge_dir_*() functions. Initialized in merge_file_opened() */
struct merge_file_baton_t
{
/* Reference to the parent baton, unless the parent is the anchor, in which
case PARENT_BATON is NULL */
struct merge_dir_baton_t *parent_baton;
/* This file doesn't have a representation in the working copy, so any
operation on it will be skipped and possibly cause a tree conflict
on the shadow root */
svn_boolean_t shadowed;
/* This node received operational changes from the merge. If this node
is the shadow root its tree conflict status has been applied */
svn_boolean_t edited;
/* If a tree conflict will be installed once edited, it's reason. If a skip
should be produced its reason. Some special values are defined. See the
merge_tree_baton_t for an explanation. */
svn_wc_conflict_reason_t tree_conflict_reason;
svn_wc_conflict_action_t tree_conflict_action;
/* When TREE_CONFLICT_REASON is CONFLICT_REASON_SKIP, the skip state to
add to the notification */
svn_wc_notify_state_t skip_reason;
/* TRUE if the node was added by this merge. Otherwise FALSE */
svn_boolean_t added;
svn_boolean_t add_is_replace; /* Add is second part of replace */
};
/* Forward declaration */
static svn_error_t *
notify_merge_begin(merge_cmd_baton_t *merge_b,
const char *local_abspath,
svn_boolean_t delete_action,
apr_pool_t *scratch_pool);
/* Record the skip for future processing and (later) produce the
skip notification */
static svn_error_t *
record_skip(merge_cmd_baton_t *merge_b,
const char *local_abspath,
svn_node_kind_t kind,
svn_wc_notify_action_t action,
svn_wc_notify_state_t state,
+ struct merge_dir_baton_t *pdb,
apr_pool_t *scratch_pool)
{
if (merge_b->record_only)
return SVN_NO_ERROR; /* ### Why? - Legacy compatibility */
- if (merge_b->merge_source.ancestral
- || merge_b->reintegrate_merge)
+ if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
+ && !(pdb && pdb->shadowed))
{
store_path(merge_b->skipped_abspaths, local_abspath);
}
if (merge_b->ctx->notify_func2)
{
svn_wc_notify_t *notify;
SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE, scratch_pool));
notify = svn_wc_create_notify(local_abspath, action, scratch_pool);
notify->kind = kind;
notify->content_state = notify->prop_state = state;
(*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
scratch_pool);
}
return SVN_NO_ERROR;
}
/* Record a tree conflict in the WC, unless this is a dry run or a record-
* only merge, or if a tree conflict is already flagged for the VICTIM_PATH.
* (The latter can happen if a merge-tracking-aware merge is doing multiple
* editor drives because of a gap in the range of eligible revisions.)
*
* The tree conflict, with its victim specified by VICTIM_PATH, is
* assumed to have happened during a merge using merge baton MERGE_B.
*
* NODE_KIND must be the node kind of "old" and "theirs" and "mine";
* this function cannot cope with node kind clashes.
* ACTION and REASON correspond to the fields
* of the same names in svn_wc_tree_conflict_description_t.
*/
static svn_error_t *
record_tree_conflict(merge_cmd_baton_t *merge_b,
const char *local_abspath,
struct merge_dir_baton_t *parent_baton,
svn_node_kind_t node_kind,
svn_wc_conflict_action_t action,
svn_wc_conflict_reason_t reason,
const svn_wc_conflict_description2_t *existing_conflict,
svn_boolean_t notify_tc,
apr_pool_t *scratch_pool)
{
svn_wc_context_t *wc_ctx = merge_b->ctx->wc_ctx;
if (merge_b->record_only)
return SVN_NO_ERROR;
if (merge_b->merge_source.ancestral
|| merge_b->reintegrate_merge)
{
store_path(merge_b->tree_conflicted_abspaths, local_abspath);
}
alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
merge_b->pool);
if (!merge_b->dry_run)
{
svn_wc_conflict_description2_t *conflict;
const svn_wc_conflict_version_t *left;
const svn_wc_conflict_version_t *right;
apr_pool_t *result_pool = parent_baton ? parent_baton->pool
: scratch_pool;
if (reason == svn_wc_conflict_reason_deleted)
{
const char *moved_to_abspath;
SVN_ERR(svn_wc__node_was_moved_away(&moved_to_abspath, NULL,
wc_ctx, local_abspath,
scratch_pool, scratch_pool));
if (moved_to_abspath)
{
/* Local abspath itself has been moved away. If only a
descendant is moved away, we call the node itself deleted */
reason = svn_wc_conflict_reason_moved_away;
}
}
else if (reason == svn_wc_conflict_reason_added)
{
const char *moved_from_abspath;
SVN_ERR(svn_wc__node_was_moved_here(&moved_from_abspath, NULL,
wc_ctx, local_abspath,
scratch_pool, scratch_pool));
if (moved_from_abspath)
reason = svn_wc_conflict_reason_moved_here;
}
SVN_ERR(make_conflict_versions(&left, &right, local_abspath, node_kind,
&merge_b->merge_source, merge_b->target,
result_pool, scratch_pool));
/* Fix up delete of file, add of dir replacement (or other way around) */
if (existing_conflict != NULL && existing_conflict->src_left_version)
left = existing_conflict->src_left_version;
conflict = svn_wc_conflict_description_create_tree2(
local_abspath, node_kind, svn_wc_operation_merge,
left, right, result_pool);
conflict->action = action;
conflict->reason = reason;
/* May return SVN_ERR_WC_PATH_UNEXPECTED_STATUS */
if (existing_conflict)
SVN_ERR(svn_wc__del_tree_conflict(wc_ctx, local_abspath,
scratch_pool));
SVN_ERR(svn_wc__add_tree_conflict(merge_b->ctx->wc_ctx, conflict,
scratch_pool));
if (parent_baton)
{
if (! parent_baton->new_tree_conflicts)
parent_baton->new_tree_conflicts = apr_hash_make(result_pool);
svn_hash_sets(parent_baton->new_tree_conflicts,
apr_pstrdup(result_pool, local_abspath),
conflict);
}
/* ### TODO: Store in parent baton */
}
/* On a replacement we currently get two tree conflicts */
if (merge_b->ctx->notify_func2 && notify_tc)
{
svn_wc_notify_t *notify;
SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE, scratch_pool));
notify = svn_wc_create_notify(local_abspath, svn_wc_notify_tree_conflict,
scratch_pool);
notify->kind = node_kind;
(*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
scratch_pool);
}
return SVN_NO_ERROR;
}
/* Record the add for future processing and produce the
update_add notification
*/
static svn_error_t *
record_update_add(merge_cmd_baton_t *merge_b,
const char *local_abspath,
svn_node_kind_t kind,
svn_boolean_t notify_replaced,
apr_pool_t *scratch_pool)
{
if (merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
{
store_path(merge_b->merged_abspaths, local_abspath);
}
if (merge_b->ctx->notify_func2)
{
svn_wc_notify_t *notify;
svn_wc_notify_action_t action = svn_wc_notify_update_add;
SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE, scratch_pool));
if (notify_replaced)
action = svn_wc_notify_update_replace;
notify = svn_wc_create_notify(local_abspath, action, scratch_pool);
notify->kind = kind;
(*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
scratch_pool);
}
return SVN_NO_ERROR;
}
/* Record the update for future processing and produce the
update_update notification */
static svn_error_t *
record_update_update(merge_cmd_baton_t *merge_b,
const char *local_abspath,
svn_node_kind_t kind,
svn_wc_notify_state_t content_state,
svn_wc_notify_state_t prop_state,
apr_pool_t *scratch_pool)
{
if (merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
{
store_path(merge_b->merged_abspaths, local_abspath);
}
if (merge_b->ctx->notify_func2)
{
svn_wc_notify_t *notify;
SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE, scratch_pool));
notify = svn_wc_create_notify(local_abspath, svn_wc_notify_update_update,
scratch_pool);
notify->kind = kind;
notify->content_state = content_state;
notify->prop_state = prop_state;
(*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
scratch_pool);
}
return SVN_NO_ERROR;
}
/* Record the delete for future processing and for (later) producing the
update_delete notification */
static svn_error_t *
record_update_delete(merge_cmd_baton_t *merge_b,
struct merge_dir_baton_t *parent_db,
const char *local_abspath,
svn_node_kind_t kind,
apr_pool_t *scratch_pool)
{
/* Update the lists of merged, skipped, tree-conflicted and added paths. */
if (merge_b->merge_source.ancestral
|| merge_b->reintegrate_merge)
{
/* Issue #4166: If a previous merge added NOTIFY_ABSPATH, but we
are now deleting it, then remove it from the list of added
paths. */
svn_hash_sets(merge_b->added_abspaths, local_abspath, NULL);
store_path(merge_b->merged_abspaths, local_abspath);
}
SVN_ERR(notify_merge_begin(merge_b, local_abspath, TRUE, scratch_pool));
if (parent_db)
{
const char *dup_abspath = apr_pstrdup(parent_db->pool, local_abspath);
if (!parent_db->pending_deletes)
parent_db->pending_deletes = apr_hash_make(parent_db->pool);
svn_hash_sets(parent_db->pending_deletes, dup_abspath,
svn_node_kind_to_word(kind));
}
return SVN_NO_ERROR;
}
/* Notify the pending 'D'eletes, that were waiting to see if a matching 'A'dd
might make them a 'R'eplace. */
static svn_error_t *
handle_pending_notifications(merge_cmd_baton_t *merge_b,
struct merge_dir_baton_t *db,
apr_pool_t *scratch_pool)
{
if (merge_b->ctx->notify_func2 && db->pending_deletes)
{
apr_hash_index_t *hi;
for (hi = apr_hash_first(scratch_pool, db->pending_deletes);
hi;
hi = apr_hash_next(hi))
{
const char *del_abspath = svn__apr_hash_index_key(hi);
svn_wc_notify_t *notify;
notify = svn_wc_create_notify(del_abspath,
svn_wc_notify_update_delete,
scratch_pool);
notify->kind = svn_node_kind_from_word(
svn__apr_hash_index_val(hi));
(*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2,
notify, scratch_pool);
}
db->pending_deletes = NULL;
}
return SVN_NO_ERROR;
}
/* Helper function for the merge_dir_*() and merge_file_*() functions.
Installs and notifies pre-recorded tree conflicts and skips for
ancestors of operational merges
*/
static svn_error_t *
mark_dir_edited(merge_cmd_baton_t *merge_b,
struct merge_dir_baton_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
/* ### Too much common code with mark_file_edited */
if (db->edited)
return SVN_NO_ERROR;
if (db->parent_baton && !db->parent_baton->edited)
{
const char *dir_abspath = svn_dirent_dirname(local_abspath,
scratch_pool);
SVN_ERR(mark_dir_edited(merge_b, db->parent_baton, dir_abspath,
scratch_pool));
}
db->edited = TRUE;
if (! db->shadowed)
return SVN_NO_ERROR; /* Easy out */
if (db->parent_baton
&& db->parent_baton->delete_state
&& db->tree_conflict_reason != CONFLICT_REASON_NONE)
{
db->parent_baton->delete_state->found_edit = TRUE;
}
else if (db->tree_conflict_reason == CONFLICT_REASON_SKIP
|| db->tree_conflict_reason == CONFLICT_REASON_SKIP_WC)
{
/* open_directory() decided not to flag a tree conflict, but
for clarity we produce a skip for this node that
most likely isn't touched by the merge itself */
if (merge_b->ctx->notify_func2)
{
svn_wc_notify_t *notify;
SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE,
scratch_pool));
notify = svn_wc_create_notify(
local_abspath,
(db->tree_conflict_reason == CONFLICT_REASON_SKIP)
? svn_wc_notify_skip
: svn_wc_notify_update_skip_obstruction,
scratch_pool);
notify->kind = svn_node_dir;
notify->content_state = notify->prop_state = db->skip_reason;
(*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2,
notify,
scratch_pool);
}
if (merge_b->merge_source.ancestral
|| merge_b->reintegrate_merge)
{
store_path(merge_b->skipped_abspaths, local_abspath);
}
}
else if (db->tree_conflict_reason != CONFLICT_REASON_NONE)
{
/* open_directory() decided that a tree conflict should be raised */
SVN_ERR(record_tree_conflict(merge_b, local_abspath, db->parent_baton,
svn_node_dir, db->tree_conflict_action,
db->tree_conflict_reason,
NULL, TRUE,
scratch_pool));
}
return SVN_NO_ERROR;
}
/* Helper function for the merge_file_*() functions.
Installs and notifies pre-recorded tree conflicts and skips for
ancestors of operational merges
*/
static svn_error_t *
mark_file_edited(merge_cmd_baton_t *merge_b,
struct merge_file_baton_t *fb,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
/* ### Too much common code with mark_dir_edited */
if (fb->edited)
return SVN_NO_ERROR;
if (fb->parent_baton && !fb->parent_baton->edited)
{
const char *dir_abspath = svn_dirent_dirname(local_abspath,
scratch_pool);
SVN_ERR(mark_dir_edited(merge_b, fb->parent_baton, dir_abspath,
scratch_pool));
}
fb->edited = TRUE;
if (! fb->shadowed)
return SVN_NO_ERROR; /* Easy out */
if (fb->parent_baton
&& fb->parent_baton->delete_state
&& fb->tree_conflict_reason != CONFLICT_REASON_NONE)
{
fb->parent_baton->delete_state->found_edit = TRUE;
}
else if (fb->tree_conflict_reason == CONFLICT_REASON_SKIP
|| fb->tree_conflict_reason == CONFLICT_REASON_SKIP_WC)
{
/* open_directory() decided not to flag a tree conflict, but
for clarity we produce a skip for this node that
most likely isn't touched by the merge itself */
if (merge_b->ctx->notify_func2)
{
svn_wc_notify_t *notify;
SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE,
scratch_pool));
notify = svn_wc_create_notify(local_abspath, svn_wc_notify_skip,
scratch_pool);
notify->kind = svn_node_file;
notify->content_state = notify->prop_state = fb->skip_reason;
(*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2,
notify,
scratch_pool);
}
if (merge_b->merge_source.ancestral
|| merge_b->reintegrate_merge)
{
store_path(merge_b->skipped_abspaths, local_abspath);
}
}
else if (fb->tree_conflict_reason != CONFLICT_REASON_NONE)
{
/* open_file() decided that a tree conflict should be raised */
SVN_ERR(record_tree_conflict(merge_b, local_abspath, fb->parent_baton,
svn_node_file, fb->tree_conflict_action,
fb->tree_conflict_reason,
NULL, TRUE,
scratch_pool));
}
return SVN_NO_ERROR;
}
/* An svn_diff_tree_processor_t function.
Called before either merge_file_changed(), merge_file_added(),
merge_file_deleted() or merge_file_closed(), unless it sets *SKIP to TRUE.
When *SKIP is TRUE, the diff driver avoids work on getting the details
for the closing callbacks.
*/
static svn_error_t *
merge_file_opened(void **new_file_baton,
svn_boolean_t *skip,
const char *relpath,
const svn_diff_source_t *left_source,
const svn_diff_source_t *right_source,
const svn_diff_source_t *copyfrom_source,
void *dir_baton,
const struct svn_diff_tree_processor_t *processor,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t *merge_b = processor->baton;
struct merge_dir_baton_t *pdb = dir_baton;
struct merge_file_baton_t *fb;
const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
relpath, scratch_pool);
fb = apr_pcalloc(result_pool, sizeof(*fb));
fb->tree_conflict_reason = CONFLICT_REASON_NONE;
fb->tree_conflict_action = svn_wc_conflict_action_edit;
fb->skip_reason = svn_wc_notify_state_unknown;
*new_file_baton = fb;
if (pdb)
{
fb->parent_baton = pdb;
fb->shadowed = pdb->shadowed;
fb->skip_reason = pdb->skip_reason;
}
if (fb->shadowed)
{
/* An ancestor is tree conflicted. Nothing to do here. */
}
else if (left_source != NULL)
{
/* Node is expected to be a file, which will be changed or deleted. */
svn_node_kind_t kind;
svn_boolean_t is_deleted;
svn_boolean_t excluded;
svn_depth_t parent_depth;
if (! right_source)
fb->tree_conflict_action = svn_wc_conflict_action_delete;
{
svn_wc_notify_state_t obstr_state;
SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, &excluded,
&kind, &parent_depth,
merge_b, local_abspath,
scratch_pool));
if (obstr_state != svn_wc_notify_state_inapplicable)
{
fb->shadowed = TRUE;
fb->tree_conflict_reason = CONFLICT_REASON_SKIP;
fb->skip_reason = obstr_state;
return SVN_NO_ERROR;
}
if (is_deleted)
kind = svn_node_none;
}
if (kind == svn_node_none)
{
fb->shadowed = TRUE;
/* If this is not the merge target and the parent is too shallow to
contain this directory, and the directory is not present
via exclusion or depth filtering, skip it instead of recording
a tree conflict.
Non-inheritable mergeinfo will be recorded, allowing
future merges into non-shallow working copies to merge
changes we missed this time around. */
if (pdb && (excluded
|| (parent_depth != svn_depth_unknown &&
parent_depth < svn_depth_files)))
{
fb->shadowed = TRUE;
fb->tree_conflict_reason = CONFLICT_REASON_SKIP;
fb->skip_reason = svn_wc_notify_state_missing;
return SVN_NO_ERROR;
}
if (is_deleted)
fb->tree_conflict_reason = svn_wc_conflict_reason_deleted;
else
fb->tree_conflict_reason = svn_wc_conflict_reason_missing;
/* ### Similar to directory */
*skip = TRUE;
SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
return SVN_NO_ERROR;
/* ### /Similar */
}
else if (kind != svn_node_file)
{
fb->shadowed = TRUE;
fb->tree_conflict_reason = svn_wc_conflict_reason_obstructed;
/* ### Similar to directory */
*skip = TRUE;
SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
return SVN_NO_ERROR;
/* ### /Similar */
}
if (! right_source)
{
/* We want to delete the directory */
fb->tree_conflict_action = svn_wc_conflict_action_delete;
SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
if (fb->shadowed)
{
return SVN_NO_ERROR; /* Already set a tree conflict */
}
/* Comparison mode to verify for delete tree conflicts? */
if (pdb && pdb->delete_state
&& pdb->delete_state->found_edit)
{
/* Earlier nodes found a conflict. Done. */
*skip = TRUE;
}
}
}
else
{
const svn_wc_conflict_description2_t *old_tc = NULL;
/* The node doesn't exist pre-merge: We have an addition */
fb->added = TRUE;
fb->tree_conflict_action = svn_wc_conflict_action_add;
if (pdb && pdb->pending_deletes
&& svn_hash_gets(pdb->pending_deletes, local_abspath))
{
fb->add_is_replace = TRUE;
fb->tree_conflict_action = svn_wc_conflict_action_replace;
svn_hash_sets(pdb->pending_deletes, local_abspath, NULL);
}
if (pdb
&& pdb->new_tree_conflicts
&& (old_tc = svn_hash_gets(pdb->new_tree_conflicts, local_abspath)))
{
fb->tree_conflict_action = svn_wc_conflict_action_replace;
fb->tree_conflict_reason = old_tc->reason;
/* Update the tree conflict to store that this is a replace */
SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
svn_node_file,
fb->tree_conflict_action,
fb->tree_conflict_reason,
old_tc, FALSE,
scratch_pool));
if (old_tc->reason == svn_wc_conflict_reason_deleted
|| old_tc->reason == svn_wc_conflict_reason_moved_away)
{
/* Issue #3806: Incoming replacements on local deletes produce
inconsistent result.
In this specific case we can continue applying the add part
of the replacement. */
}
else
{
*skip = TRUE;
return SVN_NO_ERROR;
}
}
else if (! (merge_b->dry_run
&& ((pdb && pdb->added) || fb->add_is_replace)))
{
svn_wc_notify_state_t obstr_state;
svn_node_kind_t kind;
svn_boolean_t is_deleted;
SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, NULL,
&kind, NULL,
merge_b, local_abspath,
scratch_pool));
if (obstr_state != svn_wc_notify_state_inapplicable)
{
/* Skip the obstruction */
fb->shadowed = TRUE;
fb->tree_conflict_reason = CONFLICT_REASON_SKIP;
fb->skip_reason = obstr_state;
}
else if (kind != svn_node_none && !is_deleted)
{
/* Set a tree conflict */
fb->shadowed = TRUE;
fb->tree_conflict_reason = svn_wc_conflict_reason_obstructed;
}
}
/* Handle pending conflicts */
SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
}
return SVN_NO_ERROR;
}
/* An svn_diff_tree_processor_t function.
*
* Called after merge_file_opened() when a node receives only text and/or
* property changes between LEFT_SOURCE and RIGHT_SOURCE.
*
* left_file and right_file can be NULL when the file is not modified.
* left_props and right_props are always available.
*/
static svn_error_t *
merge_file_changed(const char *relpath,
const svn_diff_source_t *left_source,
const svn_diff_source_t *right_source,
const char *left_file,
const char *right_file,
/*const*/ apr_hash_t *left_props,
/*const*/ apr_hash_t *right_props,
svn_boolean_t file_modified,
const apr_array_header_t *prop_changes,
void *file_baton,
const struct svn_diff_tree_processor_t *processor,
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t *merge_b = processor->baton;
struct merge_file_baton_t *fb = file_baton;
svn_client_ctx_t *ctx = merge_b->ctx;
const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
relpath, scratch_pool);
const svn_wc_conflict_version_t *left;
const svn_wc_conflict_version_t *right;
svn_wc_notify_state_t text_state;
svn_wc_notify_state_t property_state;
SVN_ERR_ASSERT(local_abspath && svn_dirent_is_absolute(local_abspath));
SVN_ERR_ASSERT(!left_file || svn_dirent_is_absolute(left_file));
SVN_ERR_ASSERT(!right_file || svn_dirent_is_absolute(right_file));
SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
if (fb->shadowed)
{
if (fb->tree_conflict_reason == CONFLICT_REASON_NONE)
{
/* We haven't notified for this node yet: report a skip */
SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file,
svn_wc_notify_update_shadowed_update,
- fb->skip_reason, scratch_pool));
+ fb->skip_reason, fb->parent_baton,
+ scratch_pool));
}
return SVN_NO_ERROR;
}
/* This callback is essentially no more than a wrapper around
svn_wc_merge5(). Thank goodness that all the
diff-editor-mechanisms are doing the hard work of getting the
fulltexts! */
property_state = svn_wc_notify_state_unchanged;
text_state = svn_wc_notify_state_unchanged;
SVN_ERR(prepare_merge_props_changed(&prop_changes, local_abspath,
prop_changes, merge_b,
scratch_pool, scratch_pool));
SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
svn_node_file, &merge_b->merge_source, merge_b->target,
scratch_pool, scratch_pool));
/* Do property merge now, if we are not going to perform a text merge */
if ((merge_b->record_only || !left_file) && prop_changes->nelts)
{
SVN_ERR(svn_wc_merge_props3(&property_state, ctx->wc_ctx, local_abspath,
left, right,
left_props, prop_changes,
merge_b->dry_run,
NULL, NULL,
ctx->cancel_func, ctx->cancel_baton,
scratch_pool));
if (property_state == svn_wc_notify_state_conflicted)
{
alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
merge_b->pool);
}
}
/* Easy out: We are only applying mergeinfo differences. */
if (merge_b->record_only)
{
/* NO-OP */
}
else if (left_file)
{
svn_boolean_t has_local_mods;
enum svn_wc_merge_outcome_t content_outcome;
const char *target_label;
const char *left_label;
const char *right_label;
const char *path_ext = "";
if (merge_b->ext_patterns && merge_b->ext_patterns->nelts)
{
svn_path_splitext(NULL, &path_ext, local_abspath, scratch_pool);
if (! (*path_ext
&& svn_cstring_match_glob_list(path_ext,
merge_b->ext_patterns)))
{
path_ext = "";
}
}
/* xgettext: the '.working', '.merge-left.r%ld' and
'.merge-right.r%ld' strings are used to tag onto a file
name in case of a merge conflict */
target_label = apr_psprintf(scratch_pool, _(".working%s%s"),
*path_ext ? "." : "", path_ext);
left_label = apr_psprintf(scratch_pool,
_(".merge-left.r%ld%s%s"),
left_source->revision,
*path_ext ? "." : "", path_ext);
right_label = apr_psprintf(scratch_pool,
_(".merge-right.r%ld%s%s"),
right_source->revision,
*path_ext ? "." : "", path_ext);
SVN_ERR(svn_wc_text_modified_p2(&has_local_mods, ctx->wc_ctx,
local_abspath, FALSE, scratch_pool));
/* Do property merge and text merge in one step so that keyword expansion
takes into account the new property values. */
SVN_ERR(svn_wc_merge5(&content_outcome, &property_state, ctx->wc_ctx,
left_file, right_file, local_abspath,
left_label, right_label, target_label,
left, right,
merge_b->dry_run, merge_b->diff3_cmd,
merge_b->merge_options,
left_props, prop_changes,
NULL, NULL,
ctx->cancel_func,
ctx->cancel_baton,
scratch_pool));
if (content_outcome == svn_wc_merge_conflict
|| property_state == svn_wc_notify_state_conflicted)
{
alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
merge_b->pool);
}
if (content_outcome == svn_wc_merge_conflict)
text_state = svn_wc_notify_state_conflicted;
else if (has_local_mods
&& content_outcome != svn_wc_merge_unchanged)
text_state = svn_wc_notify_state_merged;
else if (content_outcome == svn_wc_merge_merged)
text_state = svn_wc_notify_state_changed;
else if (content_outcome == svn_wc_merge_no_merge)
text_state = svn_wc_notify_state_missing;
else /* merge_outcome == svn_wc_merge_unchanged */
text_state = svn_wc_notify_state_unchanged;
}
if (text_state == svn_wc_notify_state_conflicted
|| text_state == svn_wc_notify_state_merged
|| text_state == svn_wc_notify_state_changed
|| property_state == svn_wc_notify_state_conflicted
|| property_state == svn_wc_notify_state_merged
|| property_state == svn_wc_notify_state_changed)
{
SVN_ERR(record_update_update(merge_b, local_abspath, svn_node_file,
text_state, property_state,
scratch_pool));
}
return SVN_NO_ERROR;
}
/* An svn_diff_tree_processor_t function.
*
* Called after merge_file_opened() when a node doesn't exist in LEFT_SOURCE,
* but does in RIGHT_SOURCE.
*
* When a node is replaced instead of just added a separate opened+deleted will
* be invoked before the current open+added.
*/
static svn_error_t *
merge_file_added(const char *relpath,
const svn_diff_source_t *copyfrom_source,
const svn_diff_source_t *right_source,
const char *copyfrom_file,
const char *right_file,
/*const*/ apr_hash_t *copyfrom_props,
/*const*/ apr_hash_t *right_props,
void *file_baton,
const struct svn_diff_tree_processor_t *processor,
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t *merge_b = processor->baton;
struct merge_file_baton_t *fb = file_baton;
const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
relpath, scratch_pool);
apr_hash_t *pristine_props;
apr_hash_t *new_props;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
if (fb->shadowed)
{
if (fb->tree_conflict_reason == CONFLICT_REASON_NONE)
{
/* We haven't notified for this node yet: report a skip */
SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file,
svn_wc_notify_update_shadowed_add,
- fb->skip_reason, scratch_pool));
+ fb->skip_reason, fb->parent_baton,
+ scratch_pool));
}
return SVN_NO_ERROR;
}
/* Easy out: We are only applying mergeinfo differences. */
if (merge_b->record_only)
{
return SVN_NO_ERROR;
}
if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
&& ( !fb->parent_baton || !fb->parent_baton->added))
{
/* Store the roots of added subtrees */
store_path(merge_b->added_abspaths, local_abspath);
}
if (!merge_b->dry_run)
{
const char *copyfrom_url;
svn_revnum_t copyfrom_rev;
svn_stream_t *new_contents, *pristine_contents;
/* If this is a merge from the same repository as our
working copy, we handle adds as add-with-history.
Otherwise, we'll use a pure add. */
if (merge_b->same_repos)
{
const char *child =
svn_dirent_skip_ancestor(merge_b->target->abspath,
local_abspath);
SVN_ERR_ASSERT(child != NULL);
copyfrom_url = svn_path_url_add_component2(
merge_b->merge_source.loc2->url,
child, scratch_pool);
copyfrom_rev = right_source->revision;
SVN_ERR(check_repos_match(merge_b->target, local_abspath,
copyfrom_url, scratch_pool));
SVN_ERR(svn_stream_open_readonly(&pristine_contents,
right_file,
scratch_pool,
scratch_pool));
new_contents = NULL; /* inherit from new_base_contents */
pristine_props = right_props; /* Includes last_* information */
new_props = NULL; /* No local changes */
if (svn_hash_gets(pristine_props, SVN_PROP_MERGEINFO))
{
alloc_and_store_path(&merge_b->paths_with_new_mergeinfo,
local_abspath, merge_b->pool);
}
}
else
{
apr_array_header_t *regular_props;
copyfrom_url = NULL;
copyfrom_rev = SVN_INVALID_REVNUM;
pristine_contents = svn_stream_empty(scratch_pool);
SVN_ERR(svn_stream_open_readonly(&new_contents, right_file,
scratch_pool, scratch_pool));
pristine_props = apr_hash_make(scratch_pool); /* Local addition */
/* We don't want any foreign properties */
SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(right_props,
scratch_pool),
NULL, NULL, &regular_props,
scratch_pool));
new_props = svn_prop_array_to_hash(regular_props, scratch_pool);
/* Issue #3383: We don't want mergeinfo from a foreign repository. */
svn_hash_sets(new_props, SVN_PROP_MERGEINFO, NULL);
}
/* Do everything like if we had called 'svn cp PATH1 PATH2'. */
SVN_ERR(svn_wc_add_repos_file4(merge_b->ctx->wc_ctx,
local_abspath,
pristine_contents,
new_contents,
pristine_props, new_props,
copyfrom_url, copyfrom_rev,
merge_b->ctx->cancel_func,
merge_b->ctx->cancel_baton,
scratch_pool));
/* Caller must call svn_sleep_for_timestamps() */
*merge_b->use_sleep = TRUE;
}
SVN_ERR(record_update_add(merge_b, local_abspath, svn_node_file,
fb->add_is_replace, scratch_pool));
return SVN_NO_ERROR;
}
/* Compare the two sets of properties PROPS1 and PROPS2, ignoring the
* "svn:mergeinfo" property, and noticing only "normal" props. Set *SAME to
* true if the rest of the properties are identical or false if they differ.
*/
static svn_error_t *
properties_same_p(svn_boolean_t *same,
apr_hash_t *props1,
apr_hash_t *props2,
apr_pool_t *scratch_pool)
{
apr_array_header_t *prop_changes;
int i, diffs;
/* Examine the properties that differ */
SVN_ERR(svn_prop_diffs(&prop_changes, props1, props2, scratch_pool));
diffs = 0;
for (i = 0; i < prop_changes->nelts; i++)
{
const char *pname = APR_ARRAY_IDX(prop_changes, i, svn_prop_t).name;
/* Count the properties we're interested in; ignore the rest */
if (svn_wc_is_normal_prop(pname)
&& strcmp(pname, SVN_PROP_MERGEINFO) != 0)
diffs++;
}
*same = (diffs == 0);
return SVN_NO_ERROR;
}
/* Compare the file OLDER_ABSPATH (together with its normal properties in
* ORIGINAL_PROPS which may also contain WC props and entry props) with the
* versioned file MINE_ABSPATH (together with its versioned properties).
* Set *SAME to true if they are the same or false if they differ, ignoring
* the "svn:mergeinfo" property, and ignoring differences in keyword
* expansion and end-of-line style. */
static svn_error_t *
files_same_p(svn_boolean_t *same,
const char *older_abspath,
apr_hash_t *original_props,
const char *mine_abspath,
svn_wc_context_t *wc_ctx,
apr_pool_t *scratch_pool)
{
apr_hash_t *working_props;
SVN_ERR(svn_wc_prop_list2(&working_props, wc_ctx, mine_abspath,
scratch_pool, scratch_pool));
/* Compare the properties */
SVN_ERR(properties_same_p(same, original_props, working_props,
scratch_pool));
if (*same)
{
svn_stream_t *mine_stream;
svn_stream_t *older_stream;
svn_opt_revision_t working_rev = { svn_opt_revision_working, { 0 } };
/* Compare the file content, translating 'mine' to 'normal' form. */
if (svn_prop_get_value(working_props, SVN_PROP_SPECIAL) != NULL)
SVN_ERR(svn_subst_read_specialfile(&mine_stream, mine_abspath,
scratch_pool, scratch_pool));
else
SVN_ERR(svn_client__get_normalized_stream(&mine_stream, wc_ctx,
mine_abspath, &working_rev,
FALSE, TRUE, NULL, NULL,
scratch_pool, scratch_pool));
SVN_ERR(svn_stream_open_readonly(&older_stream, older_abspath,
scratch_pool, scratch_pool));
SVN_ERR(svn_stream_contents_same2(same, mine_stream, older_stream,
scratch_pool));
}
return SVN_NO_ERROR;
}
/* An svn_diff_tree_processor_t function.
*
* Called after merge_file_opened() when a node does exist in LEFT_SOURCE, but
* no longer exists (or is replaced) in RIGHT_SOURCE.
*
* When a node is replaced instead of just added a separate opened+added will
* be invoked after the current open+deleted.
*/
static svn_error_t *
merge_file_deleted(const char *relpath,
const svn_diff_source_t *left_source,
const char *left_file,
/*const*/ apr_hash_t *left_props,
void *file_baton,
const struct svn_diff_tree_processor_t *processor,
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t *merge_b = processor->baton;
struct merge_file_baton_t *fb = file_baton;
const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
relpath, scratch_pool);
svn_boolean_t same;
SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
if (fb->shadowed)
{
if (fb->tree_conflict_reason == CONFLICT_REASON_NONE)
{
/* We haven't notified for this node yet: report a skip */
SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file,
svn_wc_notify_update_shadowed_delete,
- fb->skip_reason, scratch_pool));
+ fb->skip_reason, fb->parent_baton,
+ scratch_pool));
}
return SVN_NO_ERROR;
}
/* Easy out: We are only applying mergeinfo differences. */
if (merge_b->record_only)
{
return SVN_NO_ERROR;
}
/* If the files are identical, attempt deletion */
if (merge_b->force_delete)
same = TRUE;
else
SVN_ERR(files_same_p(&same, left_file, left_props,
local_abspath, merge_b->ctx->wc_ctx,
scratch_pool));
if (fb->parent_baton
&& fb->parent_baton->delete_state)
{
if (same)
{
/* Note that we checked this file */
store_path(fb->parent_baton->delete_state->compared_abspaths,
local_abspath);
}
else
{
/* We found some modification. Parent should raise a tree conflict */
fb->parent_baton->delete_state->found_edit = TRUE;
}
return SVN_NO_ERROR;
}
else if (same)
{
if (!merge_b->dry_run)
SVN_ERR(svn_wc_delete4(merge_b->ctx->wc_ctx, local_abspath,
FALSE /* keep_local */, FALSE /* unversioned */,
merge_b->ctx->cancel_func,
merge_b->ctx->cancel_baton,
NULL, NULL /* no notify */,
scratch_pool));
/* Record that we might have deleted mergeinfo */
alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo,
local_abspath, merge_b->pool);
/* And notify the deletion */
SVN_ERR(record_update_delete(merge_b, fb->parent_baton, local_abspath,
svn_node_file, scratch_pool));
}
else
{
/* The files differ, so raise a conflict instead of deleting */
/* This is use case 5 described in the paper attached to issue
* #2282. See also notes/tree-conflicts/detection.txt
*/
SVN_ERR(record_tree_conflict(merge_b, local_abspath, fb->parent_baton,
svn_node_file,
svn_wc_conflict_action_delete,
svn_wc_conflict_reason_edited,
NULL, TRUE,
scratch_pool));
}
return SVN_NO_ERROR;
}
/* An svn_diff_tree_processor_t function.
Called before either merge_dir_changed(), merge_dir_added(),
merge_dir_deleted() or merge_dir_closed(), unless it sets *SKIP to TRUE.
After this call and before the close call, all descendants will receive
their changes, unless *SKIP_CHILDREN is set to TRUE.
When *SKIP is TRUE, the diff driver avoids work on getting the details
for the closing callbacks.
The SKIP and SKIP_DESCENDANTS work independantly.
*/
static svn_error_t *
merge_dir_opened(void **new_dir_baton,
svn_boolean_t *skip,
svn_boolean_t *skip_children,
const char *relpath,
const svn_diff_source_t *left_source,
const svn_diff_source_t *right_source,
const svn_diff_source_t *copyfrom_source,
void *parent_dir_baton,
const struct svn_diff_tree_processor_t *processor,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t *merge_b = processor->baton;
struct merge_dir_baton_t *db;
struct merge_dir_baton_t *pdb = parent_dir_baton;
const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
relpath, scratch_pool);
db = apr_pcalloc(result_pool, sizeof(*db));
db->pool = result_pool;
db->tree_conflict_reason = CONFLICT_REASON_NONE;
db->tree_conflict_action = svn_wc_conflict_action_edit;
db->skip_reason = svn_wc_notify_state_unknown;
*new_dir_baton = db;
if (pdb)
{
db->parent_baton = pdb;
db->shadowed = pdb->shadowed;
db->skip_reason = pdb->skip_reason;
}
if (db->shadowed)
{
/* An ancestor is tree conflicted. Nothing to do here. */
if (! left_source)
db->added = TRUE;
}
else if (left_source != NULL)
{
/* Node is expected to be a directory. */
svn_node_kind_t kind;
svn_boolean_t is_deleted;
svn_boolean_t excluded;
svn_depth_t parent_depth;
if (! right_source)
db->tree_conflict_action = svn_wc_conflict_action_delete;
/* Check for an obstructed or missing node on disk. */
{
svn_wc_notify_state_t obstr_state;
SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, &excluded,
&kind, &parent_depth,
merge_b, local_abspath,
scratch_pool));
if (obstr_state != svn_wc_notify_state_inapplicable)
{
db->shadowed = TRUE;
if (obstr_state == svn_wc_notify_state_obstructed)
{
svn_boolean_t is_wcroot;
SVN_ERR(svn_wc_check_root(&is_wcroot, NULL, NULL,
merge_b->ctx->wc_ctx,
local_abspath, scratch_pool));
if (is_wcroot)
{
db->tree_conflict_reason = CONFLICT_REASON_SKIP_WC;
return SVN_NO_ERROR;
}
}
db->tree_conflict_reason = CONFLICT_REASON_SKIP;
db->skip_reason = obstr_state;
if (! right_source)
{
*skip = *skip_children = TRUE;
SVN_ERR(mark_dir_edited(merge_b, db, local_abspath,
scratch_pool));
}
return SVN_NO_ERROR;
}
if (is_deleted)
kind = svn_node_none;
}
if (kind == svn_node_none)
{
db->shadowed = TRUE;
/* If this is not the merge target and the parent is too shallow to
contain this directory, and the directory is not presen
via exclusion or depth filtering, skip it instead of recording
a tree conflict.
Non-inheritable mergeinfo will be recorded, allowing
future merges into non-shallow working copies to merge
changes we missed this time around. */
if (pdb && (excluded
|| (parent_depth != svn_depth_unknown &&
parent_depth < svn_depth_immediates)))
{
db->shadowed = TRUE;
db->tree_conflict_reason = CONFLICT_REASON_SKIP;
db->skip_reason = svn_wc_notify_state_missing;
return SVN_NO_ERROR;
}
if (is_deleted)
db->tree_conflict_reason = svn_wc_conflict_reason_deleted;
else
db->tree_conflict_reason = svn_wc_conflict_reason_missing;
/* ### To avoid breaking tests */
*skip = TRUE;
*skip_children = TRUE;
SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
return SVN_NO_ERROR;
/* ### /avoid breaking tests */
}
else if (kind != svn_node_dir)
{
db->shadowed = TRUE;
db->tree_conflict_reason = svn_wc_conflict_reason_obstructed;
/* ### To avoid breaking tests */
*skip = TRUE;
*skip_children = TRUE;
SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
return SVN_NO_ERROR;
/* ### /avoid breaking tests */
}
if (! right_source)
{
/* We want to delete the directory */
/* Mark PB edited now? */
db->tree_conflict_action = svn_wc_conflict_action_delete;
SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
if (db->shadowed)
{
*skip_children = TRUE;
return SVN_NO_ERROR; /* Already set a tree conflict */
}
db->delete_state = (pdb != NULL) ? pdb->delete_state : NULL;
if (db->delete_state && db->delete_state->found_edit)
{
/* A sibling found a conflict. Done. */
*skip = TRUE;
*skip_children = TRUE;
}
else if (merge_b->force_delete)
{
/* No comparison necessary */
*skip_children = TRUE;
}
else if (! db->delete_state)
{
/* Start descendant comparison */
db->delete_state = apr_pcalloc(db->pool,
sizeof(*db->delete_state));
db->delete_state->del_root = db;
db->delete_state->compared_abspaths = apr_hash_make(db->pool);
}
}
}
else
{
const svn_wc_conflict_description2_t *old_tc = NULL;
/* The node doesn't exist pre-merge: We have an addition */
db->added = TRUE;
db->tree_conflict_action = svn_wc_conflict_action_add;
if (pdb && pdb->pending_deletes
&& svn_hash_gets(pdb->pending_deletes, local_abspath))
{
db->add_is_replace = TRUE;
db->tree_conflict_action = svn_wc_conflict_action_replace;
svn_hash_sets(pdb->pending_deletes, local_abspath, NULL);
}
if (pdb
&& pdb->new_tree_conflicts
&& (old_tc = svn_hash_gets(pdb->new_tree_conflicts, local_abspath)))
{
db->tree_conflict_action = svn_wc_conflict_action_replace;
db->tree_conflict_reason = old_tc->reason;
if (old_tc->reason == svn_wc_conflict_reason_deleted
|| old_tc->reason == svn_wc_conflict_reason_moved_away)
{
/* Issue #3806: Incoming replacements on local deletes produce
inconsistent result.
In this specific case we can continue applying the add part
of the replacement. */
}
else
{
*skip = TRUE;
*skip_children = TRUE;
/* Update the tree conflict to store that this is a replace */
SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
svn_node_dir,
db->tree_conflict_action,
db->tree_conflict_reason,
old_tc, FALSE,
scratch_pool));
return SVN_NO_ERROR;
}
}
if (! (merge_b->dry_run
&& ((pdb && pdb->added) || db->add_is_replace)))
{
svn_wc_notify_state_t obstr_state;
svn_node_kind_t kind;
svn_boolean_t is_deleted;
SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, NULL,
&kind, NULL,
merge_b, local_abspath,
scratch_pool));
/* In this case of adding a directory, we have an exception to the
* usual "skip if it's inconsistent" rule. If the directory exists
* on disk unexpectedly, we simply make it versioned, because we can
* do so without risk of destroying data. Only skip if it is
* versioned but unexpectedly missing from disk, or is unversioned
* but obstructed by a node of the wrong kind. */
if (obstr_state == svn_wc_notify_state_obstructed
&& (is_deleted || kind == svn_node_none))
{
svn_node_kind_t disk_kind;
SVN_ERR(svn_io_check_path(local_abspath, &disk_kind,
scratch_pool));
if (disk_kind == svn_node_dir)
{
obstr_state = svn_wc_notify_state_inapplicable;
db->add_existing = TRUE; /* Take over existing directory */
}
}
if (obstr_state != svn_wc_notify_state_inapplicable)
{
/* Skip the obstruction */
db->shadowed = TRUE;
db->tree_conflict_reason = CONFLICT_REASON_SKIP;
db->skip_reason = obstr_state;
}
else if (kind != svn_node_none && !is_deleted)
{
/* Set a tree conflict */
db->shadowed = TRUE;
db->tree_conflict_reason = svn_wc_conflict_reason_obstructed;
+
+ if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
+ && !(pdb && pdb->shadowed))
+ {
+ store_path(merge_b->skipped_abspaths, local_abspath);
+ }
}
}
/* Handle pending conflicts */
SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
if (db->shadowed)
{
/* Notified and done. Skip children? */
}
else if (merge_b->record_only)
{
/* Ok, we are done for this node and its descendants */
*skip = TRUE;
*skip_children = TRUE;
}
else if (! merge_b->dry_run)
{
/* Create the directory on disk, to allow descendants to be added */
if (! db->add_existing)
SVN_ERR(svn_io_dir_make(local_abspath, APR_OS_DEFAULT,
scratch_pool));
if (old_tc)
{
/* svn_wc_add4 and svn_wc_add_from_disk2 can't add a node
over an existing tree conflict */
/* ### These functions should take some tree conflict argument
and allow overwriting the tc when one is passed */
SVN_ERR(svn_wc__del_tree_conflict(merge_b->ctx->wc_ctx,
local_abspath,
scratch_pool));
}
if (merge_b->same_repos)
{
const char *original_url;
original_url = svn_path_url_add_component2(
merge_b->merge_source.loc2->url,
relpath, scratch_pool);
/* Limitation (aka HACK):
We create a newly added directory with an original URL and
revision as that in the repository, but without its properties
and children.
When the merge is cancelled before the final dir_added(), the
copy won't really represent the in-repository state of the node.
*/
SVN_ERR(svn_wc_add4(merge_b->ctx->wc_ctx, local_abspath,
svn_depth_infinity,
original_url,
right_source->revision,
merge_b->ctx->cancel_func,
merge_b->ctx->cancel_baton,
NULL, NULL /* no notify! */,
scratch_pool));
}
else
{
SVN_ERR(svn_wc_add_from_disk2(merge_b->ctx->wc_ctx, local_abspath,
apr_hash_make(scratch_pool),
NULL, NULL /* no notify! */,
scratch_pool));
}
if (old_tc != NULL)
{
/* ### Should be atomic with svn_wc_add(4|_from_disk2)() */
SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
svn_node_dir,
db->tree_conflict_action,
db->tree_conflict_reason,
old_tc, FALSE,
scratch_pool));
}
}
if (! db->shadowed && !merge_b->record_only)
SVN_ERR(record_update_add(merge_b, local_abspath, svn_node_dir,
db->add_is_replace, scratch_pool));
}
return SVN_NO_ERROR;
}
/* An svn_diff_tree_processor_t function.
*
* Called after merge_dir_opened() when a node exists in both the left and
* right source, but has its properties changed inbetween.
*
* After the merge_dir_opened() but before the call to this merge_dir_changed()
* function all descendants will have been updated.
*/
static svn_error_t *
merge_dir_changed(const char *relpath,
const svn_diff_source_t *left_source,
const svn_diff_source_t *right_source,
/*const*/ apr_hash_t *left_props,
/*const*/ apr_hash_t *right_props,
const apr_array_header_t *prop_changes,
void *dir_baton,
const struct svn_diff_tree_processor_t *processor,
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t *merge_b = processor->baton;
struct merge_dir_baton_t *db = dir_baton;
const apr_array_header_t *props;
const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
relpath, scratch_pool);
SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool));
SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
if (db->shadowed)
{
if (db->tree_conflict_reason == CONFLICT_REASON_NONE)
{
/* We haven't notified for this node yet: report a skip */
SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir,
svn_wc_notify_update_shadowed_update,
- db->skip_reason, scratch_pool));
+ db->skip_reason, db->parent_baton,
+ scratch_pool));
}
return SVN_NO_ERROR;
}
SVN_ERR(prepare_merge_props_changed(&props, local_abspath, prop_changes,
merge_b, scratch_pool, scratch_pool));
if (props->nelts)
{
const svn_wc_conflict_version_t *left;
const svn_wc_conflict_version_t *right;
svn_client_ctx_t *ctx = merge_b->ctx;
svn_wc_notify_state_t prop_state;
SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
svn_node_dir, &merge_b->merge_source,
merge_b->target,
scratch_pool, scratch_pool));
SVN_ERR(svn_wc_merge_props3(&prop_state, ctx->wc_ctx, local_abspath,
left, right,
left_props, props,
merge_b->dry_run,
NULL, NULL,
ctx->cancel_func, ctx->cancel_baton,
scratch_pool));
if (prop_state == svn_wc_notify_state_conflicted)
{
alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
merge_b->pool);
}
if (prop_state == svn_wc_notify_state_conflicted
|| prop_state == svn_wc_notify_state_merged
|| prop_state == svn_wc_notify_state_changed)
{
SVN_ERR(record_update_update(merge_b, local_abspath, svn_node_file,
svn_wc_notify_state_inapplicable,
prop_state, scratch_pool));
}
}
return SVN_NO_ERROR;
}
/* An svn_diff_tree_processor_t function.
*
* Called after merge_dir_opened() when a node doesn't exist in LEFT_SOURCE,
* but does in RIGHT_SOURCE. After the merge_dir_opened() but before the call
* to this merge_dir_added() function all descendants will have been added.
*
* When a node is replaced instead of just added a separate opened+deleted will
* be invoked before the current open+added.
*/
static svn_error_t *
merge_dir_added(const char *relpath,
const svn_diff_source_t *copyfrom_source,
const svn_diff_source_t *right_source,
/*const*/ apr_hash_t *copyfrom_props,
/*const*/ apr_hash_t *right_props,
void *dir_baton,
const struct svn_diff_tree_processor_t *processor,
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t *merge_b = processor->baton;
struct merge_dir_baton_t *db = dir_baton;
const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
relpath, scratch_pool);
/* For consistency; usually a no-op from _dir_added() */
SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool));
SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
if (db->shadowed)
{
if (db->tree_conflict_reason == CONFLICT_REASON_NONE)
{
/* We haven't notified for this node yet: report a skip */
SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir,
svn_wc_notify_update_shadowed_add,
- db->skip_reason, scratch_pool));
+ db->skip_reason, db->parent_baton,
+ scratch_pool));
}
return SVN_NO_ERROR;
}
SVN_ERR_ASSERT(
db->edited /* Marked edited from merge_open_dir() */
&& ! merge_b->record_only /* Skip details from merge_open_dir() */
);
if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
&& ( !db->parent_baton || !db->parent_baton->added))
{
/* Store the roots of added subtrees */
store_path(merge_b->added_abspaths, local_abspath);
}
if (merge_b->same_repos)
{
/* When the directory was added in merge_dir_added() we didn't update its
pristine properties. Instead we receive the property changes later and
apply them in this function.
If we would apply them as changes (such as before fixing issue #3405),
we would see the unmodified properties as local changes, and the
pristine properties would be out of sync with what the repository
expects for this directory.
Instead of doing that we now simply set the properties as the pristine
properties via a private libsvn_wc api.
*/
const char *copyfrom_url;
svn_revnum_t copyfrom_rev;
const char *parent_abspath;
const char *child;
/* Creating a hash containing regular and entry props */
apr_hash_t *new_pristine_props = right_props;
parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
child = svn_dirent_is_child(merge_b->target->abspath, local_abspath, NULL);
SVN_ERR_ASSERT(child != NULL);
copyfrom_url = svn_path_url_add_component2(merge_b->merge_source.loc2->url,
child, scratch_pool);
copyfrom_rev = right_source->revision;
SVN_ERR(check_repos_match(merge_b->target, parent_abspath, copyfrom_url,
scratch_pool));
if (!merge_b->dry_run)
{
SVN_ERR(svn_wc__complete_directory_add(merge_b->ctx->wc_ctx,
local_abspath,
new_pristine_props,
copyfrom_url, copyfrom_rev,
scratch_pool));
}
if (svn_hash_gets(new_pristine_props, SVN_PROP_MERGEINFO))
{
alloc_and_store_path(&merge_b->paths_with_new_mergeinfo,
local_abspath, merge_b->pool);
}
}
else
{
apr_array_header_t *regular_props;
apr_hash_t *new_props;
svn_wc_notify_state_t prop_state;
SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(right_props,
scratch_pool),
NULL, NULL, &regular_props, scratch_pool));
new_props = svn_prop_array_to_hash(regular_props, scratch_pool);
svn_hash_sets(new_props, SVN_PROP_MERGEINFO, NULL);
/* ### What is the easiest way to set new_props on LOCAL_ABSPATH?
### This doesn't need a merge as we just added the node
### (or installed a tree conflict and skipped this node)*/
SVN_ERR(svn_wc_merge_props3(&prop_state, merge_b->ctx->wc_ctx,
local_abspath,
NULL, NULL,
apr_hash_make(scratch_pool),
svn_prop_hash_to_array(new_props,
scratch_pool),
merge_b->dry_run,
NULL, NULL,
merge_b->ctx->cancel_func,
merge_b->ctx->cancel_baton,
scratch_pool));
if (prop_state == svn_wc_notify_state_conflicted)
{
alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
merge_b->pool);
}
}
return SVN_NO_ERROR;
}
/* Helper for merge_dir_deleted. Implement svn_wc_status_func4_t */
static svn_error_t *
verify_touched_by_del_check(void *baton,
const char *local_abspath,
const svn_wc_status3_t *status,
apr_pool_t *scratch_pool)
{
struct dir_delete_baton_t *delb = baton;
if (svn_hash_gets(delb->compared_abspaths, local_abspath))
return SVN_NO_ERROR;
switch (status->node_status)
{
case svn_wc_status_deleted:
case svn_wc_status_ignored:
case svn_wc_status_none:
return SVN_NO_ERROR;
default:
delb->found_edit = TRUE;
return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
}
}
/* An svn_diff_tree_processor_t function.
*
* Called after merge_dir_opened() when a node existed only in the left source.
*
* After the merge_dir_opened() but before the call to this merge_dir_deleted()
* function all descendants that existed in left_source will have been deleted.
*
* If this node is replaced, an _opened() followed by a matching _add() will
* be invoked after this function.
*/
static svn_error_t *
merge_dir_deleted(const char *relpath,
const svn_diff_source_t *left_source,
/*const*/ apr_hash_t *left_props,
void *dir_baton,
const struct svn_diff_tree_processor_t *processor,
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t *merge_b = processor->baton;
struct merge_dir_baton_t *db = dir_baton;
const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
relpath, scratch_pool);
svn_boolean_t same;
apr_hash_t *working_props;
SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool));
SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
if (db->shadowed)
{
if (db->tree_conflict_reason == CONFLICT_REASON_NONE)
{
/* We haven't notified for this node yet: report a skip */
SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir,
svn_wc_notify_update_shadowed_delete,
- db->skip_reason, scratch_pool));
+ db->skip_reason, db->parent_baton,
+ scratch_pool));
}
return SVN_NO_ERROR;
}
/* Easy out: We are only applying mergeinfo differences. */
if (merge_b->record_only)
{
return SVN_NO_ERROR;
}
SVN_ERR(svn_wc_prop_list2(&working_props,
merge_b->ctx->wc_ctx, local_abspath,
scratch_pool, scratch_pool));
if (merge_b->force_delete)
{
/* In this legacy mode we just assume that a directory delete
matches any directory. db->delete_state is NULL */
same = TRUE;
}
else
{
struct dir_delete_baton_t *delb;
/* Compare the properties */
SVN_ERR(properties_same_p(&same, left_props, working_props,
scratch_pool));
delb = db->delete_state;
assert(delb != NULL);
if (! same)
{
delb->found_edit = TRUE;
}
else
{
store_path(delb->compared_abspaths, local_abspath);
}
if (delb->del_root != db)
return SVN_NO_ERROR;
if (delb->found_edit)
same = FALSE;
else
{
apr_array_header_t *ignores;
svn_error_t *err;
same = TRUE;
SVN_ERR(svn_wc_get_default_ignores(&ignores, merge_b->ctx->config,
scratch_pool));
/* None of the descendants was modified, but maybe there are
descendants we haven't walked?
Note that we aren't interested in changes, as we already verified
changes in the paths touched by the merge. And the existence of
other paths is enough to mark the directory edited */
err = svn_wc_walk_status(merge_b->ctx->wc_ctx, local_abspath,
svn_depth_infinity, TRUE /* get-all */,
FALSE /* no-ignore */,
TRUE /* ignore-text-mods */, ignores,
verify_touched_by_del_check, delb,
merge_b->ctx->cancel_func,
merge_b->ctx->cancel_baton,
scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_CEASE_INVOCATION)
return svn_error_trace(err);
svn_error_clear(err);
}
same = ! delb->found_edit;
}
}
if (same && !merge_b->dry_run)
{
svn_error_t *err;
err = svn_wc_delete4(merge_b->ctx->wc_ctx, local_abspath,
FALSE /* keep_local */, FALSE /* unversioned */,
merge_b->ctx->cancel_func,
merge_b->ctx->cancel_baton,
NULL, NULL /* no notify */,
scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_LEFT_LOCAL_MOD)
return svn_error_trace(err);
svn_error_clear(err);
same = FALSE;
}
}
if (! same)
{
/* If the attempt to delete an existing directory failed,
* the directory has local modifications (e.g. locally added
* files, or property changes). Flag a tree conflict. */
/* This handles use case 5 described in the paper attached to issue
* #2282. See also notes/tree-conflicts/detection.txt
*/
SVN_ERR(record_tree_conflict(merge_b, local_abspath, db->parent_baton,
svn_node_dir,
svn_wc_conflict_action_delete,
svn_wc_conflict_reason_edited,
NULL, TRUE,
scratch_pool));
}
else
{
/* Record that we might have deleted mergeinfo */
if (working_props
&& svn_hash_gets(working_props, SVN_PROP_MERGEINFO))
{
alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo,
local_abspath, merge_b->pool);
}
SVN_ERR(record_update_delete(merge_b, db->parent_baton, local_abspath,
svn_node_dir, scratch_pool));
}
return SVN_NO_ERROR;
}
/* An svn_diff_tree_processor_t function.
*
* Called after merge_dir_opened() when a node itself didn't change between
* the left and right source.
*
* After the merge_dir_opened() but before the call to this merge_dir_closed()
* function all descendants will have been processed.
*/
static svn_error_t *
merge_dir_closed(const char *relpath,
const svn_diff_source_t *left_source,
const svn_diff_source_t *right_source,
void *dir_baton,
const struct svn_diff_tree_processor_t *processor,
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t *merge_b = processor->baton;
struct merge_dir_baton_t *db = dir_baton;
SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool));
return SVN_NO_ERROR;
}
/* An svn_diff_tree_processor_t function.
Called when the diff driver wants to report an absent path.
In case of merges this happens when the diff encounters a server-excluded
path.
We register a skipped path, which will make parent mergeinfo non-
inheritable. This ensures that a future merge might see these skipped
changes as eligable for merging.
For legacy reasons we also notify the path as skipped.
*/
static svn_error_t *
merge_node_absent(const char *relpath,
void *dir_baton,
const svn_diff_tree_processor_t *processor,
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t *merge_b = processor->baton;
+ struct merge_dir_baton_t *db = dir_baton;
const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
relpath, scratch_pool);
SVN_ERR(record_skip(merge_b, local_abspath, svn_node_unknown,
svn_wc_notify_skip, svn_wc_notify_state_missing,
- scratch_pool));
+ db, scratch_pool));
return SVN_NO_ERROR;
}
/*-----------------------------------------------------------------------*/
/*** Merge Notification ***/
/* Finds a nearest ancestor in CHILDREN_WITH_MERGEINFO for LOCAL_ABSPATH. If
PATH_IS_OWN_ANCESTOR is TRUE then a child in CHILDREN_WITH_MERGEINFO
where child->abspath == PATH is considered PATH's ancestor. If FALSE,
then child->abspath must be a proper ancestor of PATH.
CHILDREN_WITH_MERGEINFO is expected to be sorted in Depth first
order of path. */
static svn_client__merge_path_t *
find_nearest_ancestor(const apr_array_header_t *children_with_mergeinfo,
svn_boolean_t path_is_own_ancestor,
const char *local_abspath)
{
int i;
SVN_ERR_ASSERT_NO_RETURN(children_with_mergeinfo != NULL);
for (i = children_with_mergeinfo->nelts - 1; i >= 0 ; i--)
{
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
if (svn_dirent_is_ancestor(child->abspath, local_abspath)
&& (path_is_own_ancestor
|| strcmp(child->abspath, local_abspath) != 0))
return child;
}
return NULL;
}
/* Find the highest level path in a merge target (possibly the merge target
itself) to use in a merge notification header.
Return the svn_client__merge_path_t * representing the most distant
ancestor in CHILDREN_WITH_MERGEINFO of LOCAL_ABSPATH where said
ancestor's first remaining ranges element (per the REMAINING_RANGES
member of the ancestor) intersect with the first remaining ranges element
for every intermediate ancestor svn_client__merge_path_t * of
LOCAL_ABSPATH. If no such ancestor is found return NULL.
If the remaining ranges of the elements in CHILDREN_WITH_MERGEINFO
represent a forward merge, then set *START to the oldest revision found
in any of the intersecting ancestors and *END to the youngest revision
found. If the remaining ranges of the elements in CHILDREN_WITH_MERGEINFO
represent a reverse merge, then set *START to the youngest revision
found and *END to the oldest revision found. If no ancestors are found
then set *START and *END to SVN_INVALID_REVNUM.
If PATH_IS_OWN_ANCESTOR is TRUE then a child in CHILDREN_WITH_MERGEINFO
where child->abspath == PATH is considered PATH's ancestor. If FALSE,
then child->abspath must be a proper ancestor of PATH.
See the CHILDREN_WITH_MERGEINFO ARRAY global comment for more
information. */
static svn_client__merge_path_t *
find_nearest_ancestor_with_intersecting_ranges(
svn_revnum_t *start,
svn_revnum_t *end,
const apr_array_header_t *children_with_mergeinfo,
svn_boolean_t path_is_own_ancestor,
const char *local_abspath)
{
int i;
svn_client__merge_path_t *nearest_ancestor = NULL;
*start = SVN_INVALID_REVNUM;
*end = SVN_INVALID_REVNUM;
SVN_ERR_ASSERT_NO_RETURN(children_with_mergeinfo != NULL);
for (i = children_with_mergeinfo->nelts - 1; i >= 0 ; i--)
{
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
if (svn_dirent_is_ancestor(child->abspath, local_abspath)
&& (path_is_own_ancestor
|| strcmp(child->abspath, local_abspath) != 0))
{
if (nearest_ancestor == NULL)
{
/* Found an ancestor. */
nearest_ancestor = child;
if (child->remaining_ranges)
{
svn_merge_range_t *r1 = APR_ARRAY_IDX(
child->remaining_ranges, 0, svn_merge_range_t *);
*start = r1->start;
*end = r1->end;
}
else
{
/* If CHILD->REMAINING_RANGES is null then LOCAL_ABSPATH
is inside an absent subtree in the merge target. */
*start = SVN_INVALID_REVNUM;
*end = SVN_INVALID_REVNUM;
break;
}
}
else
{
/* We'e found another ancestor for LOCAL_ABSPATH. Do its
first remaining range intersect with the previously
found ancestor? */
svn_merge_range_t *r1 =
APR_ARRAY_IDX(nearest_ancestor->remaining_ranges, 0,
svn_merge_range_t *);
svn_merge_range_t *r2 =
APR_ARRAY_IDX(child->remaining_ranges, 0,
svn_merge_range_t *);
if (r1 && r2)
{
svn_merge_range_t range1;
svn_merge_range_t range2;
svn_boolean_t reverse_merge = r1->start > r2->end;
/* Flip endpoints if this is a reverse merge. */
if (reverse_merge)
{
range1.start = r1->end;
range1.end = r1->start;
range2.start = r2->end;
range2.end = r2->start;
}
else
{
range1.start = r1->start;
range1.end = r1->end;
range2.start = r2->start;
range2.end = r2->end;
}
if (range1.start < range2.end && range2.start < range1.end)
{
*start = reverse_merge ?
MAX(r1->start, r2->start) : MIN(r1->start, r2->start);
*end = reverse_merge ?
MIN(r1->end, r2->end) : MAX(r1->end, r2->end);
nearest_ancestor = child;
}
}
}
}
}
return nearest_ancestor;
}
/* Notify that we're starting to record mergeinfo for the merge of the
* revision range RANGE into TARGET_ABSPATH. RANGE should be null if the
* merge sources are not from the same URL.
*
* This calls the client's notification receiver (as found in the client
* context), with a WC abspath.
*/
static void
notify_mergeinfo_recording(const char *target_abspath,
const svn_merge_range_t *range,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
if (ctx->notify_func2)
{
svn_wc_notify_t *n = svn_wc_create_notify(
target_abspath, svn_wc_notify_merge_record_info_begin, pool);
n->merge_range = range ? svn_merge_range_dup(range, pool) : NULL;
ctx->notify_func2(ctx->notify_baton2, n, pool);
}
}
/* Notify that we're completing the merge into TARGET_ABSPATH.
*
* This calls the client's notification receiver (as found in the client
* context), with a WC abspath.
*/
static void
notify_merge_completed(const char *target_abspath,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
if (ctx->notify_func2)
{
svn_wc_notify_t *n
= svn_wc_create_notify(target_abspath, svn_wc_notify_merge_completed,
pool);
ctx->notify_func2(ctx->notify_baton2, n, pool);
}
}
/* Is the notification the result of a real operative merge? */
#define IS_OPERATIVE_NOTIFICATION(notify) \
(notify->content_state == svn_wc_notify_state_conflicted \
|| notify->content_state == svn_wc_notify_state_merged \
|| notify->content_state == svn_wc_notify_state_changed \
|| notify->prop_state == svn_wc_notify_state_conflicted \
|| notify->prop_state == svn_wc_notify_state_merged \
|| notify->prop_state == svn_wc_notify_state_changed \
|| notify->action == svn_wc_notify_update_add \
|| notify->action == svn_wc_notify_tree_conflict)
/* Remove merge source gaps from range used for merge notifications.
See http://subversion.tigris.org/issues/show_bug.cgi?id=4138
If IMPLICIT_SRC_GAP is not NULL then it is a rangelist containing a
single range (see the implicit_src_gap member of merge_cmd_baton_t).
RANGE describes a (possibly reverse) merge.
If IMPLICIT_SRC_GAP is not NULL and it's sole range intersects with
the older revision in *RANGE, then remove IMPLICIT_SRC_GAP's range
from *RANGE. */
static void
remove_source_gap(svn_merge_range_t *range,
apr_array_header_t *implicit_src_gap)
{
if (implicit_src_gap)
{
svn_merge_range_t *gap_range =
APR_ARRAY_IDX(implicit_src_gap, 0, svn_merge_range_t *);
if (range->start < range->end)
{
if (gap_range->start == range->start)
range->start = gap_range->end;
}
else /* Reverse merge */
{
if (gap_range->start == range->end)
range->end = gap_range->end;
}
}
}
/* Notify that we're starting a merge
*
* This calls the client's notification receiver (as found in the client
* context), with a WC abspath.
*/
static svn_error_t *
notify_merge_begin(merge_cmd_baton_t *merge_b,
const char *local_abspath,
svn_boolean_t delete_action,
apr_pool_t *scratch_pool)
{
svn_wc_notify_t *notify;
svn_merge_range_t n_range =
{SVN_INVALID_REVNUM, SVN_INVALID_REVNUM, TRUE};
const char *notify_abspath;
if (! merge_b->ctx->notify_func2)
return SVN_NO_ERROR;
/* If our merge sources are ancestors of one another... */
if (merge_b->merge_source.ancestral)
{
const svn_client__merge_path_t *child;
/* Find NOTIFY->PATH's nearest ancestor in
NOTIFY->CHILDREN_WITH_MERGEINFO. Normally we consider a child in
NOTIFY->CHILDREN_WITH_MERGEINFO representing PATH to be an
ancestor of PATH, but if this is a deletion of PATH then the
notification must be for a proper ancestor of PATH. This ensures
we don't get notifications like:
--- Merging rX into 'PARENT/CHILD'
D PARENT/CHILD
But rather:
--- Merging rX into 'PARENT'
D PARENT/CHILD
*/
child = find_nearest_ancestor_with_intersecting_ranges(
&(n_range.start), &(n_range.end),
merge_b->notify_begin.nodes_with_mergeinfo,
! delete_action, local_abspath);
if (!child && delete_action)
{
/* Triggered by file replace in single-file-merge */
child = find_nearest_ancestor(merge_b->notify_begin.nodes_with_mergeinfo,
TRUE, local_abspath);
}
assert(child != NULL); /* Should always find the merge anchor */
if (! child)
return SVN_NO_ERROR;
if (merge_b->notify_begin.last_abspath != NULL
&& strcmp(child->abspath, merge_b->notify_begin.last_abspath) == 0)
{
/* Don't notify the same merge again */
return SVN_NO_ERROR;
}
merge_b->notify_begin.last_abspath = child->abspath;
if (child->absent || child->remaining_ranges->nelts == 0
|| !SVN_IS_VALID_REVNUM(n_range.start))
{
/* No valid information for an header */
return SVN_NO_ERROR;
}
notify_abspath = child->abspath;
}
else
{
if (merge_b->notify_begin.last_abspath)
return SVN_NO_ERROR; /* already notified */
notify_abspath = merge_b->target->abspath;
/* Store something in last_abspath. Any value would do */
merge_b->notify_begin.last_abspath = merge_b->target->abspath;
}
notify = svn_wc_create_notify(notify_abspath,
merge_b->same_repos
? svn_wc_notify_merge_begin
: svn_wc_notify_foreign_merge_begin,
scratch_pool);
if (SVN_IS_VALID_REVNUM(n_range.start))
{
/* If the merge source has a gap, then don't mention
those gap revisions in the notification. */
remove_source_gap(&n_range, merge_b->implicit_src_gap);
notify->merge_range = &n_range;
}
else
{
notify->merge_range = NULL;
}
(*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
scratch_pool);
return SVN_NO_ERROR;
}
/* Set *OUT_RANGELIST to the intersection of IN_RANGELIST with the simple
* (inheritable) revision range REV1:REV2, according to CONSIDER_INHERITANCE.
* If REV1 is equal to REV2, the result is an empty rangelist, otherwise
* REV1 must be less than REV2.
*
* Note: If CONSIDER_INHERITANCE is FALSE, the effect is to treat any non-
* inheritable input ranges as if they were inheritable. If it is TRUE, the
* effect is to discard any non-inheritable input ranges. Therefore the
* ranges in *OUT_RANGELIST will always be inheritable. */
static svn_error_t *
rangelist_intersect_range(svn_rangelist_t **out_rangelist,
const svn_rangelist_t *in_rangelist,
svn_revnum_t rev1,
svn_revnum_t rev2,
svn_boolean_t consider_inheritance,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
SVN_ERR_ASSERT(rev1 <= rev2);
if (rev1 < rev2)
{
svn_rangelist_t *simple_rangelist =
svn_rangelist__initialize(rev1, rev2, TRUE, scratch_pool);
SVN_ERR(svn_rangelist_intersect(out_rangelist,
simple_rangelist, in_rangelist,
consider_inheritance, result_pool));
}
else
{
*out_rangelist = apr_array_make(result_pool, 0,
sizeof(svn_merge_range_t *));
}
return SVN_NO_ERROR;
}
/* Helper for fix_deleted_subtree_ranges(). Like fix_deleted_subtree_ranges()
this function should only be called when honoring mergeinfo.
CHILD, PARENT, REVISION1, REVISION2, and RA_SESSION are all cascaded from
fix_deleted_subtree_ranges() -- see that function for more information on
each.
If PARENT is not the merge target then PARENT must have already have been
processed by this function as a child. Specifically, this means that
PARENT->REMAINING_RANGES must already be populated -- it can be an empty
rangelist but cannot be NULL.
PRIMARY_URL is the merge source url of CHILD at the younger of REVISION1
and REVISION2.
Since this function is only invoked for subtrees of the merge target, the
guarantees afforded by normalize_merge_sources() don't apply - see the
'MERGEINFO MERGE SOURCE NORMALIZATION' comment at the top of this file.
Therefore it is possible that PRIMARY_URL@REVISION1 and
PRIMARY_URL@REVISION2 don't describe the endpoints of an unbroken line of
history. The purpose of this helper is to identify these cases of broken
history and adjust CHILD->REMAINING_RANGES in such a way we don't later try
to describe nonexistent path/revisions to the merge report editor -- see
drive_merge_report_editor().
If PRIMARY_URL@REVISION1 and PRIMARY_URL@REVISION2 describe an unbroken
line of history then do nothing and leave CHILD->REMAINING_RANGES as-is.
If neither PRIMARY_URL@REVISION1 nor PRIMARY_URL@REVISION2 exist then
there is nothing to merge to CHILD->ABSPATH so set CHILD->REMAINING_RANGES
equal to PARENT->REMAINING_RANGES. This will cause the subtree to
effectively ignore CHILD -- see 'Note: If the first svn_merge_range_t...'
in drive_merge_report_editor()'s doc string.
If PRIMARY_URL@REVISION1 *xor* PRIMARY_URL@REVISION2 exist then we take the
subset of REVISION1:REVISION2 in CHILD->REMAINING_RANGES at which
PRIMARY_URL doesn't exist and set that subset equal to
PARENT->REMAINING_RANGES' intersection with that non-existent range. Why?
Because this causes CHILD->REMAINING_RANGES to be identical to
PARENT->REMAINING_RANGES for revisions between REVISION1 and REVISION2 at
which PRIMARY_URL doesn't exist. As mentioned above this means that
drive_merge_report_editor() won't attempt to describe these non-existent
subtree path/ranges to the reporter (which would break the merge).
If the preceding paragraph wasn't terribly clear then what follows spells
out this function's behavior a bit more explicitly:
For forward merges (REVISION1 < REVISION2)
If PRIMARY_URL@REVISION1 exists but PRIMARY_URL@REVISION2 doesn't, then
find the revision 'N' in which PRIMARY_URL@REVISION1 was deleted. Leave
the subset of CHILD->REMAINING_RANGES that intersects with
REVISION1:(N - 1) as-is and set the subset of CHILD->REMAINING_RANGES
that intersects with (N - 1):REVISION2 equal to PARENT->REMAINING_RANGES'
intersection with (N - 1):REVISION2.
If PRIMARY_URL@REVISION1 doesn't exist but PRIMARY_URL@REVISION2 does,
then find the revision 'M' in which PRIMARY_URL@REVISION2 came into
existence. Leave the subset of CHILD->REMAINING_RANGES that intersects with
(M - 1):REVISION2 as-is and set the subset of CHILD->REMAINING_RANGES
that intersects with REVISION1:(M - 1) equal to PARENT->REMAINING_RANGES'
intersection with REVISION1:(M - 1).
For reverse merges (REVISION1 > REVISION2)
If PRIMARY_URL@REVISION1 exists but PRIMARY_URL@REVISION2 doesn't, then
find the revision 'N' in which PRIMARY_URL@REVISION1 came into existence.
Leave the subset of CHILD->REMAINING_RANGES that intersects with
REVISION2:(N - 1) as-is and set the subset of CHILD->REMAINING_RANGES
that intersects with (N - 1):REVISION1 equal to PARENT->REMAINING_RANGES'
intersection with (N - 1):REVISION1.
If PRIMARY_URL@REVISION1 doesn't exist but PRIMARY_URL@REVISION2 does,
then find the revision 'M' in which PRIMARY_URL@REVISION2 came into
existence. Leave the subset of CHILD->REMAINING_RANGES that intersects with
REVISION2:(M - 1) as-is and set the subset of CHILD->REMAINING_RANGES
that intersects with (M - 1):REVISION1 equal to PARENT->REMAINING_RANGES'
intersection with REVISION1:(M - 1).
SCRATCH_POOL is used for all temporary allocations. Changes to CHILD are
allocated in RESULT_POOL. */
static svn_error_t *
adjust_deleted_subtree_ranges(svn_client__merge_path_t *child,
svn_client__merge_path_t *parent,
svn_revnum_t revision1,
svn_revnum_t revision2,
const char *primary_url,
svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_boolean_t is_rollback = revision2 < revision1;
svn_revnum_t younger_rev = is_rollback ? revision1 : revision2;
svn_revnum_t peg_rev = younger_rev;
svn_revnum_t older_rev = is_rollback ? revision2 : revision1;
apr_array_header_t *segments;
svn_error_t *err;
SVN_ERR_ASSERT(parent->remaining_ranges);
err = svn_client__repos_location_segments(&segments, ra_session,
primary_url, peg_rev,
younger_rev, older_rev, ctx,
scratch_pool);
/* If PRIMARY_URL@peg_rev doesn't exist then
svn_client__repos_location_segments() typically returns an
SVN_ERR_FS_NOT_FOUND error, but if it doesn't exist for a
forward merge over ra_neon then we get SVN_ERR_RA_DAV_REQUEST_FAILED.
http://subversion.tigris.org/issues/show_bug.cgi?id=3137 fixed some of
the cases where different RA layers returned different error codes to
signal the "path not found"...but it looks like there is more to do.
### Do we still need to special case for ra_neon (since it no longer
exists)? */
if (err)
{
if (err->apr_err == SVN_ERR_FS_NOT_FOUND
|| err->apr_err == SVN_ERR_RA_DAV_REQUEST_FAILED)
{
/* PRIMARY_URL@peg_rev doesn't exist. Check if PRIMARY_URL@older_rev
exists, if neither exist then the editor can simply ignore this
subtree. */
const char *rel_source_path; /* PRIMARY_URL relative to RA_SESSION */
svn_node_kind_t kind;
svn_error_clear(err);
err = NULL;
SVN_ERR(svn_ra_get_path_relative_to_session(
ra_session, &rel_source_path, primary_url, scratch_pool));
SVN_ERR(svn_ra_check_path(ra_session, rel_source_path,
older_rev, &kind, scratch_pool));
if (kind == svn_node_none)
{
/* Neither PRIMARY_URL@peg_rev nor PRIMARY_URL@older_rev exist,
so there is nothing to merge. Set CHILD->REMAINING_RANGES
identical to PARENT's. */
child->remaining_ranges =
svn_rangelist_dup(parent->remaining_ranges, scratch_pool);
}
else
{
svn_rangelist_t *deleted_rangelist;
svn_revnum_t rev_primary_url_deleted;
/* PRIMARY_URL@older_rev exists, so it was deleted at some
revision prior to peg_rev, find that revision. */
SVN_ERR(svn_ra_get_deleted_rev(ra_session, rel_source_path,
older_rev, younger_rev,
&rev_primary_url_deleted,
scratch_pool));
/* PRIMARY_URL@older_rev exists and PRIMARY_URL@peg_rev doesn't,
so svn_ra_get_deleted_rev() should always find the revision
PRIMARY_URL@older_rev was deleted. */
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev_primary_url_deleted));
/* If this is a reverse merge reorder CHILD->REMAINING_RANGES and
PARENT->REMAINING_RANGES so both will work with the
svn_rangelist_* APIs below. */
if (is_rollback)
{
/* svn_rangelist_reverse operates in place so it's safe
to use our scratch_pool. */
SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
scratch_pool));
SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
scratch_pool));
}
/* Find the intersection of CHILD->REMAINING_RANGES with the
range over which PRIMARY_URL@older_rev exists (ending at
the youngest revision at which it still exists). */
SVN_ERR(rangelist_intersect_range(&child->remaining_ranges,
child->remaining_ranges,
older_rev,
rev_primary_url_deleted - 1,
FALSE,
scratch_pool, scratch_pool));
/* Merge into CHILD->REMAINING_RANGES the intersection of
PARENT->REMAINING_RANGES with the range beginning when
PRIMARY_URL@older_rev was deleted until younger_rev. */
SVN_ERR(rangelist_intersect_range(&deleted_rangelist,
parent->remaining_ranges,
rev_primary_url_deleted - 1,
peg_rev,
FALSE,
scratch_pool, scratch_pool));
SVN_ERR(svn_rangelist_merge2(child->remaining_ranges,
deleted_rangelist, scratch_pool,
scratch_pool));
/* Return CHILD->REMAINING_RANGES and PARENT->REMAINING_RANGES
to reverse order if necessary. */
if (is_rollback)
{
SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
scratch_pool));
SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
scratch_pool));
}
}
}
else
{
return svn_error_trace(err);
}
}
else /* PRIMARY_URL@peg_rev exists. */
{
svn_rangelist_t *non_existent_rangelist;
svn_location_segment_t *segment =
APR_ARRAY_IDX(segments, (segments->nelts - 1),
svn_location_segment_t *);
/* We know PRIMARY_URL@peg_rev exists as the call to
svn_client__repos_location_segments() succeeded. If there is only
one segment that starts at oldest_rev then we know that
PRIMARY_URL@oldest_rev:PRIMARY_URL@peg_rev describes an unbroken
line of history, so there is nothing more to adjust in
CHILD->REMAINING_RANGES. */
if (segment->range_start == older_rev)
{
return SVN_NO_ERROR;
}
/* If this is a reverse merge reorder CHILD->REMAINING_RANGES and
PARENT->REMAINING_RANGES so both will work with the
svn_rangelist_* APIs below. */
if (is_rollback)
{
SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
scratch_pool));
SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
scratch_pool));
}
/* Intersect CHILD->REMAINING_RANGES with the range where PRIMARY_URL
exists. Since segment doesn't span older_rev:peg_rev we know
PRIMARY_URL@peg_rev didn't come into existence until
segment->range_start + 1. */
SVN_ERR(rangelist_intersect_range(&child->remaining_ranges,
child->remaining_ranges,
segment->range_start, peg_rev,
FALSE, scratch_pool, scratch_pool));
/* Merge into CHILD->REMAINING_RANGES the intersection of
PARENT->REMAINING_RANGES with the range before PRIMARY_URL@peg_rev
came into existence. */
SVN_ERR(rangelist_intersect_range(&non_existent_rangelist,
parent->remaining_ranges,
older_rev, segment->range_start,
FALSE, scratch_pool, scratch_pool));
SVN_ERR(svn_rangelist_merge2(child->remaining_ranges,
non_existent_rangelist, scratch_pool,
scratch_pool));
/* Return CHILD->REMAINING_RANGES and PARENT->REMAINING_RANGES
to reverse order if necessary. */
if (is_rollback)
{
SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
scratch_pool));
SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
scratch_pool));
}
}
/* Make a lasting copy of CHILD->REMAINING_RANGES using POOL. */
child->remaining_ranges = svn_rangelist_dup(child->remaining_ranges,
result_pool);
return SVN_NO_ERROR;
}
/* Helper for do_directory_merge().
SOURCE is cascaded from the argument of the same name in
do_directory_merge(). TARGET is the merge target. RA_SESSION is the
session for the younger of SOURCE->loc1 and SOURCE->loc2.
Adjust the subtrees in CHILDREN_WITH_MERGEINFO so that we don't
later try to describe invalid paths in drive_merge_report_editor().
This function is just a thin wrapper around
adjust_deleted_subtree_ranges(), which see for further details.
SCRATCH_POOL is used for all temporary allocations. Changes to
CHILDREN_WITH_MERGEINFO are allocated in RESULT_POOL.
*/
static svn_error_t *
fix_deleted_subtree_ranges(const merge_source_t *source,
const merge_target_t *target,
svn_ra_session_t *ra_session,
apr_array_header_t *children_with_mergeinfo,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
int i;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
svn_boolean_t is_rollback = source->loc2->rev < source->loc1->rev;
assert(session_url_is(ra_session,
(is_rollback ? source->loc1 : source->loc2)->url,
scratch_pool));
/* CHILDREN_WITH_MERGEINFO is sorted in depth-first order, so
start at index 1 to examine only subtrees. */
for (i = 1; i < children_with_mergeinfo->nelts; i++)
{
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
svn_client__merge_path_t *parent;
svn_rangelist_t *deleted_rangelist, *added_rangelist;
SVN_ERR_ASSERT(child);
if (child->absent)
continue;
svn_pool_clear(iterpool);
/* Find CHILD's parent. */
parent = find_nearest_ancestor(children_with_mergeinfo,
FALSE, child->abspath);
/* Since CHILD is a subtree then its parent must be in
CHILDREN_WITH_MERGEINFO, see the global comment
'THE CHILDREN_WITH_MERGEINFO ARRAY'. */
SVN_ERR_ASSERT(parent);
/* If this is a reverse merge reorder CHILD->REMAINING_RANGES
so it will work with the svn_rangelist_diff API. */
if (is_rollback)
{
SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges, iterpool));
}
SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist,
child->remaining_ranges,
parent->remaining_ranges,
TRUE, iterpool));
if (is_rollback)
{
SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges, iterpool));
}
/* If CHILD is the merge target we then know that SOURCE is provided
by normalize_merge_sources() -- see 'MERGEINFO MERGE SOURCE
NORMALIZATION'. Due to this normalization we know that SOURCE
describes an unbroken line of history such that the entire range
described by SOURCE can potentially be merged to CHILD.
But if CHILD is a subtree we don't have the same guarantees about
SOURCE as we do for the merge target. SOURCE->loc1 and/or
SOURCE->loc2 might not exist.
If one or both doesn't exist, then adjust CHILD->REMAINING_RANGES
such that we don't later try to describe invalid subtrees in
drive_merge_report_editor(), as that will break the merge.
If CHILD has the same remaining ranges as PARENT however, then
there is no need to make these adjustments, since
drive_merge_report_editor() won't attempt to describe CHILD in this
case, see the 'Note' in drive_merge_report_editor's docstring. */
if (deleted_rangelist->nelts || added_rangelist->nelts)
{
const char *child_primary_source_url;
const char *child_repos_src_path =
svn_dirent_is_child(target->abspath, child->abspath, iterpool);
/* This loop is only processing subtrees, so CHILD->ABSPATH
better be a proper child of the merge target. */
SVN_ERR_ASSERT(child_repos_src_path);
child_primary_source_url =
svn_path_url_add_component2((source->loc1->rev < source->loc2->rev)
? source->loc2->url : source->loc1->url,
child_repos_src_path, iterpool);
SVN_ERR(adjust_deleted_subtree_ranges(child, parent,
source->loc1->rev,
source->loc2->rev,
child_primary_source_url,
ra_session,
ctx, result_pool, iterpool));
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/*-----------------------------------------------------------------------*/
/*** Determining What Remains To Be Merged ***/
/* Get explicit and/or implicit mergeinfo for the working copy path
TARGET_ABSPATH.
If RECORDED_MERGEINFO is not NULL then set *RECORDED_MERGEINFO
to TARGET_ABSPATH's explicit or inherited mergeinfo as dictated by
INHERIT.
If IMPLICIT_MERGEINFO is not NULL then set *IMPLICIT_MERGEINFO
to TARGET_ABSPATH's implicit mergeinfo (a.k.a. natural history).
If both RECORDED_MERGEINFO and IMPLICIT_MERGEINFO are not NULL and
*RECORDED_MERGEINFO is inherited, then *IMPLICIT_MERGEINFO will be
removed from *RECORDED_MERGEINFO.
If INHERITED is not NULL set *INHERITED to TRUE if *RECORDED_MERGEINFO
is inherited rather than explicit. If RECORDED_MERGEINFO is NULL then
INHERITED is ignored.
If IMPLICIT_MERGEINFO is not NULL then START and END are limits on
the natural history sought, must both be valid revision numbers, and
START must be greater than END. If TARGET_ABSPATH's base revision
is older than START, then the base revision is used as the younger
bound in place of START.
RA_SESSION is an RA session open to the repository in which TARGET_ABSPATH
lives. It may be temporarily reparented as needed by this function.
Allocate *RECORDED_MERGEINFO and *IMPLICIT_MERGEINFO in RESULT_POOL.
Use SCRATCH_POOL for any temporary allocations. */
static svn_error_t *
get_full_mergeinfo(svn_mergeinfo_t *recorded_mergeinfo,
svn_mergeinfo_t *implicit_mergeinfo,
svn_boolean_t *inherited,
svn_mergeinfo_inheritance_t inherit,
svn_ra_session_t *ra_session,
const char *target_abspath,
svn_revnum_t start,
svn_revnum_t end,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
/* First, we get the real mergeinfo. */
if (recorded_mergeinfo)
{
SVN_ERR(svn_client__get_wc_or_repos_mergeinfo(recorded_mergeinfo,
inherited,
NULL /* from_repos */,
FALSE,
inherit, ra_session,
target_abspath,
ctx, result_pool));
}
if (implicit_mergeinfo)
{
svn_client__pathrev_t *target;
/* Assert that we have sane input. */
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(start) && SVN_IS_VALID_REVNUM(end)
&& (start > end));
/* Retrieve the origin (original_*) of the node, or just the
url if the node was not copied. */
SVN_ERR(svn_client__wc_node_get_origin(&target, target_abspath, ctx,
scratch_pool, scratch_pool));
if (! target)
{
/* We've been asked to operate on a locally added target, so its
* implicit mergeinfo is empty. */
*implicit_mergeinfo = apr_hash_make(result_pool);
}
else if (target->rev <= end)
{
/* We're asking about a range outside our natural history
altogether. That means our implicit mergeinfo is empty. */
*implicit_mergeinfo = apr_hash_make(result_pool);
}
else
{
/* Fetch so-called "implicit mergeinfo" (that is, natural
history). */
/* Do not ask for implicit mergeinfo from TARGET_ABSPATH's future.
TARGET_ABSPATH might not even exist, and even if it does the
working copy is *at* TARGET_REV so its implicit history ends
at TARGET_REV! */
if (target->rev < start)
start = target->rev;
/* Fetch the implicit mergeinfo. */
SVN_ERR(svn_client__get_history_as_mergeinfo(implicit_mergeinfo,
NULL,
target, start, end,
ra_session, ctx,
result_pool));
}
} /*if (implicit_mergeinfo) */
return SVN_NO_ERROR;
}
/* Helper for ensure_implicit_mergeinfo().
PARENT, CHILD, REVISION1, REVISION2 and CTX
are all cascaded from the arguments of the same names in
ensure_implicit_mergeinfo(). PARENT and CHILD must both exist, i.e.
this function should never be called where CHILD is the merge target.
If PARENT->IMPLICIT_MERGEINFO is NULL, obtain it from the server.
Set CHILD->IMPLICIT_MERGEINFO to the mergeinfo inherited from
PARENT->IMPLICIT_MERGEINFO. CHILD->IMPLICIT_MERGEINFO is allocated
in RESULT_POOL.
RA_SESSION is an RA session open to the repository that contains CHILD.
It may be temporarily reparented by this function.
*/
static svn_error_t *
inherit_implicit_mergeinfo_from_parent(svn_client__merge_path_t *parent,
svn_client__merge_path_t *child,
svn_revnum_t revision1,
svn_revnum_t revision2,
svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *path_diff;
/* This only works on subtrees! */
SVN_ERR_ASSERT(parent);
SVN_ERR_ASSERT(child);
/* While PARENT must exist, it is possible we've deferred
getting its implicit mergeinfo. If so get it now. */
if (!parent->implicit_mergeinfo)
SVN_ERR(get_full_mergeinfo(NULL, &(parent->implicit_mergeinfo),
NULL, svn_mergeinfo_inherited,
ra_session, child->abspath,
MAX(revision1, revision2),
MIN(revision1, revision2),
ctx, result_pool, scratch_pool));
/* Let CHILD inherit PARENT's implicit mergeinfo. */
path_diff = svn_dirent_is_child(parent->abspath, child->abspath,
scratch_pool);
/* PARENT->PATH better be an ancestor of CHILD->ABSPATH! */
SVN_ERR_ASSERT(path_diff);
SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(
&child->implicit_mergeinfo, parent->implicit_mergeinfo,
path_diff, result_pool, scratch_pool));
child->implicit_mergeinfo = svn_mergeinfo_dup(child->implicit_mergeinfo,
result_pool);
return SVN_NO_ERROR;
}
/* Helper of filter_merged_revisions().
If we have deferred obtaining CHILD->IMPLICIT_MERGEINFO, then get
it now, allocating it in RESULT_POOL. If CHILD_INHERITS_PARENT is true
then set CHILD->IMPLICIT_MERGEINFO to the mergeinfo inherited from
PARENT->IMPLICIT_MERGEINFO, otherwise contact the repository. Use
SCRATCH_POOL for all temporary allocations.
RA_SESSION is an RA session open to the repository that contains CHILD.
It may be temporarily reparented by this function.
PARENT, CHILD, REVISION1, REVISION2 and
CTX are all cascaded from the arguments of the same name in
filter_merged_revisions() and the same conditions for that function
hold here. */
static svn_error_t *
ensure_implicit_mergeinfo(svn_client__merge_path_t *parent,
svn_client__merge_path_t *child,
svn_boolean_t child_inherits_parent,
svn_revnum_t revision1,
svn_revnum_t revision2,
svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
/* If we haven't already found CHILD->IMPLICIT_MERGEINFO then
contact the server to get it. */
if (child->implicit_mergeinfo)
return SVN_NO_ERROR;
if (child_inherits_parent)
SVN_ERR(inherit_implicit_mergeinfo_from_parent(parent,
child,
revision1,
revision2,
ra_session,
ctx,
result_pool,
scratch_pool));
else
SVN_ERR(get_full_mergeinfo(NULL,
&(child->implicit_mergeinfo),
NULL, svn_mergeinfo_inherited,
ra_session, child->abspath,
MAX(revision1, revision2),
MIN(revision1, revision2),
ctx, result_pool, scratch_pool));
return SVN_NO_ERROR;
}
/* Helper for calculate_remaining_ranges().
Initialize CHILD->REMAINING_RANGES to a rangelist representing the
requested merge of REVISION1:REVISION2 from MERGEINFO_PATH to
CHILD->ABSPATH.
For forward merges remove any ranges from CHILD->REMAINING_RANGES that
have already been merged to CHILD->ABSPATH per TARGET_MERGEINFO or
CHILD->IMPLICIT_MERGEINFO. For reverse merges remove any ranges from
CHILD->REMAINING_RANGES that have not already been merged to CHILD->ABSPATH
per TARGET_MERGEINFO or CHILD->IMPLICIT_MERGEINFO. If we have deferred
obtaining CHILD->IMPLICIT_MERGEINFO and it is necessary to use it for
these calculations, then get it from the server, allocating it in
RESULT_POOL.
CHILD represents a working copy path which is the merge target or one of
the target's subtrees. If not NULL, PARENT is CHILD's nearest path-wise
ancestor - see 'THE CHILDREN_WITH_MERGEINFO ARRAY'.
If the function needs to consider CHILD->IMPLICIT_MERGEINFO and
CHILD_INHERITS_IMPLICIT is true, then set CHILD->IMPLICIT_MERGEINFO to the
mergeinfo inherited from PARENT->IMPLICIT_MERGEINFO. Otherwise contact
the repository for CHILD->IMPLICIT_MERGEINFO.
NOTE: If PARENT is present then this function must have previously been
called for PARENT, i.e. if populate_remaining_ranges() is calling this
function for a set of svn_client__merge_path_t* the calls must be made
in depth-first order.
MERGEINFO_PATH is the merge source relative to the repository root.
REVISION1 and REVISION2 describe the merge range requested from
MERGEINFO_PATH.
TARGET_RANGELIST is the portion of CHILD->ABSPATH's explicit or inherited
mergeinfo that intersects with the merge history described by
MERGEINFO_PATH@REVISION1:MERGEINFO_PATH@REVISION2. TARGET_RANGELIST
should be NULL if there is no explicit or inherited mergeinfo on
CHILD->ABSPATH or an empty list if CHILD->ABSPATH has empty mergeinfo or
explicit mergeinfo that exclusively describes non-intersecting history
with MERGEINFO_PATH@REVISION1:MERGEINFO_PATH@REVISION2.
SCRATCH_POOL is used for all temporary allocations.
NOTE: This should only be called when honoring mergeinfo.
NOTE: Like calculate_remaining_ranges() if PARENT is present then this
function must have previously been called for PARENT.
*/
static svn_error_t *
filter_merged_revisions(svn_client__merge_path_t *parent,
svn_client__merge_path_t *child,
const char *mergeinfo_path,
svn_rangelist_t *target_rangelist,
svn_revnum_t revision1,
svn_revnum_t revision2,
svn_boolean_t child_inherits_implicit,
svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_rangelist_t *requested_rangelist,
*target_implicit_rangelist, *explicit_rangelist;
/* Convert REVISION1 and REVISION2 to a rangelist.
Note: Talking about a requested merge range's inheritability
doesn't make much sense, but as we are using svn_merge_range_t
to describe it we need to pick *something*. Since all the
rangelist manipulations in this function either don't consider
inheritance by default or we are requesting that they don't (i.e.
svn_rangelist_remove and svn_rangelist_intersect) then we could
set the inheritability as FALSE, it won't matter either way. */
requested_rangelist = svn_rangelist__initialize(revision1, revision2,
TRUE, scratch_pool);
/* Now filter out revisions that have already been merged to CHILD. */
if (revision1 > revision2) /* This is a reverse merge. */
{
svn_rangelist_t *added_rangelist, *deleted_rangelist;
/* The revert range and will need to be reversed for
our svn_rangelist_* APIs to work properly. */
SVN_ERR(svn_rangelist_reverse(requested_rangelist, scratch_pool));
/* Set EXPLICIT_RANGELIST to the list of source-range revs that are
already recorded as merged to target. */
if (target_rangelist)
{
/* Return the intersection of the revs which are both already
represented by CHILD's explicit or inherited mergeinfo.
We don't consider inheritance when determining intersecting
ranges. If we *did* consider inheritance, then our calculation
would be wrong. For example, if the CHILD->REMAINING_RANGES is
5:3 and TARGET_RANGELIST is r5* (non-inheritable) then the
intersection would be r4. And that would be wrong as we clearly
want to reverse merge both r4 and r5 in this case. Ignoring the
ranges' inheritance results in an intersection of r4-5.
You might be wondering about CHILD's children, doesn't the above
imply that we will reverse merge r4-5 from them? Nope, this is
safe to do because any path whose parent has non-inheritable
ranges is always considered a subtree with differing mergeinfo
even if that path has no explicit mergeinfo prior to the
merge -- See condition 3 in the doc string for
merge.c:get_mergeinfo_paths(). */
SVN_ERR(svn_rangelist_intersect(&explicit_rangelist,
target_rangelist,
requested_rangelist,
FALSE, scratch_pool));
}
else
{
explicit_rangelist =
apr_array_make(result_pool, 0, sizeof(svn_merge_range_t *));
}
/* Was any part of the requested reverse merge not accounted for in
CHILD's explicit or inherited mergeinfo? */
SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist,
requested_rangelist, explicit_rangelist,
FALSE, scratch_pool));
if (deleted_rangelist->nelts == 0)
{
/* The whole of REVISION1:REVISION2 was represented in CHILD's
explicit/inherited mergeinfo, allocate CHILD's remaining
ranges in POOL and then we are done. */
SVN_ERR(svn_rangelist_reverse(requested_rangelist, scratch_pool));
child->remaining_ranges = svn_rangelist_dup(requested_rangelist,
result_pool);
}
else /* We need to check CHILD's implicit mergeinfo. */
{
svn_rangelist_t *implicit_rangelist;
SVN_ERR(ensure_implicit_mergeinfo(parent,
child,
child_inherits_implicit,
revision1,
revision2,
ra_session,
ctx,
result_pool,
scratch_pool));
target_implicit_rangelist = svn_hash_gets(child->implicit_mergeinfo,
mergeinfo_path);
if (target_implicit_rangelist)
SVN_ERR(svn_rangelist_intersect(&implicit_rangelist,
target_implicit_rangelist,
requested_rangelist,
FALSE, scratch_pool));
else
implicit_rangelist = apr_array_make(scratch_pool, 0,
sizeof(svn_merge_range_t *));
SVN_ERR(svn_rangelist_merge2(implicit_rangelist,
explicit_rangelist, scratch_pool,
scratch_pool));
SVN_ERR(svn_rangelist_reverse(implicit_rangelist, scratch_pool));
child->remaining_ranges = svn_rangelist_dup(implicit_rangelist,
result_pool);
}
}
else /* This is a forward merge */
{
/* Set EXPLICIT_RANGELIST to the list of source-range revs that are
NOT already recorded as merged to target. */
if (target_rangelist)
{
/* See earlier comment preceding svn_rangelist_intersect() for
why we don't consider inheritance here. */
SVN_ERR(svn_rangelist_remove(&explicit_rangelist,
target_rangelist,
requested_rangelist, FALSE,
scratch_pool));
}
else
{
explicit_rangelist = svn_rangelist_dup(requested_rangelist,
scratch_pool);
}
if (explicit_rangelist->nelts == 0)
{
child->remaining_ranges =
apr_array_make(result_pool, 0, sizeof(svn_merge_range_t *));
}
else
/* ### TODO: Which evil shall we choose?
###
### If we allow all forward-merges not already found in recorded
### mergeinfo, we destroy the ability to, say, merge the whole of a
### branch to the trunk while automatically ignoring the revisions
### common to both. That's bad.
###
### If we allow only forward-merges not found in either recorded
### mergeinfo or implicit mergeinfo (natural history), then the
### previous scenario works great, but we can't reverse-merge a
### previous change made to our line of history and then remake it
### (because the reverse-merge will leave no mergeinfo trace, and
### the remake-it attempt will still find the original change in
### natural mergeinfo. But you know, that we happen to use 'merge'
### for revision undoing is somewhat unnatural anyway, so I'm
### finding myself having little interest in caring too much about
### this. That said, if we had a way of storing reverse merge
### ranges, we'd be in good shape either way.
*/
#ifdef SVN_MERGE__ALLOW_ALL_FORWARD_MERGES_FROM_SELF
{
/* ### Don't consider implicit mergeinfo. */
child->remaining_ranges = svn_rangelist_dup(explicit_rangelist,
pool);
}
#else
{
/* Based on CHILD's TARGET_MERGEINFO there are ranges to merge.
Check CHILD's implicit mergeinfo to see if these remaining
ranges are represented there. */
SVN_ERR(ensure_implicit_mergeinfo(parent,
child,
child_inherits_implicit,
revision1,
revision2,
ra_session,
ctx,
result_pool,
scratch_pool));
target_implicit_rangelist = svn_hash_gets(child->implicit_mergeinfo,
mergeinfo_path);
if (target_implicit_rangelist)
SVN_ERR(svn_rangelist_remove(&(child->remaining_ranges),
target_implicit_rangelist,
explicit_rangelist,
FALSE, result_pool));
else
child->remaining_ranges = svn_rangelist_dup(explicit_rangelist,
result_pool);
}
#endif
}
return SVN_NO_ERROR;
}
/* Helper for do_file_merge and do_directory_merge (by way of
populate_remaining_ranges() for the latter).
Determine what portions of SOURCE have already
been merged to CHILD->ABSPATH and populate CHILD->REMAINING_RANGES with
the ranges that still need merging.
SOURCE and CTX are all cascaded from the caller's arguments of the same
names. Note that this means SOURCE adheres to the requirements noted in
`MERGEINFO MERGE SOURCE NORMALIZATION'.
CHILD represents a working copy path which is the merge target or one of
the target's subtrees. If not NULL, PARENT is CHILD's nearest path-wise
ancestor - see 'THE CHILDREN_WITH_MERGEINFO ARRAY'. TARGET_MERGEINFO is
the working mergeinfo on CHILD.
RA_SESSION is the session for the younger of SOURCE->loc1 and
SOURCE->loc2.
If the function needs to consider CHILD->IMPLICIT_MERGEINFO and
CHILD_INHERITS_IMPLICIT is true, then set CHILD->IMPLICIT_MERGEINFO to the
mergeinfo inherited from PARENT->IMPLICIT_MERGEINFO. Otherwise contact
the repository for CHILD->IMPLICIT_MERGEINFO.
If not null, IMPLICIT_SRC_GAP is the gap, if any, in the natural history
of SOURCE, see merge_cmd_baton_t.implicit_src_gap.
SCRATCH_POOL is used for all temporary allocations. Changes to CHILD and
PARENT are made in RESULT_POOL.
NOTE: This should only be called when honoring mergeinfo.
NOTE: If PARENT is present then this function must have previously been
called for PARENT, i.e. if populate_remaining_ranges() is calling this
function for a set of svn_client__merge_path_t* the calls must be made
in depth-first order.
NOTE: When performing reverse merges, return
SVN_ERR_CLIENT_NOT_READY_TO_MERGE if both locations in SOURCE and
CHILD->ABSPATH are all on the same line of history but CHILD->ABSPATH's
base revision is older than the SOURCE->rev1:rev2 range, see comment re
issue #2973 below.
*/
static svn_error_t *
calculate_remaining_ranges(svn_client__merge_path_t *parent,
svn_client__merge_path_t *child,
const merge_source_t *source,
svn_mergeinfo_t target_mergeinfo,
const apr_array_header_t *implicit_src_gap,
svn_boolean_t child_inherits_implicit,
svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const svn_client__pathrev_t *primary_src
= (source->loc1->rev < source->loc2->rev) ? source->loc2 : source->loc1;
const char *mergeinfo_path = svn_client__pathrev_fspath(primary_src,
scratch_pool);
/* Intersection of TARGET_MERGEINFO and the merge history
described by SOURCE. */
svn_rangelist_t *target_rangelist;
svn_revnum_t child_base_revision;
/* Since this function should only be called when honoring mergeinfo and
* SOURCE adheres to the requirements noted in 'MERGEINFO MERGE SOURCE
* NORMALIZATION', SOURCE must be 'ancestral'. */
SVN_ERR_ASSERT(source->ancestral);
/* Determine which of the requested ranges to consider merging... */
/* Set TARGET_RANGELIST to the portion of TARGET_MERGEINFO that refers
to SOURCE (excluding any gap in SOURCE): first get all ranges from
TARGET_MERGEINFO that refer to the path of SOURCE, and then prune
any ranges that lie in the gap in SOURCE.
### [JAF] In fact, that may still leave some ranges that lie entirely
outside the range of SOURCE; it seems we don't care about that. */
if (target_mergeinfo)
target_rangelist = svn_hash_gets(target_mergeinfo, mergeinfo_path);
else
target_rangelist = NULL;
if (implicit_src_gap && target_rangelist)
{
/* Remove any mergeinfo referring to the 'gap' in SOURCE, as that
mergeinfo doesn't really refer to SOURCE at all but instead
refers to locations that are non-existent or on a different
line of history. (Issue #3242.) */
SVN_ERR(svn_rangelist_remove(&target_rangelist,
implicit_src_gap, target_rangelist,
FALSE, result_pool));
}
/* Initialize CHILD->REMAINING_RANGES and filter out revisions already
merged (or, in the case of reverse merges, ranges not yet merged). */
SVN_ERR(filter_merged_revisions(parent, child, mergeinfo_path,
target_rangelist,
source->loc1->rev, source->loc2->rev,
child_inherits_implicit,
ra_session, ctx, result_pool,
scratch_pool));
/* Issue #2973 -- from the continuing series of "Why, since the advent of
merge tracking, allowing merges into mixed rev and locally modified
working copies isn't simple and could be considered downright evil".
If reverse merging a range to the WC path represented by CHILD, from
that path's own history, where the path inherits no locally modified
mergeinfo from its WC parents (i.e. there is no uncommitted merge to
the WC), and the path's base revision is older than the range, then
the merge will always be a no-op. This is because we only allow reverse
merges of ranges in the path's explicit or natural mergeinfo and a
reverse merge from the path's future history obviously isn't going to be
in either, hence the no-op.
The problem is two-fold. First, in a mixed rev WC, the change we
want to revert might actually be to some child of the target path
which is at a younger base revision. Sure, we can merge directly
to that child or update the WC or even use --ignore-ancestry and then
successfully run the reverse merge, but that gets to the second
problem: Those courses of action are not very obvious. Before 1.5 if
a user committed a change that didn't touch the commit target, then
immediately decided to revert that change via a reverse merge it would
just DTRT. But with the advent of merge tracking the user gets a no-op.
So in the name of user friendliness, return an error suggesting a helpful
course of action.
*/
SVN_ERR(svn_wc__node_get_base(NULL, &child_base_revision,
NULL, NULL, NULL, NULL,
ctx->wc_ctx, child->abspath,
TRUE /* ignore_enoent */,
FALSE /* show_hidden */,
scratch_pool, scratch_pool));
/* If CHILD has no base revision then it hasn't been committed yet, so it
can't have any "future" history. */
if (SVN_IS_VALID_REVNUM(child_base_revision)
&& ((child->remaining_ranges)->nelts == 0) /* Inoperative merge */
&& (source->loc2->rev < source->loc1->rev) /* Reverse merge */
&& (child_base_revision <= source->loc2->rev)) /* From CHILD's future */
{
/* Hmmm, an inoperative reverse merge from the "future". If it is
from our own future return a helpful error. */
svn_error_t *err;
svn_client__pathrev_t *start_loc;
err = svn_client__repos_location(&start_loc,
ra_session,
source->loc1,
child_base_revision,
ctx, scratch_pool, scratch_pool);
if (err)
{
if (err->apr_err == SVN_ERR_FS_NOT_FOUND
|| err->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES)
svn_error_clear(err);
else
return svn_error_trace(err);
}
else
{
const char *url;
SVN_ERR(svn_wc__node_get_url(&url, ctx->wc_ctx, child->abspath,
scratch_pool, scratch_pool));
if (strcmp(start_loc->url, url) == 0)
return svn_error_create(SVN_ERR_CLIENT_MERGE_UPDATE_REQUIRED, NULL,
_("Cannot reverse-merge a range from a "
"path's own future history; try "
"updating first"));
}
}
return SVN_NO_ERROR;
}
/* Helper for populate_remaining_ranges().
SOURCE is cascaded from the arguments of the same name in
populate_remaining_ranges().
Note: The following comments assume a forward merge, i.e.
SOURCE->loc1->rev < SOURCE->loc2->rev. If this is a reverse merge then
all the following comments still apply, but with SOURCE->loc1 switched
with SOURCE->loc2.
Like populate_remaining_ranges(), SOURCE must adhere to the restrictions
documented in 'MERGEINFO MERGE SOURCE NORMALIZATION'. These restrictions
allow for a *single* gap in SOURCE, GAP_REV1:GAP_REV2 exclusive:inclusive
(where SOURCE->loc1->rev == GAP_REV1 <= GAP_REV2 < SOURCE->loc2->rev),
if SOURCE->loc2->url@(GAP_REV2+1) was copied from SOURCE->loc1. If such
a gap exists, set *GAP_START and *GAP_END to the starting and ending
revisions of the gap. Otherwise set both to SVN_INVALID_REVNUM.
For example, if the natural history of URL@2:URL@9 is 'trunk/:2,7-9' this
would indicate that trunk@7 was copied from trunk@2. This function would
return GAP_START:GAP_END of 2:6 in this case. Note that a path 'trunk'
might exist at r3-6, but it would not be on the same line of history as
trunk@9.
### GAP_START is basically redundant, as (if there is a gap at all) it is
necessarily the older revision of SOURCE.
RA_SESSION is an open RA session to the repository in which SOURCE lives.
*/
static svn_error_t *
find_gaps_in_merge_source_history(svn_revnum_t *gap_start,
svn_revnum_t *gap_end,
const merge_source_t *source,
svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
svn_mergeinfo_t implicit_src_mergeinfo;
svn_revnum_t old_rev = MIN(source->loc1->rev, source->loc2->rev);
const svn_client__pathrev_t *primary_src
= (source->loc1->rev < source->loc2->rev) ? source->loc2 : source->loc1;
const char *merge_src_fspath = svn_client__pathrev_fspath(primary_src,
scratch_pool);
svn_rangelist_t *rangelist;
SVN_ERR_ASSERT(source->ancestral);
/* Start by assuming there is no gap. */
*gap_start = *gap_end = SVN_INVALID_REVNUM;
/* Easy out: There can't be a gap between adjacent revisions. */
if (abs(source->loc1->rev - source->loc2->rev) == 1)
return SVN_NO_ERROR;
/* Get SOURCE as mergeinfo. */
SVN_ERR(svn_client__get_history_as_mergeinfo(&implicit_src_mergeinfo, NULL,
primary_src,
primary_src->rev, old_rev,
ra_session,
ctx, scratch_pool));
rangelist = svn_hash_gets(implicit_src_mergeinfo, merge_src_fspath);
if (!rangelist) /* ### Can we ever not find a rangelist? */
return SVN_NO_ERROR;
/* A gap in natural history can result from either a copy or
a rename. If from a copy then history as mergeinfo will look
something like this:
'/trunk:X,Y-Z'
If from a rename it will look like this:
'/trunk_old_name:X'
'/trunk_new_name:Y-Z'
In both cases the gap, if it exists, is M-N, where M = X + 1 and
N = Y - 1.
Note that per the rules of 'MERGEINFO MERGE SOURCE NORMALIZATION' we
should never have multiple gaps, e.g. if we see anything like the
following then something is quite wrong:
'/trunk_old_name:A,B-C'
'/trunk_new_name:D-E'
*/
if (rangelist->nelts > 1) /* Copy */
{
const svn_merge_range_t *gap;
/* As mentioned above, multiple gaps *shouldn't* be possible. */
SVN_ERR_ASSERT(apr_hash_count(implicit_src_mergeinfo) == 1);
gap = APR_ARRAY_IDX(rangelist, rangelist->nelts - 1,
const svn_merge_range_t *);
*gap_start = MIN(source->loc1->rev, source->loc2->rev);
*gap_end = gap->start;
/* ### Issue #4132:
### This assertion triggers in merge_tests.py svnmucc_abuse_1()
### when a node is replaced by an older copy of itself.
BH: I think we should review this and the 'rename' case to find
out which behavior we really want, and if we can really
determine what happened this way. */
SVN_ERR_ASSERT(*gap_start < *gap_end);
}
else if (apr_hash_count(implicit_src_mergeinfo) > 1) /* Rename */
{
svn_rangelist_t *requested_rangelist =
svn_rangelist__initialize(MIN(source->loc1->rev, source->loc2->rev),
MAX(source->loc1->rev, source->loc2->rev),
TRUE, scratch_pool);
svn_rangelist_t *implicit_rangelist =
apr_array_make(scratch_pool, 2, sizeof(svn_merge_range_t *));
svn_rangelist_t *gap_rangelist;
SVN_ERR(svn_rangelist__merge_many(implicit_rangelist,
implicit_src_mergeinfo,
scratch_pool, scratch_pool));
SVN_ERR(svn_rangelist_remove(&gap_rangelist, implicit_rangelist,
requested_rangelist, FALSE,
scratch_pool));
/* If there is anything left it is the gap. */
if (gap_rangelist->nelts)
{
svn_merge_range_t *gap_range =
APR_ARRAY_IDX(gap_rangelist, 0, svn_merge_range_t *);
*gap_start = gap_range->start;
*gap_end = gap_range->end;
}
}
SVN_ERR_ASSERT(*gap_start == MIN(source->loc1->rev, source->loc2->rev)
|| (*gap_start == SVN_INVALID_REVNUM
&& *gap_end == SVN_INVALID_REVNUM));
return SVN_NO_ERROR;
}
/* Helper for do_directory_merge().
For each (svn_client__merge_path_t *) child in CHILDREN_WITH_MERGEINFO,
populate that child's 'remaining_ranges' list with (### ... what?),
and populate that child's 'implicit_mergeinfo' with its implicit
mergeinfo (natural history). CHILDREN_WITH_MERGEINFO is expected
to be sorted in depth first order and each child must be processed in
that order. The inheritability of all calculated ranges is TRUE.
If mergeinfo is being honored (based on MERGE_B -- see HONOR_MERGEINFO()
for how this is determined), this function will actually try to be
intelligent about populating remaining_ranges list. Otherwise, it
will claim that each child has a single remaining range, from
SOURCE->rev1, to SOURCE->rev2.
### We also take the short-cut if doing record-only. Why?
SCRATCH_POOL is used for all temporary allocations. Changes to
CHILDREN_WITH_MERGEINFO are made in RESULT_POOL.
Note that if SOURCE->rev1 > SOURCE->rev2, then each child's remaining_ranges
member does not adhere to the API rules for rangelists described in
svn_mergeinfo.h -- See svn_client__merge_path_t.
See `MERGEINFO MERGE SOURCE NORMALIZATION' for more requirements
around SOURCE.
*/
static svn_error_t *
populate_remaining_ranges(apr_array_header_t *children_with_mergeinfo,
const merge_source_t *source,
svn_ra_session_t *ra_session,
merge_cmd_baton_t *merge_b,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
int i;
svn_revnum_t gap_start, gap_end;
/* If we aren't honoring mergeinfo or this is a --record-only merge,
we'll make quick work of this by simply adding dummy SOURCE->rev1:rev2
ranges for all children. */
if (! HONOR_MERGEINFO(merge_b) || merge_b->record_only)
{
for (i = 0; i < children_with_mergeinfo->nelts; i++)
{
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i,
svn_client__merge_path_t *);
svn_pool_clear(iterpool);
/* Issue #3646 'record-only merges create self-referential
mergeinfo'. Get the merge target's implicit mergeinfo (natural
history). We'll use it later to avoid setting self-referential
mergeinfo -- see filter_natural_history_from_mergeinfo(). */
if (i == 0) /* First item is always the merge target. */
{
SVN_ERR(get_full_mergeinfo(NULL, /* child->pre_merge_mergeinfo */
&(child->implicit_mergeinfo),
NULL, /* child->inherited_mergeinfo */
svn_mergeinfo_inherited, ra_session,
child->abspath,
MAX(source->loc1->rev,
source->loc2->rev),
MIN(source->loc1->rev,
source->loc2->rev),
merge_b->ctx, result_pool,
iterpool));
}
else
{
/* Issue #3443 - Subtrees of the merge target can inherit
their parent's implicit mergeinfo in most cases. */
svn_client__merge_path_t *parent
= find_nearest_ancestor(children_with_mergeinfo,
FALSE, child->abspath);
svn_boolean_t child_inherits_implicit;
/* If CHILD is a subtree then its parent must be in
CHILDREN_WITH_MERGEINFO, see the global comment
'THE CHILDREN_WITH_MERGEINFO ARRAY'. */
SVN_ERR_ASSERT(parent);
child_inherits_implicit = (parent && !child->switched);
SVN_ERR(ensure_implicit_mergeinfo(parent, child,
child_inherits_implicit,
source->loc1->rev,
source->loc2->rev,
ra_session, merge_b->ctx,
result_pool, iterpool));
}
child->remaining_ranges = svn_rangelist__initialize(source->loc1->rev,
source->loc2->rev,
TRUE,
result_pool);
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* If, in the merge source's history, there was a copy from an older
revision, then SOURCE->loc2->url won't exist at some range M:N, where
SOURCE->loc1->rev < M < N < SOURCE->loc2->rev. The rules of 'MERGEINFO
MERGE SOURCE NORMALIZATION' allow this, but we must ignore these gaps
when calculating what ranges remain to be merged from SOURCE. If we
don't and try to merge any part of SOURCE->loc2->url@M:N we would
break the editor since no part of that actually exists. See
http://svn.haxx.se/dev/archive-2008-11/0618.shtml.
Find the gaps in the merge target's history, if any. Eventually
we will adjust CHILD->REMAINING_RANGES such that we don't describe
non-existent paths to the editor. */
SVN_ERR(find_gaps_in_merge_source_history(&gap_start, &gap_end,
source,
ra_session, merge_b->ctx,
iterpool));
/* Stash any gap in the merge command baton, we'll need it later when
recording mergeinfo describing this merge. */
if (SVN_IS_VALID_REVNUM(gap_start) && SVN_IS_VALID_REVNUM(gap_end))
merge_b->implicit_src_gap = svn_rangelist__initialize(gap_start, gap_end,
TRUE, result_pool);
for (i = 0; i < children_with_mergeinfo->nelts; i++)
{
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
const char *child_repos_path
= svn_dirent_skip_ancestor(merge_b->target->abspath, child->abspath);
merge_source_t child_source;
svn_client__merge_path_t *parent = NULL;
svn_boolean_t child_inherits_implicit;
svn_pool_clear(iterpool);
/* If the path is absent don't do subtree merge either. */
SVN_ERR_ASSERT(child);
if (child->absent)
continue;
SVN_ERR_ASSERT(child_repos_path != NULL);
child_source.loc1 = svn_client__pathrev_join_relpath(
source->loc1, child_repos_path, iterpool);
child_source.loc2 = svn_client__pathrev_join_relpath(
source->loc2, child_repos_path, iterpool);
/* ### Is the child 'ancestral' over the same revision range? It's
* not necessarily true that a child is 'ancestral' if the parent is,
* nor that it's not if the parent is not. However, here we claim
* that it is. Before we had this 'ancestral' field that we need to
* set explicitly, the claim was implicit. Either way, the impact is
* that we might pass calculate_remaining_ranges() a source that is
* not in fact 'ancestral' (despite its 'ancestral' field being true),
* contrary to its doc-string. */
child_source.ancestral = source->ancestral;
/* Get the explicit/inherited mergeinfo for CHILD. If CHILD is the
merge target then also get its implicit mergeinfo. Otherwise defer
this until we know it is absolutely necessary, since it requires an
expensive round trip communication with the server. */
SVN_ERR(get_full_mergeinfo(
child->pre_merge_mergeinfo ? NULL : &(child->pre_merge_mergeinfo),
/* Get implicit only for merge target. */
(i == 0) ? &(child->implicit_mergeinfo) : NULL,
&(child->inherited_mergeinfo),
svn_mergeinfo_inherited, ra_session,
child->abspath,
MAX(source->loc1->rev, source->loc2->rev),
MIN(source->loc1->rev, source->loc2->rev),
merge_b->ctx, result_pool, iterpool));
/* If CHILD isn't the merge target find its parent. */
if (i > 0)
{
parent = find_nearest_ancestor(children_with_mergeinfo,
FALSE, child->abspath);
/* If CHILD is a subtree then its parent must be in
CHILDREN_WITH_MERGEINFO, see the global comment
'THE CHILDREN_WITH_MERGEINFO ARRAY'. */
SVN_ERR_ASSERT(parent);
}
/* Issue #3443 - Can CHILD inherit PARENT's implicit mergeinfo, saving
us from having to ask the repos? The only time we can't do this is if
CHILD is the merge target and so there is no PARENT to inherit from
or if CHILD is the root of a switched subtree, in which case PARENT
exists but is not CHILD's repository parent. */
child_inherits_implicit = (parent && !child->switched);
SVN_ERR(calculate_remaining_ranges(parent, child,
&child_source,
child->pre_merge_mergeinfo,
merge_b->implicit_src_gap,
child_inherits_implicit,
ra_session,
merge_b->ctx, result_pool,
iterpool));
/* Deal with any gap in SOURCE's natural history.
If the gap is a proper subset of CHILD->REMAINING_RANGES then we can
safely ignore it since we won't describe this path/rev pair.
If the gap exactly matches or is a superset of a range in
CHILD->REMAINING_RANGES then we must remove that range so we don't
attempt to describe non-existent paths via the reporter, this will
break the editor and our merge.
If the gap adjoins or overlaps a range in CHILD->REMAINING_RANGES
then we must *add* the gap so we span the missing revisions. */
if (child->remaining_ranges->nelts
&& merge_b->implicit_src_gap)
{
int j;
svn_boolean_t proper_subset = FALSE;
svn_boolean_t overlaps_or_adjoins = FALSE;
/* If this is a reverse merge reorder CHILD->REMAINING_RANGES
so it will work with the svn_rangelist_* APIs below. */
if (source->loc1->rev > source->loc2->rev)
SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
for (j = 0; j < child->remaining_ranges->nelts; j++)
{
svn_merge_range_t *range
= APR_ARRAY_IDX(child->remaining_ranges, j, svn_merge_range_t *);
if ((range->start <= gap_start && gap_end < range->end)
|| (range->start < gap_start && gap_end <= range->end))
{
proper_subset = TRUE;
break;
}
else if ((gap_start == range->start) && (range->end == gap_end))
{
break;
}
else if (gap_start <= range->end && range->start <= gap_end)
/* intersect */
{
overlaps_or_adjoins = TRUE;
break;
}
}
if (!proper_subset)
{
/* We need to make adjustments. Remove from, or add the gap
to, CHILD->REMAINING_RANGES as appropriate. */
if (overlaps_or_adjoins)
SVN_ERR(svn_rangelist_merge2(child->remaining_ranges,
merge_b->implicit_src_gap,
result_pool, iterpool));
else /* equals == TRUE */
SVN_ERR(svn_rangelist_remove(&(child->remaining_ranges),
merge_b->implicit_src_gap,
child->remaining_ranges, FALSE,
result_pool));
}
if (source->loc1->rev > source->loc2->rev) /* Reverse merge */
SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/*-----------------------------------------------------------------------*/
/*** Other Helper Functions ***/
/* Calculate the new mergeinfo for the target tree rooted at TARGET_ABSPATH
based on MERGES (a mapping of absolute WC paths to rangelists representing
a merge from the source SOURCE_FSPATH).
If RESULT_CATALOG is NULL, then record the new mergeinfo in the WC (at,
and possibly below, TARGET_ABSPATH).
If RESULT_CATALOG is not NULL, then don't record the new mergeinfo on the
WC, but instead record it in RESULT_CATALOG, where the keys are absolute
working copy paths and the values are the new mergeinfos for each.
Allocate additions to RESULT_CATALOG in pool which RESULT_CATALOG was
created in. */
static svn_error_t *
update_wc_mergeinfo(svn_mergeinfo_catalog_t result_catalog,
const char *target_abspath,
const char *source_fspath,
apr_hash_t *merges,
svn_boolean_t is_rollback,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
apr_hash_index_t *hi;
/* Combine the mergeinfo for the revision range just merged into
the WC with its on-disk mergeinfo. */
for (hi = apr_hash_first(scratch_pool, merges); hi; hi = apr_hash_next(hi))
{
const char *local_abspath = svn__apr_hash_index_key(hi);
svn_rangelist_t *ranges = svn__apr_hash_index_val(hi);
svn_rangelist_t *rangelist;
svn_error_t *err;
const char *local_abspath_rel_to_target;
const char *fspath;
svn_mergeinfo_t mergeinfo;
svn_pool_clear(iterpool);
/* As some of the merges may've changed the WC's mergeinfo, get
a fresh copy before using it to update the WC's mergeinfo. */
err = svn_client__parse_mergeinfo(&mergeinfo, ctx->wc_ctx,
local_abspath, iterpool, iterpool);
/* If a directory PATH was skipped because it is missing or was
obstructed by an unversioned item then there's nothing we can
do with that, so skip it. */
if (err)
{
if (err->apr_err == SVN_ERR_WC_NOT_LOCKED
|| err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
{
svn_error_clear(err);
continue;
}
else
{
return svn_error_trace(err);
}
}
/* If we are attempting to set empty revision range override mergeinfo
on a path with no explicit mergeinfo, we first need the
mergeinfo that path inherits. */
if (mergeinfo == NULL && ranges->nelts == 0)
{
SVN_ERR(svn_client__get_wc_mergeinfo(&mergeinfo, NULL,
svn_mergeinfo_nearest_ancestor,
local_abspath, NULL, NULL,
FALSE, ctx, iterpool, iterpool));
}
if (mergeinfo == NULL)
mergeinfo = apr_hash_make(iterpool);
local_abspath_rel_to_target = svn_dirent_skip_ancestor(target_abspath,
local_abspath);
SVN_ERR_ASSERT(local_abspath_rel_to_target != NULL);
fspath = svn_fspath__join(source_fspath,
local_abspath_rel_to_target,
iterpool);
rangelist = svn_hash_gets(mergeinfo, fspath);
if (rangelist == NULL)
rangelist = apr_array_make(iterpool, 0, sizeof(svn_merge_range_t *));
if (is_rollback)
{
ranges = svn_rangelist_dup(ranges, iterpool);
SVN_ERR(svn_rangelist_reverse(ranges, iterpool));
SVN_ERR(svn_rangelist_remove(&rangelist, ranges, rangelist,
FALSE,
iterpool));
}
else
{
SVN_ERR(svn_rangelist_merge2(rangelist, ranges, iterpool, iterpool));
}
/* Update the mergeinfo by adjusting the path's rangelist. */
svn_hash_sets(mergeinfo, fspath, rangelist);
if (is_rollback && apr_hash_count(mergeinfo) == 0)
mergeinfo = NULL;
svn_mergeinfo__remove_empty_rangelists(mergeinfo, scratch_pool);
if (result_catalog)
{
svn_mergeinfo_t existing_mergeinfo =
svn_hash_gets(result_catalog, local_abspath);
apr_pool_t *result_catalog_pool = apr_hash_pool_get(result_catalog);
if (existing_mergeinfo)
SVN_ERR(svn_mergeinfo_merge2(mergeinfo, existing_mergeinfo,
result_catalog_pool, scratch_pool));
svn_hash_sets(result_catalog,
apr_pstrdup(result_catalog_pool, local_abspath),
svn_mergeinfo_dup(mergeinfo, result_catalog_pool));
}
else
{
err = svn_client__record_wc_mergeinfo(local_abspath, mergeinfo,
TRUE, ctx, iterpool);
if (err && err->apr_err == SVN_ERR_ENTRY_NOT_FOUND)
{
/* PATH isn't just missing, it's not even versioned as far
as this working copy knows. But it was included in
MERGES, which means that the server knows about it.
Likely we don't have access to the source due to authz
restrictions. For now just clear the error and
continue...
### TODO: Set non-inheritable mergeinfo on PATH's immediate
### parent and normal mergeinfo on PATH's siblings which we
### do have access to. */
svn_error_clear(err);
}
else
SVN_ERR(err);
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Helper for record_mergeinfo_for_dir_merge().
Record override mergeinfo on any paths skipped during a merge.
Set empty mergeinfo on each path in MERGE_B->SKIPPED_ABSPATHS so the path
does not incorrectly inherit mergeinfo that will later be describing
the merge.
MERGEINFO_PATH and MERGE_B are cascaded from
arguments of the same name in the caller.
IS_ROLLBACK is true if the caller is recording a reverse merge and false
otherwise. RANGELIST is the set of revisions being merged from
MERGEINFO_PATH to MERGE_B->target. */
static svn_error_t *
record_skips_in_mergeinfo(const char *mergeinfo_path,
const svn_rangelist_t *rangelist,
svn_boolean_t is_rollback,
merge_cmd_baton_t *merge_b,
apr_pool_t *scratch_pool)
{
apr_hash_index_t *hi;
apr_hash_t *merges;
apr_size_t nbr_skips = apr_hash_count(merge_b->skipped_abspaths);
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
if (nbr_skips == 0)
return SVN_NO_ERROR;
merges = apr_hash_make(scratch_pool);
/* Override the mergeinfo for child paths which weren't actually merged. */
for (hi = apr_hash_first(scratch_pool, merge_b->skipped_abspaths); hi;
hi = apr_hash_next(hi))
{
const char *skipped_abspath = svn__apr_hash_index_key(hi);
svn_wc_notify_state_t obstruction_state;
svn_pool_clear(iterpool);
/* Before we override, make sure this is a versioned path, it might
be an external or missing from disk due to authz restrictions. */
SVN_ERR(perform_obstruction_check(&obstruction_state, NULL, NULL,
NULL, NULL,
merge_b, skipped_abspath,
iterpool));
if (obstruction_state == svn_wc_notify_state_obstructed
|| obstruction_state == svn_wc_notify_state_missing)
continue;
/* Add an empty range list for this path.
### TODO: This works fine for a file path skipped because it is
### missing as long as the file's parent directory is present.
### But missing directory paths skipped are not handled yet,
### see issue #2915.
### TODO: An empty range is fine if the skipped path doesn't
### inherit any mergeinfo from a parent, but if it does
### we need to account for that. See issue #3440
### http://subversion.tigris.org/issues/show_bug.cgi?id=3440. */
svn_hash_sets(merges, skipped_abspath,
apr_array_make(scratch_pool, 0,
sizeof(svn_merge_range_t *)));
/* if (nbr_skips < notify_b->nbr_notifications)
### Use RANGELIST as the mergeinfo for all children of
### this path which were not also explicitly
### skipped? */
}
SVN_ERR(update_wc_mergeinfo(NULL, merge_b->target->abspath,
mergeinfo_path, merges,
is_rollback, merge_b->ctx, iterpool));
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Data for reporting when a merge aborted because of raising conflicts.
*/
typedef struct single_range_conflict_report_t
{
/* What sub-range of the requested source raised conflicts?
* The 'inheritable' flag is ignored. */
merge_source_t *conflicted_range;
/* What sub-range of the requested source remains to be merged?
* NULL if no more. The 'inheritable' flag is ignored. */
merge_source_t *remaining_source;
} single_range_conflict_report_t;
/* Create a single_range_conflict_report_t, containing deep copies of
* CONFLICTED_RANGE and REMAINING_SOURCE, allocated in RESULT_POOL. */
static single_range_conflict_report_t *
single_range_conflict_report_create(const merge_source_t *conflicted_range,
const merge_source_t *remaining_source,
apr_pool_t *result_pool)
{
single_range_conflict_report_t *report
= apr_palloc(result_pool, sizeof(*report));
assert(conflicted_range != NULL);
report->conflicted_range = merge_source_dup(conflicted_range, result_pool);
report->remaining_source
= remaining_source ? merge_source_dup(remaining_source, result_pool)
: NULL;
return report;
}
/* Data for reporting when a merge aborted because of raising conflicts.
*
* ### TODO: More info, including the ranges (or other parameters) the user
* needs to complete the merge.
*/
typedef struct conflict_report_t
{
const char *target_abspath;
/* The revision range during which conflicts were raised */
const merge_source_t *conflicted_range;
/* Was the conflicted range the last range in the whole requested merge? */
svn_boolean_t was_last_range;
} conflict_report_t;
/* Return a new conflict_report_t containing deep copies of the parameters,
* allocated in RESULT_POOL. */
static conflict_report_t *
conflict_report_create(const char *target_abspath,
const merge_source_t *conflicted_range,
svn_boolean_t was_last_range,
apr_pool_t *result_pool)
{
conflict_report_t *report = apr_palloc(result_pool, sizeof(*report));
report->target_abspath = apr_pstrdup(result_pool, target_abspath);
report->conflicted_range = merge_source_dup(conflicted_range, result_pool);
report->was_last_range = was_last_range;
return report;
}
/* Return a deep copy of REPORT, allocated in RESULT_POOL. */
static conflict_report_t *
conflict_report_dup(const conflict_report_t *report,
apr_pool_t *result_pool)
{
conflict_report_t *new = apr_pmemdup(result_pool, report, sizeof(*new));
new->target_abspath = apr_pstrdup(result_pool, report->target_abspath);
new->conflicted_range = merge_source_dup(report->conflicted_range,
result_pool);
return new;
}
/* Create and return an error structure appropriate for the unmerged
revisions range(s). */
static APR_INLINE svn_error_t *
make_merge_conflict_error(conflict_report_t *report,
apr_pool_t *scratch_pool)
{
assert(!report || svn_dirent_is_absolute(report->target_abspath));
if (report && ! report->was_last_range)
{
svn_error_t *err = svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
_("One or more conflicts were produced while merging r%ld:%ld into\n"
"'%s' --\n"
"resolve all conflicts and rerun the merge to apply the remaining\n"
"unmerged revisions"),
report->conflicted_range->loc1->rev, report->conflicted_range->loc2->rev,
svn_dirent_local_style(report->target_abspath, scratch_pool));
assert(report->conflicted_range->loc1->rev != report->conflicted_range->loc2->rev); /* ### is a valid case in a 2-URL merge */
return err;
}
return SVN_NO_ERROR;
}
/* Helper for do_directory_merge().
TARGET_WCPATH is a directory and CHILDREN_WITH_MERGEINFO is filled
with paths (svn_client__merge_path_t *) arranged in depth first order,
which have mergeinfo set on them or meet one of the other criteria
defined in get_mergeinfo_paths(). Remove any paths absent from disk
or scheduled for deletion from CHILDREN_WITH_MERGEINFO which are equal to
or are descendants of TARGET_WCPATH by setting those children to NULL. */
static void
remove_absent_children(const char *target_wcpath,
apr_array_header_t *children_with_mergeinfo)
{
/* Before we try to override mergeinfo for skipped paths, make sure
the path isn't absent due to authz restrictions, because there's
nothing we can do about those. */
int i;
for (i = 0; i < children_with_mergeinfo->nelts; i++)
{
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
if ((child->absent || child->scheduled_for_deletion)
&& svn_dirent_is_ancestor(target_wcpath, child->abspath))
{
svn_sort__array_delete(children_with_mergeinfo, i--, 1);
}
}
}
/* Helper for do_directory_merge() to handle the case where a merge editor
drive removes explicit mergeinfo from a subtree of the merge target.
MERGE_B is cascaded from the argument of the same name in
do_directory_merge(). For each path (if any) in
MERGE_B->PATHS_WITH_DELETED_MERGEINFO remove that path from
CHILDREN_WITH_MERGEINFO.
The one exception is for the merge target itself,
MERGE_B->target->abspath, this must always be present in
CHILDREN_WITH_MERGEINFO so this is never removed by this
function. */
static void
remove_children_with_deleted_mergeinfo(merge_cmd_baton_t *merge_b,
apr_array_header_t *children_with_mergeinfo)
{
int i;
if (!merge_b->paths_with_deleted_mergeinfo)
return;
/* CHILDREN_WITH_MERGEINFO[0] is the always the merge target
so start at the first child. */
for (i = 1; i < children_with_mergeinfo->nelts; i++)
{
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
if (svn_hash_gets(merge_b->paths_with_deleted_mergeinfo, child->abspath))
{
svn_sort__array_delete(children_with_mergeinfo, i--, 1);
}
}
}
/* Helper for do_directory_merge().
Set up the diff editor report to merge the SOURCE diff
into TARGET_ABSPATH and drive it.
If mergeinfo is not being honored (based on MERGE_B -- see the doc
string for HONOR_MERGEINFO() for how this is determined), then ignore
CHILDREN_WITH_MERGEINFO and merge the SOURCE diff to TARGET_ABSPATH.
If mergeinfo is being honored then perform a history-aware merge,
describing TARGET_ABSPATH and its subtrees to the reporter in such as way
as to avoid repeating merges already performed per the mergeinfo and
natural history of TARGET_ABSPATH and its subtrees.
The ranges that still need to be merged to the TARGET_ABSPATH and its
subtrees are described in CHILDREN_WITH_MERGEINFO, an array of
svn_client__merge_path_t * -- see 'THE CHILDREN_WITH_MERGEINFO ARRAY'
comment at the top of this file for more info. Note that it is possible
TARGET_ABSPATH and/or some of its subtrees need only a subset, or no part,
of SOURCE to be merged. Though there is little point to
calling this function if TARGET_ABSPATH and all its subtrees have already
had SOURCE merged, this will work but is a no-op.
SOURCE->rev1 and SOURCE->rev2 must be bound by the set of remaining_ranges
fields in CHILDREN_WITH_MERGEINFO's elements, specifically:
For forward merges (SOURCE->rev1 < SOURCE->rev2):
1) The first svn_merge_range_t * element of each child's remaining_ranges
array must meet one of the following conditions:
a) The range's start field is greater than or equal to SOURCE->rev2.
b) The range's end field is SOURCE->rev2.
2) Among all the ranges that meet condition 'b' the oldest start
revision must equal SOURCE->rev1.
For reverse merges (SOURCE->rev1 > SOURCE->rev2):
1) The first svn_merge_range_t * element of each child's remaining_ranges
array must meet one of the following conditions:
a) The range's start field is less than or equal to SOURCE->rev2.
b) The range's end field is SOURCE->rev2.
2) Among all the ranges that meet condition 'b' the youngest start
revision must equal SOURCE->rev1.
Note: If the first svn_merge_range_t * element of some subtree child's
remaining_ranges array is the same as the first range of that child's
nearest path-wise ancestor, then the subtree child *will not* be described
to the reporter.
DEPTH, NOTIFY_B, and MERGE_B are cascaded from do_directory_merge(), see
that function for more info.
MERGE_B->ra_session1 and MERGE_B->ra_session2 are RA sessions open to any
URL in the repository of SOURCE; they may be temporarily reparented within
this function.
If SOURCE->ancestral is set, then SOURCE->loc1 must be a
historical ancestor of SOURCE->loc2, or vice-versa (see
`MERGEINFO MERGE SOURCE NORMALIZATION' for more requirements around
SOURCE in this case).
*/
static svn_error_t *
drive_merge_report_editor(const char *target_abspath,
const merge_source_t *source,
const apr_array_header_t *children_with_mergeinfo,
const svn_diff_tree_processor_t *processor,
svn_depth_t depth,
merge_cmd_baton_t *merge_b,
apr_pool_t *scratch_pool)
{
const svn_ra_reporter3_t *reporter;
const svn_delta_editor_t *diff_editor;
void *diff_edit_baton;
void *report_baton;
svn_revnum_t target_start;
svn_boolean_t honor_mergeinfo = HONOR_MERGEINFO(merge_b);
const char *old_sess1_url, *old_sess2_url;
svn_boolean_t is_rollback = source->loc1->rev > source->loc2->rev;
/* Start with a safe default starting revision for the editor and the
merge target. */
target_start = source->loc1->rev;
/* If we are honoring mergeinfo the starting revision for the merge target
might not be SOURCE->rev1, in fact the merge target might not need *any*
part of SOURCE merged -- Instead some subtree of the target
needs SOURCE -- So get the right starting revision for the
target. */
if (honor_mergeinfo)
{
svn_client__merge_path_t *child;
/* CHILDREN_WITH_MERGEINFO must always exist if we are honoring
mergeinfo and must have at least one element (describing the
merge target). */
SVN_ERR_ASSERT(children_with_mergeinfo);
SVN_ERR_ASSERT(children_with_mergeinfo->nelts);
/* Get the merge target's svn_client__merge_path_t, which is always
the first in the array due to depth first sorting requirement,
see 'THE CHILDREN_WITH_MERGEINFO ARRAY'. */
child = APR_ARRAY_IDX(children_with_mergeinfo, 0,
svn_client__merge_path_t *);
SVN_ERR_ASSERT(child);
if (child->remaining_ranges->nelts == 0)
{
/* The merge target doesn't need anything merged. */
target_start = source->loc2->rev;
}
else
{
/* The merge target has remaining revisions to merge. These
ranges may fully or partially overlap the range described
by SOURCE->rev1:rev2 or may not intersect that range at
all. */
svn_merge_range_t *range =
APR_ARRAY_IDX(child->remaining_ranges, 0,
svn_merge_range_t *);
if ((!is_rollback && range->start > source->loc2->rev)
|| (is_rollback && range->start < source->loc2->rev))
{
/* Merge target's first remaining range doesn't intersect. */
target_start = source->loc2->rev;
}
else
{
/* Merge target's first remaining range partially or
fully overlaps. */
target_start = range->start;
}
}
}
SVN_ERR(svn_client__ensure_ra_session_url(&old_sess1_url,
merge_b->ra_session1,
source->loc1->url, scratch_pool));
/* Temporarily point our second RA session to SOURCE->loc1->url, too. We use
this to request individual file contents. */
SVN_ERR(svn_client__ensure_ra_session_url(&old_sess2_url,
merge_b->ra_session2,
source->loc1->url, scratch_pool));
/* Get the diff editor and a reporter with which to, ultimately,
drive it. */
SVN_ERR(svn_client__get_diff_editor2(&diff_editor, &diff_edit_baton,
merge_b->ra_session2,
depth,
source->loc1->rev,
TRUE /* text_deltas */,
processor,
merge_b->ctx->cancel_func,
merge_b->ctx->cancel_baton,
scratch_pool));
SVN_ERR(svn_ra_do_diff3(merge_b->ra_session1,
&reporter, &report_baton, source->loc2->rev,
"", depth, merge_b->diff_ignore_ancestry,
TRUE, /* text_deltas */
source->loc2->url, diff_editor, diff_edit_baton,
scratch_pool));
/* Drive the reporter. */
SVN_ERR(reporter->set_path(report_baton, "", target_start, depth,
FALSE, NULL, scratch_pool));
if (honor_mergeinfo && children_with_mergeinfo)
{
/* Describe children with mergeinfo overlapping this merge
operation such that no repeated diff is retrieved for them from
the repository. */
int i;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
/* Start with CHILDREN_WITH_MERGEINFO[1], CHILDREN_WITH_MERGEINFO[0]
is always the merge target (TARGET_ABSPATH). */
for (i = 1; i < children_with_mergeinfo->nelts; i++)
{
svn_merge_range_t *range;
const char *child_repos_path;
const svn_client__merge_path_t *parent;
const svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i,
svn_client__merge_path_t *);
SVN_ERR_ASSERT(child);
if (child->absent)
continue;
svn_pool_clear(iterpool);
/* Find this child's nearest wc ancestor with mergeinfo. */
parent = find_nearest_ancestor(children_with_mergeinfo,
FALSE, child->abspath);
/* If a subtree needs the same range applied as its nearest parent
with mergeinfo or neither the subtree nor this parent need
SOURCE->rev1:rev2 merged, then we don't need to describe the
subtree separately. In the latter case this could break the
editor if child->abspath didn't exist at SOURCE->rev2 and we
attempt to describe it via a reporter set_path call. */
if (child->remaining_ranges->nelts)
{
range = APR_ARRAY_IDX(child->remaining_ranges, 0,
svn_merge_range_t *);
if ((!is_rollback && range->start > source->loc2->rev)
|| (is_rollback && range->start < source->loc2->rev))
{
/* This child's first remaining range comes after the range
we are currently merging, so skip it. We expect to get
to it in a subsequent call to this function. */
continue;
}
else if (parent->remaining_ranges->nelts)
{
svn_merge_range_t *parent_range =
APR_ARRAY_IDX(parent->remaining_ranges, 0,
svn_merge_range_t *);
svn_merge_range_t *child_range =
APR_ARRAY_IDX(child->remaining_ranges, 0,
svn_merge_range_t *);
if (parent_range->start == child_range->start)
continue; /* Subtree needs same range as parent. */
}
}
else /* child->remaining_ranges->nelts == 0*/
{
/* If both the subtree and its parent need no ranges applied
consider that as the "same ranges" and don't describe
the subtree. */
if (parent->remaining_ranges->nelts == 0)
continue;
}
/* Ok, we really need to describe this subtree as it needs different
ranges applied than its nearest working copy parent. */
child_repos_path = svn_dirent_is_child(target_abspath,
child->abspath,
iterpool);
/* This loop is only processing subtrees, so CHILD->ABSPATH
better be a proper child of the merge target. */
SVN_ERR_ASSERT(child_repos_path);
if ((child->remaining_ranges->nelts == 0)
|| (is_rollback && (range->start < source->loc2->rev))
|| (!is_rollback && (range->start > source->loc2->rev)))
{
/* Nothing to merge to this child. We'll claim we have
it up to date so the server doesn't send us
anything. */
SVN_ERR(reporter->set_path(report_baton, child_repos_path,
source->loc2->rev, depth, FALSE,
NULL, iterpool));
}
else
{
SVN_ERR(reporter->set_path(report_baton, child_repos_path,
range->start, depth, FALSE,
NULL, iterpool));
}
}
svn_pool_destroy(iterpool);
}
SVN_ERR(reporter->finish_report(report_baton, scratch_pool));
/* Point the merge baton's RA sessions back where they were. */
SVN_ERR(svn_ra_reparent(merge_b->ra_session1, old_sess1_url, scratch_pool));
SVN_ERR(svn_ra_reparent(merge_b->ra_session2, old_sess2_url, scratch_pool));
return SVN_NO_ERROR;
}
/* Iterate over each svn_client__merge_path_t * element in
CHILDREN_WITH_MERGEINFO and, if START_REV is true, find the most inclusive
start revision among those element's first remaining_ranges element. If
START_REV is false, then look for the most inclusive end revision.
If IS_ROLLBACK is true the youngest start or end (as per START_REV)
revision is considered the "most inclusive" otherwise the oldest revision
is.
If none of CHILDREN_WITH_MERGEINFO's elements have any remaining ranges
return SVN_INVALID_REVNUM. */
static svn_revnum_t
get_most_inclusive_rev(const apr_array_header_t *children_with_mergeinfo,
svn_boolean_t is_rollback,
svn_boolean_t start_rev)
{
int i;
svn_revnum_t most_inclusive_rev = SVN_INVALID_REVNUM;
for (i = 0; i < children_with_mergeinfo->nelts; i++)
{
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
if ((! child) || child->absent)
continue;
if (child->remaining_ranges->nelts > 0)
{
svn_merge_range_t *range =
APR_ARRAY_IDX(child->remaining_ranges, 0, svn_merge_range_t *);
/* Are we looking for the most inclusive start or end rev? */
svn_revnum_t rev = start_rev ? range->start : range->end;
if ((most_inclusive_rev == SVN_INVALID_REVNUM)
|| (is_rollback && (rev > most_inclusive_rev))
|| ((! is_rollback) && (rev < most_inclusive_rev)))
most_inclusive_rev = rev;
}
}
return most_inclusive_rev;
}
/* If first item in each child of CHILDREN_WITH_MERGEINFO's
remaining_ranges is inclusive of END_REV, Slice the first range in
to two at END_REV. All the allocations are persistent and allocated
from POOL. */
static void
slice_remaining_ranges(apr_array_header_t *children_with_mergeinfo,
svn_boolean_t is_rollback, svn_revnum_t end_rev,
apr_pool_t *pool)
{
int i;
for (i = 0; i < children_with_mergeinfo->nelts; i++)
{
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i,
svn_client__merge_path_t *);
if (!child || child->absent)
continue;
if (child->remaining_ranges->nelts > 0)
{
svn_merge_range_t *range = APR_ARRAY_IDX(child->remaining_ranges, 0,
svn_merge_range_t *);
if ((is_rollback && (range->start > end_rev)
&& (range->end < end_rev))
|| (!is_rollback && (range->start < end_rev)
&& (range->end > end_rev)))
{
svn_merge_range_t *split_range1, *split_range2;
split_range1 = svn_merge_range_dup(range, pool);
split_range2 = svn_merge_range_dup(range, pool);
split_range1->end = end_rev;
split_range2->start = end_rev;
APR_ARRAY_IDX(child->remaining_ranges, 0,
svn_merge_range_t *) = split_range1;
svn_sort__array_insert(&split_range2, child->remaining_ranges, 1);
}
}
}
}
/* Helper for do_directory_merge().
For each child in CHILDREN_WITH_MERGEINFO remove the first remaining_ranges
svn_merge_range_t *element of the child if that range has an end revision
equal to REVISION.
If a range is removed from a child's remaining_ranges array, allocate the
new remaining_ranges array in POOL.
*/
static void
remove_first_range_from_remaining_ranges(svn_revnum_t revision,
apr_array_header_t
*children_with_mergeinfo,
apr_pool_t *pool)
{
int i;
for (i = 0; i < children_with_mergeinfo->nelts; i++)
{
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i,
svn_client__merge_path_t *);
if (!child || child->absent)
continue;
if (child->remaining_ranges->nelts > 0)
{
svn_merge_range_t *first_range =
APR_ARRAY_IDX(child->remaining_ranges, 0, svn_merge_range_t *);
if (first_range->end == revision)
{
svn_sort__array_delete(child->remaining_ranges, 0, 1);
}
}
}
}
/* Get a file's content and properties from the repository.
Set *FILENAME to the local path to a new temporary file holding its text,
and set *PROPS to a new hash of its properties.
RA_SESSION is a session open to the correct repository, which will be
temporarily reparented to the URL of the file itself. LOCATION is the
repository location of the file.
The resulting file and the return values live as long as RESULT_POOL, all
other allocations occur in SCRATCH_POOL.
*/
static svn_error_t *
single_file_merge_get_file(const char **filename,
apr_hash_t **props,
svn_ra_session_t *ra_session,
const svn_client__pathrev_t *location,
const char *wc_target,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_stream_t *stream;
const char *old_sess_url;
svn_error_t *err;
SVN_ERR(svn_stream_open_unique(&stream, filename, NULL,
svn_io_file_del_on_pool_cleanup,
result_pool, scratch_pool));
SVN_ERR(svn_client__ensure_ra_session_url(&old_sess_url, ra_session, location->url,
scratch_pool));
err = svn_ra_get_file(ra_session, "", location->rev,
stream, NULL, props, scratch_pool);
SVN_ERR(svn_error_compose_create(
err, svn_ra_reparent(ra_session, old_sess_url, scratch_pool)));
return svn_error_trace(svn_stream_close(stream));
}
/* Compare two svn_client__merge_path_t elements **A and **B, given the
addresses of pointers to them. Return an integer less than, equal to, or
greater than zero if A sorts before, the same as, or after B, respectively.
This is a helper for qsort() and bsearch() on an array of such elements. */
static int
compare_merge_path_t_as_paths(const void *a,
const void *b)
{
const svn_client__merge_path_t *child1
= *((const svn_client__merge_path_t * const *) a);
const svn_client__merge_path_t *child2
= *((const svn_client__merge_path_t * const *) b);
return svn_path_compare_paths(child1->abspath, child2->abspath);
}
/* Return a pointer to the element of CHILDREN_WITH_MERGEINFO whose path
* is PATH, or return NULL if there is no such element. */
static svn_client__merge_path_t *
get_child_with_mergeinfo(const apr_array_header_t *children_with_mergeinfo,
const char *abspath)
{
svn_client__merge_path_t merge_path;
svn_client__merge_path_t *key;
svn_client__merge_path_t **pchild;
merge_path.abspath = abspath;
key = &merge_path;
pchild = bsearch(&key, children_with_mergeinfo->elts,
children_with_mergeinfo->nelts,
children_with_mergeinfo->elt_size,
compare_merge_path_t_as_paths);
return pchild ? *pchild : NULL;
}
/* Insert a deep copy of INSERT_ELEMENT into the CHILDREN_WITH_MERGEINFO
array at its correct position. Allocate the new storage in POOL.
CHILDREN_WITH_MERGEINFO is a depth first sorted array of
(svn_client__merge_path_t *).
### Most callers don't need this to deep-copy the new element.
### It may be more efficient for some callers to insert a bunch of items
out of order and then sort afterwards. (One caller is doing a qsort
after calling this anyway.)
*/
static void
insert_child_to_merge(apr_array_header_t *children_with_mergeinfo,
const svn_client__merge_path_t *insert_element,
apr_pool_t *pool)
{
int insert_index;
const svn_client__merge_path_t *new_element;
/* Find where to insert the new element */
insert_index =
svn_sort__bsearch_lower_bound(&insert_element, children_with_mergeinfo,
compare_merge_path_t_as_paths);
new_element = svn_client__merge_path_dup(insert_element, pool);
svn_sort__array_insert(&new_element, children_with_mergeinfo, insert_index);
}
/* Helper for get_mergeinfo_paths().
CHILDREN_WITH_MERGEINFO, DEPTH, and POOL are
all cascaded from the arguments of the same name to get_mergeinfo_paths().
TARGET is the merge target.
*CHILD is the element in in CHILDREN_WITH_MERGEINFO that
get_mergeinfo_paths() is iterating over and *CURR_INDEX is index for
*CHILD.
If CHILD->ABSPATH is equal to MERGE_CMD_BATON->target->abspath do nothing.
Else if CHILD->ABSPATH is switched or absent then make sure its immediate
(as opposed to nearest) parent in CHILDREN_WITH_MERGEINFO is marked as
missing a child. If the immediate parent does not exist in
CHILDREN_WITH_MERGEINFO then create it (and increment *CURR_INDEX so that
caller doesn't process the inserted element). Also ensure that
CHILD->ABSPATH's siblings which are not already present in
CHILDREN_WITH_MERGEINFO are also added to the array, limited by DEPTH
(e.g. don't add directory siblings of a switched file).
Use POOL for temporary allocations only, any new CHILDREN_WITH_MERGEINFO
elements are allocated in POOL. */
static svn_error_t *
insert_parent_and_sibs_of_sw_absent_del_subtree(
apr_array_header_t *children_with_mergeinfo,
const merge_target_t *target,
int *curr_index,
svn_client__merge_path_t *child,
svn_depth_t depth,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
svn_client__merge_path_t *parent;
const char *parent_abspath;
apr_pool_t *iterpool;
const apr_array_header_t *children;
int i;
if (!(child->absent
|| (child->switched
&& strcmp(target->abspath,
child->abspath) != 0)))
return SVN_NO_ERROR;
parent_abspath = svn_dirent_dirname(child->abspath, pool);
parent = get_child_with_mergeinfo(children_with_mergeinfo, parent_abspath);
if (parent)
{
parent->missing_child = child->absent;
parent->switched_child = child->switched;
}
else
{
/* Create a new element to insert into CHILDREN_WITH_MERGEINFO. */
parent = svn_client__merge_path_create(parent_abspath, pool);
parent->missing_child = child->absent;
parent->switched_child = child->switched;
/* Insert PARENT into CHILDREN_WITH_MERGEINFO. */
insert_child_to_merge(children_with_mergeinfo, parent, pool);
/* Increment for loop index so we don't process the inserted element. */
(*curr_index)++;
} /*(parent == NULL) */
/* Add all of PARENT's non-missing children that are not already present.*/
SVN_ERR(svn_wc__node_get_children(&children, ctx->wc_ctx,
parent_abspath, FALSE, pool, pool));
iterpool = svn_pool_create(pool);
for (i = 0; i < children->nelts; i++)
{
const char *child_abspath = APR_ARRAY_IDX(children, i, const char *);
svn_client__merge_path_t *sibling_of_missing;
svn_pool_clear(iterpool);
/* Does this child already exist in CHILDREN_WITH_MERGEINFO? */
sibling_of_missing = get_child_with_mergeinfo(children_with_mergeinfo,
child_abspath);
/* Create the missing child and insert it into CHILDREN_WITH_MERGEINFO.*/
if (!sibling_of_missing)
{
/* Don't add directory children if DEPTH is svn_depth_files. */
if (depth == svn_depth_files)
{
svn_node_kind_t child_kind;
SVN_ERR(svn_wc_read_kind2(&child_kind,
ctx->wc_ctx, child_abspath,
FALSE, FALSE, iterpool));
if (child_kind != svn_node_file)
continue;
}
sibling_of_missing = svn_client__merge_path_create(child_abspath,
pool);
insert_child_to_merge(children_with_mergeinfo, sibling_of_missing,
pool);
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* pre_merge_status_cb's baton */
struct pre_merge_status_baton_t
{
svn_wc_context_t *wc_ctx;
/* const char *absolute_wc_path to svn_depth_t * mapping for depths
of empty, immediates, and files. */
apr_hash_t *shallow_subtrees;
/* const char *absolute_wc_path to the same, for all paths missing
from the working copy. */
apr_hash_t *missing_subtrees;
/* const char *absolute_wc_path const char * repos relative path, describing
the root of each switched subtree in the working copy and the repository
relative path it is switched to. */
apr_hash_t *switched_subtrees;
/* A pool to allocate additions to the above hashes in. */
apr_pool_t *pool;
};
/* A svn_wc_status_func4_t callback used by get_mergeinfo_paths to gather
all switched, depth filtered and missing subtrees under a merge target.
Note that this doesn't see server and user excluded trees. */
static svn_error_t *
pre_merge_status_cb(void *baton,
const char *local_abspath,
const svn_wc_status3_t *status,
apr_pool_t *scratch_pool)
{
struct pre_merge_status_baton_t *pmsb = baton;
if (status->switched && !status->file_external)
{
store_path(pmsb->switched_subtrees, local_abspath);
}
if (status->depth == svn_depth_empty
|| status->depth == svn_depth_files)
{
const char *dup_abspath;
svn_depth_t *depth = apr_pmemdup(pmsb->pool, &status->depth,
sizeof *depth);
dup_abspath = apr_pstrdup(pmsb->pool, local_abspath);
svn_hash_sets(pmsb->shallow_subtrees, dup_abspath, depth);
}
if (status->node_status == svn_wc_status_missing)
{
svn_boolean_t new_missing_root = TRUE;
apr_hash_index_t *hi;
for (hi = apr_hash_first(scratch_pool, pmsb->missing_subtrees);
hi;
hi = apr_hash_next(hi))
{
const char *missing_root_path = svn__apr_hash_index_key(hi);
if (svn_dirent_is_ancestor(missing_root_path,
local_abspath))
{
new_missing_root = FALSE;
break;
}
}
if (new_missing_root)
store_path(pmsb->missing_subtrees, local_abspath);
}
return SVN_NO_ERROR;
}
/* Find all the subtrees in the working copy tree rooted at TARGET_ABSPATH
* that have explicit mergeinfo.
* Set *SUBTREES_WITH_MERGEINFO to a hash mapping (const char *) absolute
* WC path to (svn_mergeinfo_t *) mergeinfo.
*
* ### Is this function equivalent to:
*
* svn_client__get_wc_mergeinfo_catalog(
* subtrees_with_mergeinfo, inherited=NULL, include_descendants=TRUE,
* svn_mergeinfo_explicit, target_abspath, limit_path=NULL,
* walked_path=NULL, ignore_invalid_mergeinfo=FALSE, ...)
*
* except for the catalog keys being abspaths instead of repo-relpaths?
*/
static svn_error_t *
get_wc_explicit_mergeinfo_catalog(apr_hash_t **subtrees_with_mergeinfo,
const char *target_abspath,
svn_depth_t depth,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_opt_revision_t working_revision = { svn_opt_revision_working, { 0 } };
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
apr_hash_index_t *hi;
apr_hash_t *externals;
SVN_ERR(svn_client_propget5(subtrees_with_mergeinfo, NULL,
SVN_PROP_MERGEINFO, target_abspath,
&working_revision, &working_revision, NULL,
depth, NULL, ctx, result_pool, scratch_pool));
SVN_ERR(svn_wc__externals_defined_below(&externals, ctx->wc_ctx,
target_abspath, scratch_pool,
scratch_pool));
/* Convert property values to svn_mergeinfo_t. */
for (hi = apr_hash_first(scratch_pool, *subtrees_with_mergeinfo);
hi;
hi = apr_hash_next(hi))
{
const char *wc_path = svn__apr_hash_index_key(hi);
svn_string_t *mergeinfo_string = svn__apr_hash_index_val(hi);
svn_mergeinfo_t mergeinfo;
svn_error_t *err;
/* svn_client_propget5 picks up file externals with
mergeinfo, but we don't want those. */
if (svn_hash_gets(externals, wc_path))
{
svn_hash_sets(*subtrees_with_mergeinfo, wc_path, NULL);
continue;
}
svn_pool_clear(iterpool);
err = svn_mergeinfo_parse(&mergeinfo, mergeinfo_string->data,
result_pool);
if (err)
{
if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
{
err = svn_error_createf(
SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING, err,
_("Invalid mergeinfo detected on '%s', "
"merge tracking not possible"),
svn_dirent_local_style(wc_path, scratch_pool));
}
return svn_error_trace(err);
}
svn_hash_sets(*subtrees_with_mergeinfo, wc_path, mergeinfo);
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Helper for do_directory_merge() when performing merge-tracking aware
merges.
Walk of the working copy tree rooted at TARGET->abspath to
depth DEPTH. Create an svn_client__merge_path_t * for any path which meets
one or more of the following criteria:
1) Path has working svn:mergeinfo.
2) Path is switched.
3) Path is a subtree of the merge target (i.e. is not equal to
TARGET->abspath) and has no mergeinfo of its own but
its immediate parent has mergeinfo with non-inheritable ranges. If
this isn't a dry-run and the merge is between differences in the same
repository, then this function will set working mergeinfo on the path
equal to the mergeinfo inheritable from its parent.
4) Path has an immediate child (or children) missing from the WC because
the child is switched or absent from the WC, or due to a sparse
checkout.
5) Path has a sibling (or siblings) missing from the WC because the
sibling is switched, absent, scheduled for deletion, or missing due to
a sparse checkout.
6) Path is absent from disk due to an authz restriction.
7) Path is equal to TARGET->abspath.
8) Path is an immediate *directory* child of
TARGET->abspath and DEPTH is svn_depth_immediates.
9) Path is an immediate *file* child of TARGET->abspath
and DEPTH is svn_depth_files.
10) Path is at a depth of 'empty' or 'files'.
11) Path is missing from disk (e.g. due to an OS-level deletion).
If subtrees within the requested DEPTH are unexpectedly missing disk,
then raise SVN_ERR_CLIENT_NOT_READY_TO_MERGE.
Store the svn_client__merge_path_t *'s in *CHILDREN_WITH_MERGEINFO in
depth-first order based on the svn_client__merge_path_t *s path member as
sorted by svn_path_compare_paths(). Set the remaining_ranges field of each
element to NULL.
Note: Since the walk is rooted at TARGET->abspath, the
latter is guaranteed to be in *CHILDREN_WITH_MERGEINFO and due to the
depth-first ordering it is guaranteed to be the first element in
*CHILDREN_WITH_MERGEINFO.
MERGE_CMD_BATON is cascaded from the argument of the same name in
do_directory_merge().
*/
static svn_error_t *
get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo,
const merge_target_t *target,
svn_depth_t depth,
svn_boolean_t dry_run,
svn_boolean_t same_repos,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
int i;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
apr_hash_t *subtrees_with_mergeinfo;
apr_hash_t *excluded_subtrees;
apr_hash_t *switched_subtrees;
apr_hash_t *shallow_subtrees;
apr_hash_t *missing_subtrees;
struct pre_merge_status_baton_t pre_merge_status_baton;
/* Case 1: Subtrees with explicit mergeinfo. */
SVN_ERR(get_wc_explicit_mergeinfo_catalog(&subtrees_with_mergeinfo,
target->abspath,
depth, ctx,
result_pool, scratch_pool));
if (subtrees_with_mergeinfo)
{
apr_hash_index_t *hi;
for (hi = apr_hash_first(scratch_pool, subtrees_with_mergeinfo);
hi;
hi = apr_hash_next(hi))
{
const char *wc_path = svn__apr_hash_index_key(hi);
svn_mergeinfo_t mergeinfo = svn__apr_hash_index_val(hi);
svn_client__merge_path_t *mergeinfo_child =
svn_client__merge_path_create(wc_path, result_pool);
svn_pool_clear(iterpool);
/* Stash this child's pre-existing mergeinfo. */
mergeinfo_child->pre_merge_mergeinfo = mergeinfo;
/* Note if this child has non-inheritable mergeinfo */
mergeinfo_child->has_noninheritable
= svn_mergeinfo__is_noninheritable(
mergeinfo_child->pre_merge_mergeinfo, iterpool);
/* Append it. We'll sort below. */
APR_ARRAY_PUSH(children_with_mergeinfo, svn_client__merge_path_t *)
= svn_client__merge_path_dup(mergeinfo_child, result_pool);
}
/* Sort CHILDREN_WITH_MERGEINFO by each child's path (i.e. as per
compare_merge_path_t_as_paths). Any subsequent insertions of new
children with insert_child_to_merge() require this ordering. */
qsort(children_with_mergeinfo->elts,
children_with_mergeinfo->nelts,
children_with_mergeinfo->elt_size,
compare_merge_path_t_as_paths);
}
/* Case 2: Switched subtrees
Case 10: Paths at depths of 'empty' or 'files'
Case 11: Paths missing from disk */
pre_merge_status_baton.wc_ctx = ctx->wc_ctx;
switched_subtrees = apr_hash_make(scratch_pool);
pre_merge_status_baton.switched_subtrees = switched_subtrees;
shallow_subtrees = apr_hash_make(scratch_pool);
pre_merge_status_baton.shallow_subtrees = shallow_subtrees;
missing_subtrees = apr_hash_make(scratch_pool);
pre_merge_status_baton.missing_subtrees = missing_subtrees;
pre_merge_status_baton.pool = scratch_pool;
SVN_ERR(svn_wc_walk_status(ctx->wc_ctx,
target->abspath,
depth,
TRUE /* get_all */,
FALSE /* no_ignore */,
TRUE /* ignore_text_mods */,
NULL /* ingore_patterns */,
pre_merge_status_cb, &pre_merge_status_baton,
ctx->cancel_func, ctx->cancel_baton,
scratch_pool));
/* Issue #2915: Raise an error describing the roots of any missing
subtrees, i.e. those that the WC thinks are on disk but have been
removed outside of Subversion. */
if (apr_hash_count(missing_subtrees))
{
apr_hash_index_t *hi;
svn_stringbuf_t *missing_subtree_err_buf =
svn_stringbuf_create(_("Merge tracking not allowed with missing "
"subtrees; try restoring these items "
"first:\n"), scratch_pool);
for (hi = apr_hash_first(scratch_pool, missing_subtrees);
hi;
hi = apr_hash_next(hi))
{
svn_pool_clear(iterpool);
svn_stringbuf_appendcstr(missing_subtree_err_buf,
svn_dirent_local_style(
svn__apr_hash_index_key(hi), iterpool));
svn_stringbuf_appendcstr(missing_subtree_err_buf, "\n");
}
return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE,
NULL, missing_subtree_err_buf->data);
}
if (apr_hash_count(switched_subtrees))
{
apr_hash_index_t *hi;
for (hi = apr_hash_first(scratch_pool, switched_subtrees);
hi;
hi = apr_hash_next(hi))
{
const char *wc_path = svn__apr_hash_index_key(hi);
svn_client__merge_path_t *child = get_child_with_mergeinfo(
children_with_mergeinfo, wc_path);
if (child)
{
child->switched = TRUE;
}
else
{
svn_client__merge_path_t *switched_child =
svn_client__merge_path_create(wc_path, result_pool);
switched_child->switched = TRUE;
insert_child_to_merge(children_with_mergeinfo, switched_child,
result_pool);
}
}
}
if (apr_hash_count(shallow_subtrees))
{
apr_hash_index_t *hi;
for (hi = apr_hash_first(scratch_pool, shallow_subtrees);
hi;
hi = apr_hash_next(hi))
{
svn_boolean_t new_shallow_child = FALSE;
const char *wc_path = svn__apr_hash_index_key(hi);
svn_depth_t *child_depth = svn__apr_hash_index_val(hi);
svn_client__merge_path_t *shallow_child = get_child_with_mergeinfo(
children_with_mergeinfo, wc_path);
if (shallow_child)
{
if (*child_depth == svn_depth_empty
|| *child_depth == svn_depth_files)
shallow_child->missing_child = TRUE;
}
else
{
shallow_child = svn_client__merge_path_create(wc_path,
result_pool);
new_shallow_child = TRUE;
if (*child_depth == svn_depth_empty
|| *child_depth == svn_depth_files)
shallow_child->missing_child = TRUE;
}
/* A little trickery: If PATH doesn't have any mergeinfo or has
only inheritable mergeinfo, we still describe it as having
non-inheritable mergeinfo if it is missing a child due to
a shallow depth. Why? Because the mergeinfo we'll add to PATH
to describe the merge must be non-inheritable, so PATH's missing
children don't inherit it. Marking these PATHs as non-
inheritable allows the logic for case 3 to properly account
for PATH's children. */
if (!shallow_child->has_noninheritable
&& (*child_depth == svn_depth_empty
|| *child_depth == svn_depth_files))
{
shallow_child->has_noninheritable = TRUE;
}
if (new_shallow_child)
insert_child_to_merge(children_with_mergeinfo, shallow_child,
result_pool);
}
}
/* Case 6: Paths absent from disk due to server or user exclusion. */
SVN_ERR(svn_wc__get_excluded_subtrees(&excluded_subtrees,
ctx->wc_ctx, target->abspath,
result_pool, scratch_pool));
if (excluded_subtrees)
{
apr_hash_index_t *hi;
for (hi = apr_hash_first(scratch_pool, excluded_subtrees);
hi;
hi = apr_hash_next(hi))
{
const char *wc_path = svn__apr_hash_index_key(hi);
svn_client__merge_path_t *child = get_child_with_mergeinfo(
children_with_mergeinfo, wc_path);
if (child)
{
child->absent = TRUE;
}
else
{
svn_client__merge_path_t *absent_child =
svn_client__merge_path_create(wc_path, result_pool);
absent_child->absent = TRUE;
insert_child_to_merge(children_with_mergeinfo, absent_child,
result_pool);
}
}
}
/* Case 7: The merge target MERGE_CMD_BATON->target->abspath is always
present. */
if (!get_child_with_mergeinfo(children_with_mergeinfo,
target->abspath))
{
svn_client__merge_path_t *target_child =
svn_client__merge_path_create(target->abspath,
result_pool);
insert_child_to_merge(children_with_mergeinfo, target_child,
result_pool);
}
/* Case 8: Path is an immediate *directory* child of
MERGE_CMD_BATON->target->abspath and DEPTH is svn_depth_immediates.
Case 9: Path is an immediate *file* child of
MERGE_CMD_BATON->target->abspath and DEPTH is svn_depth_files. */
if (depth == svn_depth_immediates || depth == svn_depth_files)
{
int j;
const apr_array_header_t *immediate_children;
SVN_ERR(svn_wc__node_get_children_of_working_node(
&immediate_children, ctx->wc_ctx,
target->abspath, FALSE, scratch_pool, scratch_pool));
for (j = 0; j < immediate_children->nelts; j++)
{
const char *immediate_child_abspath =
APR_ARRAY_IDX(immediate_children, j, const char *);
svn_node_kind_t immediate_child_kind;
svn_pool_clear(iterpool);
SVN_ERR(svn_wc_read_kind2(&immediate_child_kind,
ctx->wc_ctx, immediate_child_abspath,
FALSE, FALSE, iterpool));
if ((immediate_child_kind == svn_node_dir
&& depth == svn_depth_immediates)
|| (immediate_child_kind == svn_node_file
&& depth == svn_depth_files))
{
if (!get_child_with_mergeinfo(children_with_mergeinfo,
immediate_child_abspath))
{
svn_client__merge_path_t *immediate_child =
svn_client__merge_path_create(immediate_child_abspath,
result_pool);
if (immediate_child_kind == svn_node_dir
&& depth == svn_depth_immediates)
immediate_child->immediate_child_dir = TRUE;
insert_child_to_merge(children_with_mergeinfo,
immediate_child, result_pool);
}
}
}
}
/* If DEPTH isn't empty then cover cases 3), 4), and 5), possibly adding
elements to CHILDREN_WITH_MERGEINFO. */
if (depth <= svn_depth_empty)
return SVN_NO_ERROR;
for (i = 0; i < children_with_mergeinfo->nelts; i++)
{
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i,
svn_client__merge_path_t *);
svn_pool_clear(iterpool);
/* Case 3) Where merging to a path with a switched child the path
gets non-inheritable mergeinfo for the merge range performed and
the child gets its own set of mergeinfo. If the switched child
later "returns", e.g. a switched path is unswitched, the child
may not have any explicit mergeinfo. If the initial merge is
repeated we don't want to repeat the merge for the path, but we
do want to repeat it for the previously switched child. To
ensure this we check if all of CHILD's non-missing children have
explicit mergeinfo (they should already be present in
CHILDREN_WITH_MERGEINFO if they do). If not,
add the children without mergeinfo to CHILDREN_WITH_MERGEINFO so
do_directory_merge() will merge them independently.
But that's not enough! Since do_directory_merge() performs
the merges on the paths in CHILDREN_WITH_MERGEINFO in a depth
first manner it will merge the previously switched path's parent
first. As part of this merge it will update the parent's
previously non-inheritable mergeinfo and make it inheritable
(since it notices the path has no missing children), then when
do_directory_merge() finally merges the previously missing
child it needs to get mergeinfo from the child's nearest
ancestor, but since do_directory_merge() already tweaked that
mergeinfo, removing the non-inheritable flag, it appears that the
child already has been merged to. To prevent this we set
override mergeinfo on the child now, before any merging is done,
so it has explicit mergeinfo that reflects only CHILD's
inheritable mergeinfo. */
/* If depth is immediates or files then don't add new children if
CHILD is a subtree of the merge target; those children are below
the operational depth of the merge. */
if (child->has_noninheritable
&& (i == 0 || depth == svn_depth_infinity))
{
const apr_array_header_t *children;
int j;
SVN_ERR(svn_wc__node_get_children(&children,
ctx->wc_ctx,
child->abspath, FALSE,
iterpool, iterpool));
for (j = 0; j < children->nelts; j++)
{
svn_client__merge_path_t *child_of_noninheritable;
const char *child_abspath = APR_ARRAY_IDX(children, j,
const char*);
/* Does this child already exist in CHILDREN_WITH_MERGEINFO?
If not, create it and insert it into
CHILDREN_WITH_MERGEINFO and set override mergeinfo on
it. */
child_of_noninheritable =
get_child_with_mergeinfo(children_with_mergeinfo,
child_abspath);
if (!child_of_noninheritable)
{
/* Don't add directory children if DEPTH
is svn_depth_files. */
if (depth == svn_depth_files)
{
svn_node_kind_t child_kind;
SVN_ERR(svn_wc_read_kind2(&child_kind,
ctx->wc_ctx, child_abspath,
FALSE, FALSE, iterpool));
if (child_kind != svn_node_file)
continue;
}
/* else DEPTH is infinity or immediates so we want both
directory and file children. */
child_of_noninheritable =
svn_client__merge_path_create(child_abspath, result_pool);
child_of_noninheritable->child_of_noninheritable = TRUE;
insert_child_to_merge(children_with_mergeinfo,
child_of_noninheritable,
result_pool);
if (!dry_run && same_repos)
{
svn_mergeinfo_t mergeinfo;
SVN_ERR(svn_client__get_wc_mergeinfo(
&mergeinfo, NULL,
svn_mergeinfo_nearest_ancestor,
child_of_noninheritable->abspath,
target->abspath, NULL, FALSE,
ctx, iterpool, iterpool));
SVN_ERR(svn_client__record_wc_mergeinfo(
child_of_noninheritable->abspath, mergeinfo,
FALSE, ctx, iterpool));
}
}
}
}
/* Case 4 and 5 are handled by the following function. */
SVN_ERR(insert_parent_and_sibs_of_sw_absent_del_subtree(
children_with_mergeinfo, target, &i, child,
depth, ctx, result_pool));
} /* i < children_with_mergeinfo->nelts */
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Implements the svn_log_entry_receiver_t interface.
*
* BATON is an 'apr_array_header_t *' array of 'svn_revnum_t'.
* Push a copy of LOG_ENTRY->revision onto BATON. Thus, a
* series of invocations of this callback accumulates the
* corresponding set of revisions into BATON.
*/
static svn_error_t *
log_changed_revs(void *baton,
svn_log_entry_t *log_entry,
apr_pool_t *pool)
{
apr_array_header_t *revs = baton;
APR_ARRAY_PUSH(revs, svn_revnum_t) = log_entry->revision;
return SVN_NO_ERROR;
}
/* Set *MIN_REV_P to the oldest and *MAX_REV_P to the youngest start or end
* revision occurring in RANGELIST, or to SVN_INVALID_REVNUM if RANGELIST
* is empty. */
static void
merge_range_find_extremes(svn_revnum_t *min_rev_p,
svn_revnum_t *max_rev_p,
const svn_rangelist_t *rangelist)
{
int i;
*min_rev_p = SVN_INVALID_REVNUM;
*max_rev_p = SVN_INVALID_REVNUM;
for (i = 0; i < rangelist->nelts; i++)
{
svn_merge_range_t *range
= APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
svn_revnum_t range_min = MIN(range->start, range->end);
svn_revnum_t range_max = MAX(range->start, range->end);
if ((! SVN_IS_VALID_REVNUM(*min_rev_p)) || (range_min < *min_rev_p))
*min_rev_p = range_min;
if ((! SVN_IS_VALID_REVNUM(*max_rev_p)) || (range_max > *max_rev_p))
*max_rev_p = range_max;
}
}
/* Wrapper around svn_ra_get_log2(). Invoke RECEIVER with RECEIVER_BATON
* on each commit from YOUNGEST_REV to OLDEST_REV in which TARGET_RELPATH
* changed. TARGET_RELPATH is relative to RA_SESSION's URL.
* Important: Revision properties are not retrieved by this function for
* performance reasons.
*/
static svn_error_t *
get_log(svn_ra_session_t *ra_session,
const char *target_relpath,
svn_revnum_t youngest_rev,
svn_revnum_t oldest_rev,
svn_boolean_t discover_changed_paths,
svn_log_entry_receiver_t receiver,
void *receiver_baton,
apr_pool_t *pool)
{
apr_array_header_t *log_targets;
apr_array_header_t *revprops;
log_targets = apr_array_make(pool, 1, sizeof(const char *));
APR_ARRAY_PUSH(log_targets, const char *) = target_relpath;
revprops = apr_array_make(pool, 0, sizeof(const char *));
SVN_ERR(svn_ra_get_log2(ra_session, log_targets, youngest_rev,
oldest_rev, 0 /* limit */, discover_changed_paths,
FALSE /* strict_node_history */,
FALSE /* include_merged_revisions */,
revprops, receiver, receiver_baton, pool));
return SVN_NO_ERROR;
}
/* Set *OPERATIVE_RANGES_P to an array of svn_merge_range_t * merge
range objects copied wholesale from RANGES which have the property
that in some revision within that range the object identified by
RA_SESSION was modified (if by "modified" we mean "'svn log' would
return that revision). *OPERATIVE_RANGES_P is allocated from the
same pool as RANGES, and the ranges within it are shared with
RANGES, too.
*OPERATIVE_RANGES_P may be the same as RANGES (that is, the output
parameter is set only after the input is no longer used).
Use POOL for temporary allocations. */
static svn_error_t *
remove_noop_merge_ranges(svn_rangelist_t **operative_ranges_p,
svn_ra_session_t *ra_session,
const svn_rangelist_t *ranges,
apr_pool_t *pool)
{
int i;
svn_revnum_t oldest_rev, youngest_rev;
apr_array_header_t *changed_revs =
apr_array_make(pool, ranges->nelts, sizeof(svn_revnum_t));
svn_rangelist_t *operative_ranges =
apr_array_make(ranges->pool, ranges->nelts, ranges->elt_size);
/* Find the revision extremes of the RANGES we have. */
merge_range_find_extremes(&oldest_rev, &youngest_rev, ranges);
if (SVN_IS_VALID_REVNUM(oldest_rev))
oldest_rev++; /* make it inclusive */
/* Get logs across those ranges, recording which revisions hold
changes to our object's history. */
SVN_ERR(get_log(ra_session, "", youngest_rev, oldest_rev, FALSE,
log_changed_revs, changed_revs, pool));
/* Are there *any* changes? */
if (changed_revs->nelts)
{
/* Our list of changed revisions should be in youngest-to-oldest
order. */
svn_revnum_t youngest_changed_rev
= APR_ARRAY_IDX(changed_revs, 0, svn_revnum_t);
svn_revnum_t oldest_changed_rev
= APR_ARRAY_IDX(changed_revs, changed_revs->nelts - 1, svn_revnum_t);
/* Now, copy from RANGES to *OPERATIVE_RANGES, filtering out ranges
that aren't operative (by virtue of not having any revisions
represented in the CHANGED_REVS array). */
for (i = 0; i < ranges->nelts; i++)
{
svn_merge_range_t *range = APR_ARRAY_IDX(ranges, i,
svn_merge_range_t *);
svn_revnum_t range_min = MIN(range->start, range->end) + 1;
svn_revnum_t range_max = MAX(range->start, range->end);
int j;
/* If the merge range is entirely outside the range of changed
revisions, we've no use for it. */
if ((range_min > youngest_changed_rev)
|| (range_max < oldest_changed_rev))
continue;
/* Walk through the changed_revs to see if any of them fall
inside our current range. */
for (j = 0; j < changed_revs->nelts; j++)
{
svn_revnum_t changed_rev
= APR_ARRAY_IDX(changed_revs, j, svn_revnum_t);
if ((changed_rev >= range_min) && (changed_rev <= range_max))
{
APR_ARRAY_PUSH(operative_ranges, svn_merge_range_t *) =
range;
break;
}
}
}
}
*operative_ranges_p = operative_ranges;
return SVN_NO_ERROR;
}
/*-----------------------------------------------------------------------*/
/*** Merge Source Normalization ***/
/* qsort-compatible sort routine, rating merge_source_t * objects to
be in descending (youngest-to-oldest) order based on their ->loc1->rev
component. */
static int
compare_merge_source_ts(const void *a,
const void *b)
{
svn_revnum_t a_rev = (*(const merge_source_t *const *)a)->loc1->rev;
svn_revnum_t b_rev = (*(const merge_source_t *const *)b)->loc1->rev;
if (a_rev == b_rev)
return 0;
return a_rev < b_rev ? 1 : -1;
}
/* Set *MERGE_SOURCE_TS_P to a list of merge sources generated by
slicing history location SEGMENTS with a given requested merge
RANGE. Use SOURCE_LOC for full source URL calculation.
Order the merge sources in *MERGE_SOURCE_TS_P from oldest to
youngest. */
static svn_error_t *
combine_range_with_segments(apr_array_header_t **merge_source_ts_p,
const svn_merge_range_t *range,
const apr_array_header_t *segments,
const svn_client__pathrev_t *source_loc,
apr_pool_t *pool)
{
apr_array_header_t *merge_source_ts =
apr_array_make(pool, 1, sizeof(merge_source_t *));
svn_revnum_t minrev = MIN(range->start, range->end) + 1;
svn_revnum_t maxrev = MAX(range->start, range->end);
svn_boolean_t subtractive = (range->start > range->end);
int i;
for (i = 0; i < segments->nelts; i++)
{
svn_location_segment_t *segment =
APR_ARRAY_IDX(segments, i, svn_location_segment_t *);
svn_client__pathrev_t *loc1, *loc2;
merge_source_t *merge_source;
const char *path1 = NULL;
svn_revnum_t rev1;
/* If this segment doesn't overlap our range at all, or
represents a gap, ignore it. */
if ((segment->range_end < minrev)
|| (segment->range_start > maxrev)
|| (! segment->path))
continue;
/* If our range spans a segment boundary, we have to point our
merge_source_t's path1 to the path of the immediately older
segment, else it points to the same location as its path2. */
rev1 = MAX(segment->range_start, minrev) - 1;
if (minrev <= segment->range_start)
{
if (i > 0)
{
path1 = (APR_ARRAY_IDX(segments, i - 1,
svn_location_segment_t *))->path;
}
/* If we've backed PATH1 up into a segment gap, let's back
it up further still to the segment before the gap. We'll
have to adjust rev1, too. */
if ((! path1) && (i > 1))
{
path1 = (APR_ARRAY_IDX(segments, i - 2,
svn_location_segment_t *))->path;
rev1 = (APR_ARRAY_IDX(segments, i - 2,
svn_location_segment_t *))->range_end;
}
}
else
{
path1 = apr_pstrdup(pool, segment->path);
}
/* If we don't have two valid paths, we won't know what to do
when merging. This could happen if someone requested a merge
where the source didn't exist in a particular revision or
something. The merge code would probably bomb out anyway, so
we'll just *not* create a merge source in this case. */
if (! (path1 && segment->path))
continue;
/* Build our merge source structure. */
loc1 = svn_client__pathrev_create_with_relpath(
source_loc->repos_root_url, source_loc->repos_uuid,
rev1, path1, pool);
loc2 = svn_client__pathrev_create_with_relpath(
source_loc->repos_root_url, source_loc->repos_uuid,
MIN(segment->range_end, maxrev), segment->path, pool);
/* If this is subtractive, reverse the whole calculation. */
if (subtractive)
merge_source = merge_source_create(loc2, loc1, TRUE /* ancestral */,
pool);
else
merge_source = merge_source_create(loc1, loc2, TRUE /* ancestral */,
pool);
APR_ARRAY_PUSH(merge_source_ts, merge_source_t *) = merge_source;
}
/* If this was a subtractive merge, and we created more than one
merge source, we need to reverse the sort ordering of our sources. */
if (subtractive && (merge_source_ts->nelts > 1))
qsort(merge_source_ts->elts, merge_source_ts->nelts,
merge_source_ts->elt_size, compare_merge_source_ts);
*merge_source_ts_p = merge_source_ts;
return SVN_NO_ERROR;
}
/* Similar to normalize_merge_sources() except the input MERGE_RANGE_TS is a
* rangelist.
*/
static svn_error_t *
normalize_merge_sources_internal(apr_array_header_t **merge_sources_p,
const svn_client__pathrev_t *source_loc,
const svn_rangelist_t *merge_range_ts,
svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_revnum_t source_peg_revnum = source_loc->rev;
svn_revnum_t oldest_requested, youngest_requested;
svn_revnum_t trim_revision = SVN_INVALID_REVNUM;
apr_array_header_t *segments;
int i;
/* Initialize our return variable. */
*merge_sources_p = apr_array_make(result_pool, 1, sizeof(merge_source_t *));
/* No ranges to merge? No problem. */
if (merge_range_ts->nelts == 0)
return SVN_NO_ERROR;
/* Find the extremes of the revisions across our set of ranges. */
merge_range_find_extremes(&oldest_requested, &youngest_requested,
merge_range_ts);
/* ### FIXME: Our underlying APIs can't yet handle the case where
the peg revision isn't the youngest of the three revisions. So
we'll just verify that the source in the peg revision is related
to the source in the youngest requested revision (which is
all the underlying APIs would do in this case right now anyway). */
if (source_peg_revnum < youngest_requested)
{
svn_client__pathrev_t *start_loc;
SVN_ERR(svn_client__repos_location(&start_loc,
ra_session, source_loc,
youngest_requested,
ctx, scratch_pool, scratch_pool));
source_peg_revnum = youngest_requested;
}
/* Fetch the locations for our merge range span. */
SVN_ERR(svn_client__repos_location_segments(&segments,
ra_session, source_loc->url,
source_peg_revnum,
youngest_requested,
oldest_requested,
ctx, result_pool));
/* See if we fetched enough history to do the job. "Surely we did,"
you say. "After all, we covered the entire requested merge
range." Yes, that's true, but if our first segment doesn't
extend back to the oldest request revision, we've got a special
case to deal with. Or if the first segment represents a gap,
that's another special case. */
trim_revision = SVN_INVALID_REVNUM;
if (segments->nelts)
{
svn_location_segment_t *first_segment =
APR_ARRAY_IDX(segments, 0, svn_location_segment_t *);
/* If the first segment doesn't start with the OLDEST_REQUESTED
revision, we'll need to pass a trim revision to our range
cruncher. */
if (first_segment->range_start != oldest_requested)
{
trim_revision = first_segment->range_start;
}
/* Else, if the first segment has no path (and therefore is a
gap), then we'll fetch the copy source revision from the
second segment (provided there is one, of course) and use it
to prepend an extra pathful segment to our list.
### We could avoid this bit entirely if we'd passed
### SVN_INVALID_REVNUM instead of OLDEST_REQUESTED to
### svn_client__repos_location_segments(), but that would
### really penalize clients hitting pre-1.5 repositories with
### the typical small merge range request (because of the
### lack of a node-origins cache in the repository). */
else if (! first_segment->path)
{
if (segments->nelts > 1)
{
svn_location_segment_t *second_segment =
APR_ARRAY_IDX(segments, 1, svn_location_segment_t *);
const char *segment_url;
const char *original_repos_relpath;
svn_revnum_t original_revision;
svn_opt_revision_t range_start_rev;
range_start_rev.kind = svn_opt_revision_number;
range_start_rev.value.number = second_segment->range_start;
segment_url = svn_path_url_add_component2(
source_loc->repos_root_url, second_segment->path,
scratch_pool);
SVN_ERR(svn_client__get_copy_source(&original_repos_relpath,
&original_revision,
segment_url,
&range_start_rev, ctx,
result_pool, scratch_pool));
/* Got copyfrom data? Fix up the first segment to cover
back to COPYFROM_REV + 1, and then prepend a new
segment covering just COPYFROM_REV. */
if (original_repos_relpath)
{
svn_location_segment_t *new_segment =
apr_pcalloc(result_pool, sizeof(*new_segment));
new_segment->path = original_repos_relpath;
new_segment->range_start = original_revision;
new_segment->range_end = original_revision;
svn_sort__array_insert(&new_segment, segments, 0);
}
}
}
}
/* For each range in our requested range set, try to determine the
path(s) associated with that range. */
for (i = 0; i < merge_range_ts->nelts; i++)
{
svn_merge_range_t *range =
APR_ARRAY_IDX(merge_range_ts, i, svn_merge_range_t *);
apr_array_header_t *merge_sources;
if (SVN_IS_VALID_REVNUM(trim_revision))
{
/* If the range predates the trim revision, discard it. */
if (MAX(range->start, range->end) < trim_revision)
continue;
/* If the range overlaps the trim revision, trim it. */
if (range->start < trim_revision)
range->start = trim_revision;
if (range->end < trim_revision)
range->end = trim_revision;
}
/* Copy the resulting merge sources into master list thereof. */
SVN_ERR(combine_range_with_segments(&merge_sources, range,
segments, source_loc,
result_pool));
apr_array_cat(*merge_sources_p, merge_sources);
}
return SVN_NO_ERROR;
}
/* Determine the normalized ranges to merge from a given line of history.
Calculate the result by intersecting the list of location segments at
which SOURCE_LOC existed along its line of history with the requested
revision ranges in RANGES_TO_MERGE. RANGES_TO_MERGE is an array of
(svn_opt_revision_range_t *) revision ranges. Use SOURCE_PATH_OR_URL to
resolve any WC-relative revision specifiers (such as 'base') in
RANGES_TO_MERGE.
Set *MERGE_SOURCES_P to an array of merge_source_t * objects, each
describing a normalized range of revisions to be merged from the line
history of SOURCE_LOC. Order the objects from oldest to youngest.
RA_SESSION is an RA session open to the repository of SOURCE_LOC; it may
be temporarily reparented within this function. Use RA_SESSION to find
the location segments along the line of history of SOURCE_LOC.
Allocate MERGE_SOURCES_P and its contents in RESULT_POOL.
See `MERGEINFO MERGE SOURCE NORMALIZATION' for more on the
background of this function.
*/
static svn_error_t *
normalize_merge_sources(apr_array_header_t **merge_sources_p,
const char *source_path_or_url,
const svn_client__pathrev_t *source_loc,
const apr_array_header_t *ranges_to_merge,
svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *source_abspath_or_url;
svn_revnum_t youngest_rev = SVN_INVALID_REVNUM;
svn_rangelist_t *merge_range_ts;
int i;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
if(!svn_path_is_url(source_path_or_url))
SVN_ERR(svn_dirent_get_absolute(&source_abspath_or_url, source_path_or_url,
scratch_pool));
else
source_abspath_or_url = source_path_or_url;
/* Create a list to hold svn_merge_range_t's. */
merge_range_ts = apr_array_make(scratch_pool, ranges_to_merge->nelts,
sizeof(svn_merge_range_t *));
for (i = 0; i < ranges_to_merge->nelts; i++)
{
svn_opt_revision_range_t *range
= APR_ARRAY_IDX(ranges_to_merge, i, svn_opt_revision_range_t *);
svn_merge_range_t mrange;
svn_pool_clear(iterpool);
/* Resolve revisions to real numbers, validating as we go. */
if ((range->start.kind == svn_opt_revision_unspecified)
|| (range->end.kind == svn_opt_revision_unspecified))
return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
_("Not all required revisions are specified"));
SVN_ERR(svn_client__get_revision_number(&mrange.start, &youngest_rev,
ctx->wc_ctx,
source_abspath_or_url,
ra_session, &range->start,
iterpool));
SVN_ERR(svn_client__get_revision_number(&mrange.end, &youngest_rev,
ctx->wc_ctx,
source_abspath_or_url,
ra_session, &range->end,
iterpool));
/* If this isn't a no-op range... */
if (mrange.start != mrange.end)
{
/* ...then add it to the list. */
mrange.inheritable = TRUE;
APR_ARRAY_PUSH(merge_range_ts, svn_merge_range_t *)
= svn_merge_range_dup(&mrange, scratch_pool);
}
}
SVN_ERR(normalize_merge_sources_internal(
merge_sources_p, source_loc,
merge_range_ts, ra_session, ctx, result_pool, scratch_pool));
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/*-----------------------------------------------------------------------*/
/*** Merge Workhorse Functions ***/
/* Helper for do_directory_merge() and do_file_merge() which filters out a
path's own natural history from the mergeinfo describing a merge.
Given the natural history IMPLICIT_MERGEINFO of some wc merge target path,
the repository-relative merge source path SOURCE_REL_PATH, and the
requested merge range REQUESTED_RANGE from SOURCE_REL_PATH, remove any
portion of REQUESTED_RANGE which is already described in
IMPLICIT_MERGEINFO. Store the result in *FILTERED_RANGELIST.
This function only filters natural history for mergeinfo that will be
*added* during a forward merge. Removing natural history from explicit
mergeinfo is harmless. If REQUESTED_RANGE describes a reverse merge,
then *FILTERED_RANGELIST is simply populated with one range described
by REQUESTED_RANGE. *FILTERED_RANGELIST is never NULL.
Allocate *FILTERED_RANGELIST in POOL. */
static svn_error_t *
filter_natural_history_from_mergeinfo(svn_rangelist_t **filtered_rangelist,
const char *source_rel_path,
svn_mergeinfo_t implicit_mergeinfo,
svn_merge_range_t *requested_range,
apr_pool_t *pool)
{
/* Make the REQUESTED_RANGE into a rangelist. */
svn_rangelist_t *requested_rangelist =
svn_rangelist__initialize(requested_range->start, requested_range->end,
requested_range->inheritable, pool);
*filtered_rangelist = NULL;
/* For forward merges: If the IMPLICIT_MERGEINFO already describes ranges
associated with SOURCE_REL_PATH then filter those ranges out. */
if (implicit_mergeinfo
&& (requested_range->start < requested_range->end))
{
svn_rangelist_t *implied_rangelist =
svn_hash_gets(implicit_mergeinfo, source_rel_path);
if (implied_rangelist)
SVN_ERR(svn_rangelist_remove(filtered_rangelist,
implied_rangelist,
requested_rangelist,
FALSE, pool));
}
/* If no filtering was performed the filtered rangelist is
simply the requested rangelist.*/
if (! (*filtered_rangelist))
*filtered_rangelist = requested_rangelist;
return SVN_NO_ERROR;
}
/* Return a merge source representing the sub-range from START_REV to
END_REV of SOURCE. SOURCE obeys the rules described in the
'MERGEINFO MERGE SOURCE NORMALIZATION' comment at the top of this file.
The younger of START_REV and END_REV is inclusive while the older is
exclusive.
Allocate the result structure in POOL but leave the URLs in it as shallow
copies of the URLs in SOURCE.
*/
static merge_source_t *
subrange_source(const merge_source_t *source,
svn_revnum_t start_rev,
svn_revnum_t end_rev,
apr_pool_t *pool)
{
svn_boolean_t is_rollback = (source->loc1->rev > source->loc2->rev);
svn_boolean_t same_urls = (strcmp(source->loc1->url, source->loc2->url) == 0);
svn_client__pathrev_t loc1 = *source->loc1;
svn_client__pathrev_t loc2 = *source->loc2;
/* For this function we require that the input source is 'ancestral'. */
SVN_ERR_ASSERT_NO_RETURN(source->ancestral);
SVN_ERR_ASSERT_NO_RETURN(start_rev != end_rev);
loc1.rev = start_rev;
loc2.rev = end_rev;
if (! same_urls)
{
if (is_rollback && (end_rev != source->loc2->rev))
{
loc2.url = source->loc1->url;
}
if ((! is_rollback) && (start_rev != source->loc1->rev))
{
loc1.url = source->loc2->url;
}
}
return merge_source_create(&loc1, &loc2, source->ancestral, pool);
}
/* The single-file, simplified version of do_directory_merge(), which see for
parameter descriptions.
Additional parameters:
If SOURCES_RELATED is set, the "left" and "right" sides of SOURCE are
historically related (ancestors, uncles, second
cousins thrice removed, etc...). (This is used to simulate the
history checks that the repository logic does in the directory case.)
If mergeinfo is being recorded to describe this merge, and RESULT_CATALOG
is not NULL, then don't record the new mergeinfo on the TARGET_ABSPATH,
but instead record it in RESULT_CATALOG, where the key is TARGET_ABSPATH
and the value is the new mergeinfo for that path. Allocate additions
to RESULT_CATALOG in pool which RESULT_CATALOG was created in.
CONFLICTED_RANGE is as documented for do_directory_merge().
Note: MERGE_B->RA_SESSION1 must be associated with SOURCE->loc1->url and
MERGE_B->RA_SESSION2 with SOURCE->loc2->url.
*/
static svn_error_t *
do_file_merge(svn_mergeinfo_catalog_t result_catalog,
single_range_conflict_report_t **conflict_report,
const merge_source_t *source,
const char *target_abspath,
const svn_diff_tree_processor_t *processor,
svn_boolean_t sources_related,
svn_boolean_t squelch_mergeinfo_notifications,
merge_cmd_baton_t *merge_b,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_rangelist_t *remaining_ranges;
svn_client_ctx_t *ctx = merge_b->ctx;
svn_merge_range_t range;
svn_mergeinfo_t target_mergeinfo;
svn_boolean_t inherited = FALSE;
svn_boolean_t is_rollback = (source->loc1->rev > source->loc2->rev);
const svn_client__pathrev_t *primary_src
= is_rollback ? source->loc1 : source->loc2;
svn_boolean_t honor_mergeinfo = HONOR_MERGEINFO(merge_b);
svn_client__merge_path_t *merge_target = NULL;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
*conflict_report = NULL;
/* Note that this is a single-file merge. */
range.start = source->loc1->rev;
range.end = source->loc2->rev;
range.inheritable = TRUE;
merge_target = svn_client__merge_path_create(target_abspath, scratch_pool);
if (honor_mergeinfo)
{
svn_error_t *err;
/* Fetch mergeinfo. */
err = get_full_mergeinfo(&target_mergeinfo,
&(merge_target->implicit_mergeinfo),
&inherited, svn_mergeinfo_inherited,
merge_b->ra_session1, target_abspath,
MAX(source->loc1->rev, source->loc2->rev),
MIN(source->loc1->rev, source->loc2->rev),
ctx, scratch_pool, iterpool);
if (err)
{
if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
{
err = svn_error_createf(
SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING, err,
_("Invalid mergeinfo detected on merge target '%s', "
"merge tracking not possible"),
svn_dirent_local_style(target_abspath, scratch_pool));
}
return svn_error_trace(err);
}
/* Calculate remaining merges unless this is a record only merge.
In that case the remaining range is the whole range described
by SOURCE->rev1:rev2. */
if (!merge_b->record_only)
{
/* ### Bug? calculate_remaining_ranges() needs 'source' to adhere
* to the requirements of 'MERGEINFO MERGE SOURCE NORMALIZATION'
* here, but it doesn't appear to be guaranteed so. */
SVN_ERR(calculate_remaining_ranges(NULL, merge_target,
source,
target_mergeinfo,
merge_b->implicit_src_gap, FALSE,
merge_b->ra_session1,
ctx, scratch_pool,
iterpool));
remaining_ranges = merge_target->remaining_ranges;
/* We are honoring mergeinfo and this is not a simple record only
merge which blindly records mergeinfo describing the merge of
SOURCE->LOC1->URL@SOURCE->LOC1->REV through
SOURCE->LOC2->URL@SOURCE->LOC2->REV. This means that the oldest
and youngest revisions merged (as determined above by
calculate_remaining_ranges) might differ from those described
in SOURCE. To keep the '--- Merging *' notifications consistent
with the '--- Recording mergeinfo *' notifications, we adjust
RANGE to account for such changes. */
if (remaining_ranges->nelts)
{
svn_merge_range_t *adj_start_range =
APR_ARRAY_IDX(remaining_ranges, 0, svn_merge_range_t *);
svn_merge_range_t *adj_end_range =
APR_ARRAY_IDX(remaining_ranges, remaining_ranges->nelts - 1,
svn_merge_range_t *);
range.start = adj_start_range->start;
range.end = adj_end_range->end;
}
}
}
/* The simple cases where our remaining range is SOURCE->rev1:rev2. */
if (!honor_mergeinfo || merge_b->record_only)
{
remaining_ranges = apr_array_make(scratch_pool, 1, sizeof(&range));
APR_ARRAY_PUSH(remaining_ranges, svn_merge_range_t *) = &range;
}
if (!merge_b->record_only)
{
svn_rangelist_t *ranges_to_merge = apr_array_copy(scratch_pool,
remaining_ranges);
const char *target_relpath = ""; /* relative to root of merge */
if (source->ancestral)
{
apr_array_header_t *child_with_mergeinfo;
svn_client__merge_path_t *target_info;
/* If we have ancestrally related sources and more than one
range to merge, eliminate no-op ranges before going through
the effort of downloading the many copies of the file
required to do these merges (two copies per range). */
if (remaining_ranges->nelts > 1)
{
const char *old_sess_url;
svn_error_t *err;
SVN_ERR(svn_client__ensure_ra_session_url(&old_sess_url,
merge_b->ra_session1,
primary_src->url,
iterpool));
err = remove_noop_merge_ranges(&ranges_to_merge,
merge_b->ra_session1,
remaining_ranges, scratch_pool);
SVN_ERR(svn_error_compose_create(
err, svn_ra_reparent(merge_b->ra_session1,
old_sess_url, iterpool)));
}
/* To support notify_merge_begin() initialize our
CHILD_WITH_MERGEINFO. See the comment
'THE CHILDREN_WITH_MERGEINFO ARRAY' at the start of this file. */
child_with_mergeinfo = apr_array_make(scratch_pool, 1,
sizeof(svn_client__merge_path_t *));
/* ### Create a fake copy of merge_target as we don't keep
remaining_ranges in sync (yet). */
target_info = apr_pcalloc(scratch_pool, sizeof(*target_info));
target_info->abspath = merge_target->abspath;
target_info->remaining_ranges = ranges_to_merge;
APR_ARRAY_PUSH(child_with_mergeinfo, svn_client__merge_path_t *)
= target_info;
/* And store in baton to allow using it from notify_merge_begin() */
merge_b->notify_begin.nodes_with_mergeinfo = child_with_mergeinfo;
}
while (ranges_to_merge->nelts > 0)
{
svn_merge_range_t *r = APR_ARRAY_IDX(ranges_to_merge, 0,
svn_merge_range_t *);
const merge_source_t *real_source;
const char *left_file, *right_file;
apr_hash_t *left_props, *right_props;
const svn_diff_source_t *left_source;
const svn_diff_source_t *right_source;
svn_pool_clear(iterpool);
/* Ensure any subsequent drives gets their own notification. */
merge_b->notify_begin.last_abspath = NULL;
/* While we currently don't allow it, in theory we could be
fetching two fulltexts from two different repositories here. */
if (source->ancestral)
real_source = subrange_source(source, r->start, r->end, iterpool);
else
real_source = source;
SVN_ERR(single_file_merge_get_file(&left_file, &left_props,
merge_b->ra_session1,
real_source->loc1,
target_abspath,
iterpool, iterpool));
SVN_ERR(single_file_merge_get_file(&right_file, &right_props,
merge_b->ra_session2,
real_source->loc2,
target_abspath,
iterpool, iterpool));
/* Calculate sources for the diff processor */
left_source = svn_diff__source_create(r->start, iterpool);
right_source = svn_diff__source_create(r->end, iterpool);
/* If the sources are related or we're ignoring ancestry in diffs,
do a text-n-props merge; otherwise, do a delete-n-add merge. */
if (! (merge_b->diff_ignore_ancestry || sources_related))
{
struct merge_dir_baton_t dir_baton;
void *file_baton;
svn_boolean_t skip;
/* Initialize minimal dir baton to allow calculating 'R'eplace
from 'D'elete + 'A'dd. */
memset(&dir_baton, 0, sizeof(dir_baton));
dir_baton.pool = iterpool;
dir_baton.tree_conflict_reason = CONFLICT_REASON_NONE;
dir_baton.tree_conflict_action = svn_wc_conflict_action_edit;
dir_baton.skip_reason = svn_wc_notify_state_unknown;
/* Delete... */
file_baton = NULL;
skip = FALSE;
SVN_ERR(processor->file_opened(&file_baton, &skip, target_relpath,
left_source,
NULL /* right_source */,
NULL /* copyfrom_source */,
&dir_baton,
processor,
iterpool, iterpool));
if (! skip)
SVN_ERR(processor->file_deleted(target_relpath,
left_source,
left_file,
left_props,
file_baton,
processor,
iterpool));
/* ...plus add... */
file_baton = NULL;
skip = FALSE;
SVN_ERR(processor->file_opened(&file_baton, &skip, target_relpath,
NULL /* left_source */,
right_source,
NULL /* copyfrom_source */,
&dir_baton,
processor,
iterpool, iterpool));
if (! skip)
SVN_ERR(processor->file_added(target_relpath,
NULL /* copyfrom_source */,
right_source,
NULL /* copyfrom_file */,
right_file,
NULL /* copyfrom_props */,
right_props,
file_baton,
processor,
iterpool));
/* ... equals replace. */
}
else
{
void *file_baton = NULL;
svn_boolean_t skip = FALSE;
apr_array_header_t *propchanges;
/* Deduce property diffs. */
SVN_ERR(svn_prop_diffs(&propchanges, right_props, left_props,
iterpool));
SVN_ERR(processor->file_opened(&file_baton, &skip, target_relpath,
left_source,
right_source,
NULL /* copyfrom_source */,
NULL /* dir_baton */,
processor,
iterpool, iterpool));
if (! skip)
SVN_ERR(processor->file_changed(target_relpath,
left_source,
right_source,
left_file,
right_file,
left_props,
right_props,
TRUE /* file changed */,
propchanges,
file_baton,
processor,
iterpool));
}
if (is_path_conflicted_by_merge(merge_b))
{
merge_source_t *remaining_range = NULL;
if (real_source->loc2->rev != source->loc2->rev)
remaining_range = subrange_source(source,
real_source->loc2->rev,
source->loc2->rev,
scratch_pool);
*conflict_report = single_range_conflict_report_create(
real_source, remaining_range, result_pool);
/* Only record partial mergeinfo if only a partial merge was
performed before a conflict was encountered. */
range.end = r->end;
break;
}
/* Now delete the just merged range from the hash
(This list is used from notify_merge_begin)
Directory merges use remove_first_range_from_remaining_ranges() */
svn_sort__array_delete(ranges_to_merge, 0, 1);
}
merge_b->notify_begin.last_abspath = NULL;
} /* !merge_b->record_only */
/* Record updated WC mergeinfo to account for our new merges, minus
any unresolved conflicts and skips. We use the original
REMAINING_RANGES here because we want to record all the requested
merge ranges, include the noop ones. */
if (RECORD_MERGEINFO(merge_b) && remaining_ranges->nelts)
{
const char *mergeinfo_path = svn_client__pathrev_fspath(primary_src,
scratch_pool);
svn_rangelist_t *filtered_rangelist;
/* Filter any ranges from TARGET_WCPATH's own history, there is no
need to record this explicitly in mergeinfo, it is already part
of TARGET_WCPATH's natural history (implicit mergeinfo). */
SVN_ERR(filter_natural_history_from_mergeinfo(
&filtered_rangelist,
mergeinfo_path,
merge_target->implicit_mergeinfo,
&range,
iterpool));
/* Only record mergeinfo if there is something other than
self-referential mergeinfo, but don't record mergeinfo if
TARGET_WCPATH was skipped. */
if (filtered_rangelist->nelts
&& (apr_hash_count(merge_b->skipped_abspaths) == 0))
{
apr_hash_t *merges = apr_hash_make(iterpool);
/* If merge target has inherited mergeinfo set it before
recording the first merge range. */
if (inherited)
SVN_ERR(svn_client__record_wc_mergeinfo(target_abspath,
target_mergeinfo,
FALSE, ctx,
iterpool));
svn_hash_sets(merges, target_abspath, filtered_rangelist);
if (!squelch_mergeinfo_notifications)
{
/* Notify that we are recording mergeinfo describing a merge. */
svn_merge_range_t n_range;
SVN_ERR(svn_mergeinfo__get_range_endpoints(
&n_range.end, &n_range.start, merges, iterpool));
n_range.inheritable = TRUE;
notify_mergeinfo_recording(target_abspath, &n_range,
merge_b->ctx, iterpool);
}
SVN_ERR(update_wc_mergeinfo(result_catalog, target_abspath,
mergeinfo_path, merges, is_rollback,
ctx, iterpool));
}
}
merge_b->notify_begin.nodes_with_mergeinfo = NULL;
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Helper for do_directory_merge() to handle the case where a merge editor
drive adds explicit mergeinfo to a path which didn't have any explicit
mergeinfo previously.
MERGE_B is cascaded from the argument of the same
name in do_directory_merge(). Should be called only after
do_directory_merge() has called populate_remaining_ranges() and populated
the remaining_ranges field of each child in
CHILDREN_WITH_MERGEINFO (i.e. the remaining_ranges fields can be
empty but never NULL).
If MERGE_B->DRY_RUN is true do nothing, if it is false then
for each path (if any) in MERGE_B->PATHS_WITH_NEW_MERGEINFO merge that
path's inherited mergeinfo (if any) with its working explicit mergeinfo
and set that as the path's new explicit mergeinfo. Then add an
svn_client__merge_path_t * element representing the path to
CHILDREN_WITH_MERGEINFO if it isn't already present. All fields
in any elements added to CHILDREN_WITH_MERGEINFO are initialized
to FALSE/NULL with the exception of 'path' and 'remaining_ranges'. The
latter is set to a rangelist equal to the remaining_ranges of the path's
nearest path-wise ancestor in CHILDREN_WITH_MERGEINFO.
Any elements added to CHILDREN_WITH_MERGEINFO are allocated
in POOL. */
static svn_error_t *
process_children_with_new_mergeinfo(merge_cmd_baton_t *merge_b,
apr_array_header_t *children_with_mergeinfo,
apr_pool_t *pool)
{
apr_pool_t *iterpool;
apr_hash_index_t *hi;
if (!merge_b->paths_with_new_mergeinfo || merge_b->dry_run)
return SVN_NO_ERROR;
/* Iterate over each path with explicit mergeinfo added by the merge. */
iterpool = svn_pool_create(pool);
for (hi = apr_hash_first(pool, merge_b->paths_with_new_mergeinfo);
hi;
hi = apr_hash_next(hi))
{
const char *abspath_with_new_mergeinfo = svn__apr_hash_index_key(hi);
svn_mergeinfo_t path_inherited_mergeinfo;
svn_mergeinfo_t path_explicit_mergeinfo;
svn_client__merge_path_t *new_child;
svn_pool_clear(iterpool);
/* Note: We could skip recording inherited mergeinfo here if this path
was added (with preexisting mergeinfo) by the merge. That's actually
more correct, since the inherited mergeinfo likely describes
non-existent or unrelated merge history, but it's not quite so simple
as that, see http://subversion.tigris.org/issues/show_bug.cgi?id=4309
*/
/* Get the path's new explicit mergeinfo... */
SVN_ERR(svn_client__get_wc_mergeinfo(&path_explicit_mergeinfo, NULL,
svn_mergeinfo_explicit,
abspath_with_new_mergeinfo,
NULL, NULL, FALSE,
merge_b->ctx,
iterpool, iterpool));
/* ...there *should* always be explicit mergeinfo at this point
but you can't be too careful. */
if (path_explicit_mergeinfo)
{
/* Get the mergeinfo the path would have inherited before
the merge. */
SVN_ERR(svn_client__get_wc_or_repos_mergeinfo(
&path_inherited_mergeinfo,
NULL, NULL,
FALSE,
svn_mergeinfo_nearest_ancestor, /* We only want inherited MI */
merge_b->ra_session2,
abspath_with_new_mergeinfo,
merge_b->ctx,
iterpool));
/* If the path inherited any mergeinfo then merge that with the
explicit mergeinfo and record the result as the path's new
explicit mergeinfo. */
if (path_inherited_mergeinfo)
{
SVN_ERR(svn_mergeinfo_merge2(path_explicit_mergeinfo,
path_inherited_mergeinfo,
iterpool, iterpool));
SVN_ERR(svn_client__record_wc_mergeinfo(
abspath_with_new_mergeinfo,
path_explicit_mergeinfo,
FALSE, merge_b->ctx, iterpool));
}
/* If the path is not in CHILDREN_WITH_MERGEINFO then add it. */
new_child =
get_child_with_mergeinfo(children_with_mergeinfo,
abspath_with_new_mergeinfo);
if (!new_child)
{
const svn_client__merge_path_t *parent
= find_nearest_ancestor(children_with_mergeinfo,
FALSE, abspath_with_new_mergeinfo);
new_child
= svn_client__merge_path_create(abspath_with_new_mergeinfo,
pool);
/* If path_with_new_mergeinfo is the merge target itself
then it should already be in
CHILDREN_WITH_MERGEINFO per the criteria of
get_mergeinfo_paths() and we shouldn't be in this block.
If path_with_new_mergeinfo is a subtree then it must have
a parent in CHILDREN_WITH_MERGEINFO if only
the merge target itself...so if we don't find a parent
the caller has done something quite wrong. */
SVN_ERR_ASSERT(parent);
SVN_ERR_ASSERT(parent->remaining_ranges);
/* Set the path's remaining_ranges equal to its parent's. */
new_child->remaining_ranges = svn_rangelist_dup(
parent->remaining_ranges, pool);
insert_child_to_merge(children_with_mergeinfo, new_child, pool);
}
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Return true if any path in SUBTREES is equal to, or is a subtree of,
LOCAL_ABSPATH. Return false otherwise. The keys of SUBTREES are
(const char *) absolute paths and its values are irrelevant.
If SUBTREES is NULL return false. */
static svn_boolean_t
path_is_subtree(const char *local_abspath,
apr_hash_t *subtrees,
apr_pool_t *pool)
{
if (subtrees)
{
apr_hash_index_t *hi;
for (hi = apr_hash_first(pool, subtrees);
hi; hi = apr_hash_next(hi))
{
const char *path_touched_by_merge = svn__apr_hash_index_key(hi);
if (svn_dirent_is_ancestor(local_abspath, path_touched_by_merge))
return TRUE;
}
}
return FALSE;
}
/* Return true if any merged, skipped, added or tree-conflicted path
recorded in MERGE_B is equal to, or is a subtree of LOCAL_ABSPATH. Return
false otherwise.
### Why not text- or prop-conflicted paths? Are such paths guaranteed
to be recorded as 'merged' or 'skipped' or 'added', perhaps?
*/
static svn_boolean_t
subtree_touched_by_merge(const char *local_abspath,
merge_cmd_baton_t *merge_b,
apr_pool_t *pool)
{
return (path_is_subtree(local_abspath, merge_b->merged_abspaths, pool)
|| path_is_subtree(local_abspath, merge_b->skipped_abspaths, pool)
|| path_is_subtree(local_abspath, merge_b->added_abspaths, pool)
|| path_is_subtree(local_abspath, merge_b->tree_conflicted_abspaths,
pool));
}
/* Helper for do_directory_merge() when performing mergeinfo unaware merges.
Merge the SOURCE diff into TARGET_DIR_WCPATH.
SOURCE, DEPTH, NOTIFY_B, and MERGE_B
are all cascaded from do_directory_merge's arguments of the same names.
CONFLICT_REPORT is as documented for do_directory_merge().
NOTE: This is a very thin wrapper around drive_merge_report_editor() and
exists only to populate CHILDREN_WITH_MERGEINFO with the single element
expected during mergeinfo unaware merges.
*/
static svn_error_t *
do_mergeinfo_unaware_dir_merge(single_range_conflict_report_t **conflict_report,
const merge_source_t *source,
const char *target_dir_wcpath,
apr_array_header_t *children_with_mergeinfo,
const svn_diff_tree_processor_t *processor,
svn_depth_t depth,
merge_cmd_baton_t *merge_b,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
/* Initialize CHILDREN_WITH_MERGEINFO and populate it with
one element describing the merge of SOURCE->rev1:rev2 to
TARGET_DIR_WCPATH. */
svn_client__merge_path_t *item
= svn_client__merge_path_create(target_dir_wcpath, scratch_pool);
*conflict_report = NULL;
item->remaining_ranges = svn_rangelist__initialize(source->loc1->rev,
source->loc2->rev,
TRUE, scratch_pool);
APR_ARRAY_PUSH(children_with_mergeinfo,
svn_client__merge_path_t *) = item;
SVN_ERR(drive_merge_report_editor(target_dir_wcpath,
source,
NULL, processor, depth,
merge_b, scratch_pool));
if (is_path_conflicted_by_merge(merge_b))
{
*conflict_report = single_range_conflict_report_create(
source, NULL, result_pool);
}
return SVN_NO_ERROR;
}
/* A svn_log_entry_receiver_t baton for log_find_operative_subtree_revs(). */
typedef struct log_find_operative_subtree_baton_t
{
/* Mapping of const char * absolute working copy paths to those
path's const char * repos absolute paths. */
apr_hash_t *operative_children;
/* As per the arguments of the same name to
get_operative_immediate_children(). */
const char *merge_source_fspath;
const char *merge_target_abspath;
svn_depth_t depth;
svn_wc_context_t *wc_ctx;
/* A pool to allocate additions to the hashes in. */
apr_pool_t *result_pool;
} log_find_operative_subtree_baton_t;
/* A svn_log_entry_receiver_t callback for
get_inoperative_immediate_children(). */
static svn_error_t *
log_find_operative_subtree_revs(void *baton,
svn_log_entry_t *log_entry,
apr_pool_t *pool)
{
log_find_operative_subtree_baton_t *log_baton = baton;
apr_hash_index_t *hi;
apr_pool_t *iterpool;
/* It's possible that authz restrictions on the merge source prevent us
from knowing about any of the changes for LOG_ENTRY->REVISION. */
if (!log_entry->changed_paths2)
return SVN_NO_ERROR;
iterpool = svn_pool_create(pool);
for (hi = apr_hash_first(pool, log_entry->changed_paths2);
hi;
hi = apr_hash_next(hi))
{
const char *path = svn__apr_hash_index_key(hi);
svn_log_changed_path2_t *change = svn__apr_hash_index_val(hi);
{
const char *child;
const char *potential_child;
const char *rel_path =
svn_fspath__skip_ancestor(log_baton->merge_source_fspath, path);
/* Some affected paths might be the root of the merge source or
entirely outside our subtree of interest. In either case they
are not operative *immediate* children. */
if (rel_path == NULL
|| rel_path[0] == '\0')
continue;
svn_pool_clear(iterpool);
child = svn_relpath_dirname(rel_path, iterpool);
if (child[0] == '\0')
{
/* The svn_log_changed_path2_t.node_kind members in
LOG_ENTRY->CHANGED_PATHS2 may be set to
svn_node_unknown, see svn_log_changed_path2_t and
svn_fs_paths_changed2. In that case we check the
type of the corresponding subtree in the merge
target. */
svn_node_kind_t node_kind;
if (change->node_kind == svn_node_unknown)
{
const char *wc_child_abspath =
svn_dirent_join(log_baton->merge_target_abspath,
rel_path, iterpool);
SVN_ERR(svn_wc_read_kind2(&node_kind, log_baton->wc_ctx,
wc_child_abspath, FALSE, FALSE,
iterpool));
}
else
{
node_kind = change->node_kind;
}
/* We only care about immediate directory children if
DEPTH is svn_depth_files. */
if (log_baton->depth == svn_depth_files
&& node_kind != svn_node_dir)
continue;
/* If depth is svn_depth_immediates, then we only care
about changes to proper subtrees of PATH. If the change
is to PATH itself then PATH is within the operational
depth of the merge. */
if (log_baton->depth == svn_depth_immediates)
continue;
child = rel_path;
}
potential_child = svn_dirent_join(log_baton->merge_target_abspath,
child, iterpool);
if (change->action == 'A'
|| !svn_hash_gets(log_baton->operative_children,
potential_child))
{
svn_hash_sets(log_baton->operative_children,
apr_pstrdup(log_baton->result_pool,
potential_child),
apr_pstrdup(log_baton->result_pool, path));
}
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Find immediate subtrees of MERGE_TARGET_ABSPATH which would have
additional differences applied if record_mergeinfo_for_dir_merge() were
recording mergeinfo describing a merge at svn_depth_infinity, rather
than at DEPTH (which is assumed to be shallow; if
DEPTH == svn_depth_infinity then this function does nothing beyond
setting *OPERATIVE_CHILDREN to an empty hash).
MERGE_SOURCE_FSPATH is the absolute repository path of the merge
source. OLDEST_REV and YOUNGEST_REV are the revisions merged from
MERGE_SOURCE_FSPATH to MERGE_TARGET_ABSPATH.
RA_SESSION points to MERGE_SOURCE_FSPATH.
Set *OPERATIVE_CHILDREN to a hash (mapping const char * absolute
working copy paths to those path's const char * repos absolute paths)
containing all the immediate subtrees of MERGE_TARGET_ABSPATH which would
have a different diff applied if MERGE_SOURCE_FSPATH
-r(OLDEST_REV - 1):YOUNGEST_REV were merged to MERGE_TARGET_ABSPATH at
svn_depth_infinity rather than DEPTH.
RESULT_POOL is used to allocate the contents of *OPERATIVE_CHILDREN.
SCRATCH_POOL is used for temporary allocations. */
static svn_error_t *
get_operative_immediate_children(apr_hash_t **operative_children,
const char *merge_source_fspath,
svn_revnum_t oldest_rev,
svn_revnum_t youngest_rev,
const char *merge_target_abspath,
svn_depth_t depth,
svn_wc_context_t *wc_ctx,
svn_ra_session_t *ra_session,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
log_find_operative_subtree_baton_t log_baton;
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(oldest_rev));
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
SVN_ERR_ASSERT(oldest_rev <= youngest_rev);
*operative_children = apr_hash_make(result_pool);
if (depth == svn_depth_infinity)
return SVN_NO_ERROR;
/* Now remove any paths from *OPERATIVE_CHILDREN that are inoperative when
merging MERGE_SOURCE_REPOS_PATH -r(OLDEST_REV - 1):YOUNGEST_REV to
MERGE_TARGET_ABSPATH at --depth infinity. */
log_baton.operative_children = *operative_children;
log_baton.merge_source_fspath = merge_source_fspath;
log_baton.merge_target_abspath = merge_target_abspath;
log_baton.depth = depth;
log_baton.wc_ctx = wc_ctx;
log_baton.result_pool = result_pool;
SVN_ERR(get_log(ra_session, "", youngest_rev, oldest_rev,
TRUE, /* discover_changed_paths */
log_find_operative_subtree_revs,
&log_baton, scratch_pool));
return SVN_NO_ERROR;
}
/* Helper for record_mergeinfo_for_dir_merge(): Identify which elements of
CHILDREN_WITH_MERGEINFO need new mergeinfo set to accurately
describe a merge, what inheritance type such new mergeinfo should have,
and what subtrees can be ignored altogether.
For each svn_client__merge_path_t CHILD in CHILDREN_WITH_MERGEINFO,
set CHILD->RECORD_MERGEINFO and CHILD->RECORD_NONINHERITABLE to true
if the subtree needs mergeinfo to describe the merge and if that
mergeinfo should be non-inheritable respectively.
If OPERATIVE_MERGE is true, then the merge being described is operative
as per subtree_touched_by_merge(). OPERATIVE_MERGE is false otherwise.
MERGED_RANGE, MERGEINFO_FSPATH, DEPTH, NOTIFY_B, and MERGE_B are all
cascaded from record_mergeinfo_for_dir_merge's arguments of the same
names.
SCRATCH_POOL is used for temporary allocations.
*/
static svn_error_t *
flag_subtrees_needing_mergeinfo(svn_boolean_t operative_merge,
const svn_merge_range_t *merged_range,
apr_array_header_t *children_with_mergeinfo,
const char *mergeinfo_fspath,
svn_depth_t depth,
merge_cmd_baton_t *merge_b,
apr_pool_t *scratch_pool)
{
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
int i;
apr_hash_t *operative_immediate_children = NULL;
assert(! merge_b->dry_run);
if (!merge_b->record_only
&& merged_range->start <= merged_range->end
&& (depth < svn_depth_infinity))
SVN_ERR(get_operative_immediate_children(
&operative_immediate_children,
mergeinfo_fspath, merged_range->start + 1, merged_range->end,
merge_b->target->abspath, depth, merge_b->ctx->wc_ctx,
merge_b->ra_session1, scratch_pool, iterpool));
/* Issue #4056: Walk NOTIFY_B->CHILDREN_WITH_MERGEINFO reverse depth-first
order. This way each child knows if it has operative missing/switched
children which necessitates non-inheritable mergeinfo. */
for (i = children_with_mergeinfo->nelts - 1; i >= 0; i--)
{
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i,
svn_client__merge_path_t *);
/* Can't record mergeinfo on something that isn't here. */
if (child->absent)
continue;
/* Verify that remove_children_with_deleted_mergeinfo() did its job */
assert((i == 0)
||! merge_b->paths_with_deleted_mergeinfo
|| !svn_hash_gets(merge_b->paths_with_deleted_mergeinfo,
child->abspath));
/* Don't record mergeinfo on skipped paths. */
if (svn_hash_gets(merge_b->skipped_abspaths, child->abspath))
continue;
/* ### ptb: Yes, we could combine the following into a single
### conditional, but clarity would suffer (even more than
### it does now). */
if (i == 0)
{
/* Always record mergeinfo on the merge target. */
child->record_mergeinfo = TRUE;
}
else if (merge_b->record_only && !merge_b->reintegrate_merge)
{
/* Always record mergeinfo for --record-only merges. */
child->record_mergeinfo = TRUE;
}
else if (child->immediate_child_dir
&& !child->pre_merge_mergeinfo
&& operative_immediate_children
&& svn_hash_gets(operative_immediate_children, child->abspath))
{
/* We must record mergeinfo on those issue #3642 children
that are operative at a greater depth. */
child->record_mergeinfo = TRUE;
}
if (operative_merge
&& subtree_touched_by_merge(child->abspath, merge_b, iterpool))
{
svn_pool_clear(iterpool);
/* This subtree was affected by the merge. */
child->record_mergeinfo = TRUE;
/* Were any CHILD's missing children skipped by the merge?
If not, then CHILD's missing children don't need to be
considered when recording mergeinfo describing the merge. */
if (! merge_b->reintegrate_merge
&& child->missing_child
&& !path_is_subtree(child->abspath,
merge_b->skipped_abspaths,
iterpool))
{
child->missing_child = FALSE;
}
/* If CHILD has an immediate switched child or children and
none of these were touched by the merge, then we don't need
need to do any special handling of those switched subtrees
(e.g. record non-inheritable mergeinfo) when recording
mergeinfo describing the merge. */
if (child->switched_child)
{
int j;
svn_boolean_t operative_switched_child = FALSE;
for (j = i + 1;
j < children_with_mergeinfo->nelts;
j++)
{
svn_client__merge_path_t *potential_child =
APR_ARRAY_IDX(children_with_mergeinfo, j,
svn_client__merge_path_t *);
if (!svn_dirent_is_ancestor(child->abspath,
potential_child->abspath))
break;
/* POTENTIAL_CHILD is a subtree of CHILD, but is it
an immediate child? */
if (strcmp(child->abspath,
svn_dirent_dirname(potential_child->abspath,
iterpool)))
continue;
if (potential_child->switched
&& potential_child->record_mergeinfo)
{
operative_switched_child = TRUE;
break;
}
}
/* Can we treat CHILD as if it has no switched children? */
if (! operative_switched_child)
child->switched_child = FALSE;
}
}
if (child->record_mergeinfo)
{
/* We need to record mergeinfo, but should that mergeinfo be
non-inheritable? */
svn_node_kind_t path_kind;
SVN_ERR(svn_wc_read_kind2(&path_kind, merge_b->ctx->wc_ctx,
child->abspath, FALSE, FALSE, iterpool));
/* Only directories can have non-inheritable mergeinfo. */
if (path_kind == svn_node_dir)
{
/* There are two general cases where non-inheritable mergeinfo
is required:
1) There merge target has missing subtrees (due to authz
restrictions, switched subtrees, or a shallow working
copy).
2) The operational depth of the merge itself is shallow. */
/* We've already determined the first case. */
child->record_noninheritable =
child->missing_child || child->switched_child;
/* The second case requires a bit more work. */
if (i == 0)
{
/* If CHILD is the root of the merge target and the
operational depth is empty or files, then the mere
existence of operative immediate children means we
must record non-inheritable mergeinfo.
### What about svn_depth_immediates? In that case
### the merge target needs only normal inheritable
### mergeinfo and the target's immediate children will
### get non-inheritable mergeinfo, assuming they
### need even that. */
if (depth < svn_depth_immediates
&& operative_immediate_children
&& apr_hash_count(operative_immediate_children))
child->record_noninheritable = TRUE;
}
else if (depth == svn_depth_immediates)
{
/* An immediate directory child of the merge target, which
was affected by a --depth=immediates merge, needs
non-inheritable mergeinfo. */
if (svn_hash_gets(operative_immediate_children,
child->abspath))
child->record_noninheritable = TRUE;
}
}
}
else /* child->record_mergeinfo */
{
/* If CHILD is in NOTIFY_B->CHILDREN_WITH_MERGEINFO simply
because it had no explicit mergeinfo of its own at the
start of the merge but is the child of of some path with
non-inheritable mergeinfo, then the explicit mergeinfo it
has *now* was set by get_mergeinfo_paths() -- see criteria
3 in that function's doc string. So since CHILD->ABSPATH
was not touched by the merge we can remove the
mergeinfo. */
if (child->child_of_noninheritable)
SVN_ERR(svn_client__record_wc_mergeinfo(child->abspath,
NULL, FALSE,
merge_b->ctx,
iterpool));
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Helper for do_directory_merge().
If RESULT_CATALOG is NULL then record mergeinfo describing a merge of
MERGED_RANGE->START:MERGED_RANGE->END from the repository relative path
MERGEINFO_FSPATH to the merge target (and possibly its subtrees) described
by NOTIFY_B->CHILDREN_WITH_MERGEINFO -- see the global comment
'THE CHILDREN_WITH_MERGEINFO ARRAY'. Obviously this should only
be called if recording mergeinfo -- see doc string for RECORD_MERGEINFO().
If RESULT_CATALOG is not NULL, then don't record the new mergeinfo on the
WC, but instead record it in RESULT_CATALOG, where the keys are absolute
working copy paths and the values are the new mergeinfos for each.
Allocate additions to RESULT_CATALOG in pool which RESULT_CATALOG was
created in.
DEPTH, NOTIFY_B, MERGE_B, and SQUELCH_MERGEINFO_NOTIFICATIONS are all
cascaded from do_directory_merge's arguments of the same names.
SCRATCH_POOL is used for temporary allocations.
*/
static svn_error_t *
record_mergeinfo_for_dir_merge(svn_mergeinfo_catalog_t result_catalog,
const svn_merge_range_t *merged_range,
const char *mergeinfo_fspath,
apr_array_header_t *children_with_mergeinfo,
svn_depth_t depth,
svn_boolean_t squelch_mergeinfo_notifications,
merge_cmd_baton_t *merge_b,
apr_pool_t *scratch_pool)
{
int i;
svn_boolean_t is_rollback = (merged_range->start > merged_range->end);
svn_boolean_t operative_merge;
/* Update the WC mergeinfo here to account for our new
merges, minus any unresolved conflicts and skips. */
/* We need a scratch pool for iterations below. */
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
svn_merge_range_t range = *merged_range;
assert(! merge_b->dry_run);
/* Regardless of what subtrees in MERGE_B->target->abspath might be missing
could this merge have been operative? */
operative_merge = subtree_touched_by_merge(merge_b->target->abspath,
merge_b, iterpool);
/* If this couldn't be an operative merge then don't bother with
the added complexity (and user confusion) of non-inheritable ranges.
There is no harm in subtrees inheriting inoperative mergeinfo. */
if (!operative_merge)
range.inheritable = TRUE;
/* Remove absent children at or under MERGE_B->target->abspath from
NOTIFY_B->CHILDREN_WITH_MERGEINFO
before we calculate the merges performed. */
remove_absent_children(merge_b->target->abspath,
children_with_mergeinfo);
/* Determine which subtrees of interest need mergeinfo recorded... */
SVN_ERR(flag_subtrees_needing_mergeinfo(operative_merge, &range,
children_with_mergeinfo,
mergeinfo_fspath, depth,
merge_b, iterpool));
/* ...and then record it. */
for (i = 0; i < children_with_mergeinfo->nelts; i++)
{
const char *child_repos_path;
const char *child_merge_src_fspath;
svn_rangelist_t *child_merge_rangelist;
apr_hash_t *child_merges;
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i,
svn_client__merge_path_t *);
SVN_ERR_ASSERT(child);
svn_pool_clear(iterpool);
if (child->record_mergeinfo)
{
child_repos_path = svn_dirent_skip_ancestor(merge_b->target->abspath,
child->abspath);
SVN_ERR_ASSERT(child_repos_path != NULL);
child_merge_src_fspath = svn_fspath__join(mergeinfo_fspath,
child_repos_path,
iterpool);
/* Filter any ranges from each child's natural history before
setting mergeinfo describing the merge. */
SVN_ERR(filter_natural_history_from_mergeinfo(
&child_merge_rangelist, child_merge_src_fspath,
child->implicit_mergeinfo, &range, iterpool));
if (child_merge_rangelist->nelts == 0)
continue;
if (!squelch_mergeinfo_notifications)
{
/* If the merge source has a gap, then don't mention
those gap revisions in the notification. */
remove_source_gap(&range, merge_b->implicit_src_gap);
notify_mergeinfo_recording(child->abspath, &range,
merge_b->ctx, iterpool);
}
/* If we are here we know we will be recording some mergeinfo, but
before we do, set override mergeinfo on skipped paths so they
don't incorrectly inherit the mergeinfo we are about to set. */
if (i == 0)
SVN_ERR(record_skips_in_mergeinfo(mergeinfo_fspath,
child_merge_rangelist,
is_rollback, merge_b, iterpool));
/* We may need to record non-inheritable mergeinfo that applies
only to CHILD->ABSPATH. */
if (child->record_noninheritable)
svn_rangelist__set_inheritance(child_merge_rangelist, FALSE);
/* If CHILD has inherited mergeinfo set it before
recording the first merge range. */
if (child->inherited_mergeinfo)
SVN_ERR(svn_client__record_wc_mergeinfo(
child->abspath,
child->pre_merge_mergeinfo,
FALSE, merge_b->ctx,
iterpool));
if (merge_b->implicit_src_gap)
{
/* If this is a reverse merge reorder CHILD->REMAINING_RANGES
so it will work with the svn_rangelist_remove API. */
if (is_rollback)
SVN_ERR(svn_rangelist_reverse(child_merge_rangelist,
iterpool));
SVN_ERR(svn_rangelist_remove(&child_merge_rangelist,
merge_b->implicit_src_gap,
child_merge_rangelist, FALSE,
iterpool));
if (is_rollback)
SVN_ERR(svn_rangelist_reverse(child_merge_rangelist,
iterpool));
}
child_merges = apr_hash_make(iterpool);
/* The short story:
If we are describing a forward merge, then the naive mergeinfo
defined by MERGE_SOURCE_PATH:MERGED_RANGE->START:
MERGE_SOURCE_PATH:MERGED_RANGE->END may contain non-existent
path-revs or may describe other lines of history. We must
remove these invalid portion(s) before recording mergeinfo
describing the merge.
The long story:
If CHILD is the merge target we know that
MERGE_SOURCE_PATH:MERGED_RANGE->END exists. Further, if there
were no copies in MERGE_SOURCE_PATH's history going back to
RANGE->START then we know that
MERGE_SOURCE_PATH:MERGED_RANGE->START exists too and the two
describe an unbroken line of history, and thus
MERGE_SOURCE_PATH:MERGED_RANGE->START:
MERGE_SOURCE_PATH:MERGED_RANGE->END is a valid description of
the merge -- see normalize_merge_sources() and the global comment
'MERGEINFO MERGE SOURCE NORMALIZATION'.
However, if there *was* a copy, then
MERGE_SOURCE_PATH:MERGED_RANGE->START doesn't exist or is
unrelated to MERGE_SOURCE_PATH:MERGED_RANGE->END. Also, we
don't know if (MERGE_SOURCE_PATH:MERGED_RANGE->START)+1 through
(MERGE_SOURCE_PATH:MERGED_RANGE->END)-1 actually exist.
If CHILD is a subtree of the merge target, then nothing is
guaranteed beyond the fact that MERGE_SOURCE_PATH exists at
MERGED_RANGE->END. */
if ((!merge_b->record_only || merge_b->reintegrate_merge)
&& (!is_rollback))
{
svn_error_t *err;
svn_mergeinfo_t subtree_history_as_mergeinfo;
svn_rangelist_t *child_merge_src_rangelist;
svn_client__pathrev_t *subtree_mergeinfo_pathrev
= svn_client__pathrev_create_with_relpath(
merge_b->target->loc.repos_root_url,
merge_b->target->loc.repos_uuid,
merged_range->end, child_merge_src_fspath + 1,
iterpool);
/* Confirm that the naive mergeinfo we want to set on
CHILD->ABSPATH both exists and is part of
(MERGE_SOURCE_PATH+CHILD_REPOS_PATH)@MERGED_RANGE->END's
history. */
/* We know MERGED_RANGE->END is younger than MERGE_RANGE->START
because we only do this for forward merges. */
err = svn_client__get_history_as_mergeinfo(
&subtree_history_as_mergeinfo, NULL,
subtree_mergeinfo_pathrev,
merged_range->end, merged_range->start,
merge_b->ra_session2, merge_b->ctx, iterpool);
/* If CHILD is a subtree it may have been deleted prior to
MERGED_RANGE->END so the above call to get its history
will fail. */
if (err)
{
if (err->apr_err != SVN_ERR_FS_NOT_FOUND)
return svn_error_trace(err);
svn_error_clear(err);
}
else
{
child_merge_src_rangelist = svn_hash_gets(
subtree_history_as_mergeinfo,
child_merge_src_fspath);
SVN_ERR(svn_rangelist_intersect(&child_merge_rangelist,
child_merge_rangelist,
child_merge_src_rangelist,
FALSE, iterpool));
if (child->record_noninheritable)
svn_rangelist__set_inheritance(child_merge_rangelist,
FALSE);
}
}
svn_hash_sets(child_merges, child->abspath, child_merge_rangelist);
SVN_ERR(update_wc_mergeinfo(result_catalog,
child->abspath,
child_merge_src_fspath,
child_merges, is_rollback,
merge_b->ctx, iterpool));
/* Once is enough: We don't need to record mergeinfo describing
the merge a second. If CHILD->ABSPATH is in
MERGE_B->ADDED_ABSPATHS, we'll do just that, so remove the
former from the latter. */
svn_hash_sets(merge_b->added_abspaths, child->abspath, NULL);
}
/* Elide explicit subtree mergeinfo whether or not we updated it. */
if (i > 0)
{
svn_boolean_t in_switched_subtree = FALSE;
if (child->switched)
in_switched_subtree = TRUE;
else if (i > 1)
{
/* Check if CHILD is part of a switched subtree */
svn_client__merge_path_t *parent;
int j = i - 1;
for (; j > 0; j--)
{
parent = APR_ARRAY_IDX(children_with_mergeinfo,
j, svn_client__merge_path_t *);
if (parent
&& parent->switched
&& svn_dirent_is_ancestor(parent->abspath,
child->abspath))
{
in_switched_subtree = TRUE;
break;
}
}
}
/* Allow mergeinfo on switched subtrees to elide to the
repository. Otherwise limit elision to the merge target
for now. do_directory_merge() will eventually try to
elide that when the merge is complete. */
SVN_ERR(svn_client__elide_mergeinfo(
child->abspath,
in_switched_subtree ? NULL : merge_b->target->abspath,
merge_b->ctx, iterpool));
}
} /* (i = 0; i < notify_b->children_with_mergeinfo->nelts; i++) */
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Helper for do_directory_merge().
Record mergeinfo describing a merge of
MERGED_RANGE->START:MERGED_RANGE->END from the repository relative path
MERGEINFO_FSPATH to each path in ADDED_ABSPATHS which has explicit
mergeinfo or is the immediate child of a parent with explicit
non-inheritable mergeinfo.
DEPTH, MERGE_B, and SQUELCH_MERGEINFO_NOTIFICATIONS, are
cascaded from do_directory_merge's arguments of the same names.
Note: This is intended to support forward merges only, i.e.
MERGED_RANGE->START must be older than MERGED_RANGE->END.
*/
static svn_error_t *
record_mergeinfo_for_added_subtrees(
svn_merge_range_t *merged_range,
const char *mergeinfo_fspath,
svn_depth_t depth,
svn_boolean_t squelch_mergeinfo_notifications,
apr_hash_t *added_abspaths,
merge_cmd_baton_t *merge_b,
apr_pool_t *pool)
{
apr_pool_t *iterpool;
apr_hash_index_t *hi;
/* If no paths were added by the merge then we have nothing to do. */
if (!added_abspaths)
return SVN_NO_ERROR;
SVN_ERR_ASSERT(merged_range->start < merged_range->end);
iterpool = svn_pool_create(pool);
for (hi = apr_hash_first(pool, added_abspaths); hi; hi = apr_hash_next(hi))
{
const char *added_abspath = svn__apr_hash_index_key(hi);
const char *dir_abspath;
svn_mergeinfo_t parent_mergeinfo;
svn_mergeinfo_t added_path_mergeinfo;
svn_pool_clear(iterpool);
dir_abspath = svn_dirent_dirname(added_abspath, iterpool);
/* Grab the added path's explicit mergeinfo. */
SVN_ERR(svn_client__get_wc_mergeinfo(&added_path_mergeinfo, NULL,
svn_mergeinfo_explicit,
added_abspath, NULL, NULL, FALSE,
merge_b->ctx, iterpool, iterpool));
/* If the added path doesn't have explicit mergeinfo, does its immediate
parent have non-inheritable mergeinfo? */
if (!added_path_mergeinfo)
SVN_ERR(svn_client__get_wc_mergeinfo(&parent_mergeinfo, NULL,
svn_mergeinfo_explicit,
dir_abspath, NULL, NULL, FALSE,
merge_b->ctx,
iterpool, iterpool));
if (added_path_mergeinfo
|| svn_mergeinfo__is_noninheritable(parent_mergeinfo, iterpool))
{
svn_node_kind_t added_path_kind;
svn_mergeinfo_t merge_mergeinfo;
svn_mergeinfo_t adds_history_as_mergeinfo;
svn_rangelist_t *rangelist;
const char *rel_added_path;
const char *added_path_mergeinfo_fspath;
svn_client__pathrev_t *added_path_pathrev;
SVN_ERR(svn_wc_read_kind2(&added_path_kind, merge_b->ctx->wc_ctx,
added_abspath, FALSE, FALSE, iterpool));
/* Calculate the naive mergeinfo describing the merge. */
merge_mergeinfo = apr_hash_make(iterpool);
rangelist = svn_rangelist__initialize(
merged_range->start, merged_range->end,
((added_path_kind == svn_node_file)
|| (!(depth == svn_depth_infinity
|| depth == svn_depth_immediates))),
iterpool);
/* Create the new mergeinfo path for added_path's mergeinfo.
(added_abspath had better be a child of MERGE_B->target->abspath
or something is *really* wrong.) */
rel_added_path = svn_dirent_is_child(merge_b->target->abspath,
added_abspath, iterpool);
SVN_ERR_ASSERT(rel_added_path);
added_path_mergeinfo_fspath = svn_fspath__join(mergeinfo_fspath,
rel_added_path,
iterpool);
svn_hash_sets(merge_mergeinfo, added_path_mergeinfo_fspath,
rangelist);
/* Don't add new mergeinfo to describe the merge if that mergeinfo
contains non-existent merge sources.
We know that MERGEINFO_PATH/rel_added_path's history does not
span MERGED_RANGE->START:MERGED_RANGE->END but rather that it
was added at some revions greater than MERGED_RANGE->START
(assuming this is a forward merge). It may have been added,
deleted, and re-added many times. The point is that we cannot
blindly apply the naive mergeinfo calculated above because it
will describe non-existent merge sources. To avoid this we get
take the intersection of the naive mergeinfo with
MERGEINFO_PATH/rel_added_path's history. */
added_path_pathrev = svn_client__pathrev_create_with_relpath(
merge_b->target->loc.repos_root_url,
merge_b->target->loc.repos_uuid,
MAX(merged_range->start, merged_range->end),
added_path_mergeinfo_fspath + 1, iterpool);
SVN_ERR(svn_client__get_history_as_mergeinfo(
&adds_history_as_mergeinfo, NULL,
added_path_pathrev,
MAX(merged_range->start, merged_range->end),
MIN(merged_range->start, merged_range->end),
merge_b->ra_session2, merge_b->ctx, iterpool));
SVN_ERR(svn_mergeinfo_intersect2(&merge_mergeinfo,
merge_mergeinfo,
adds_history_as_mergeinfo,
FALSE, iterpool, iterpool));
/* Combine the explicit mergeinfo on the added path (if any)
with the mergeinfo describing this merge. */
if (added_path_mergeinfo)
SVN_ERR(svn_mergeinfo_merge2(merge_mergeinfo,
added_path_mergeinfo,
iterpool, iterpool));
SVN_ERR(svn_client__record_wc_mergeinfo(
added_abspath, merge_mergeinfo,
!squelch_mergeinfo_notifications, merge_b->ctx, iterpool));
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Baton structure for log_noop_revs. */
typedef struct log_noop_baton_t
{
/* See the comment 'THE CHILDREN_WITH_MERGEINFO ARRAY' at the start
of this file.*/
apr_array_header_t *children_with_mergeinfo;
/* Absolute repository path of younger of the two merge sources
being diffed. */
const char *source_fspath;
/* The merge target. */
const merge_target_t *target;
/* Initially empty rangelists allocated in POOL. The rangelists are
* populated across multiple invocations of log_noop_revs(). */
svn_rangelist_t *operative_ranges;
svn_rangelist_t *merged_ranges;
/* Pool to store the rangelists. */
apr_pool_t *pool;
} log_noop_baton_t;
/* Helper for log_noop_revs: Merge a svn_merge_range_t representation of
REVISION into RANGELIST. New elements added to rangelist are allocated
in RESULT_POOL.
This is *not* a general purpose rangelist merge but a special replacement
for svn_rangelist_merge when REVISION is guaranteed to be younger than any
element in RANGELIST. svn_rangelist_merge is O(n) worst-case (i.e. when
all the ranges in output rangelist are older than the incoming changes).
This turns the special case of a single incoming younger range into O(1).
*/
static svn_error_t *
rangelist_merge_revision(svn_rangelist_t *rangelist,
svn_revnum_t revision,
apr_pool_t *result_pool)
{
svn_merge_range_t *new_range;
if (rangelist->nelts)
{
svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, rangelist->nelts - 1,
svn_merge_range_t *);
if (range->end == revision - 1)
{
/* REVISION is adjacent to the youngest range in RANGELIST
so we can simply expand that range to encompass REVISION. */
range->end = revision;
return SVN_NO_ERROR;
}
}
new_range = apr_palloc(result_pool, sizeof(*new_range));
new_range->start = revision - 1;
new_range->end = revision;
new_range->inheritable = TRUE;
APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = new_range;
return SVN_NO_ERROR;
}
/* Implements the svn_log_entry_receiver_t interface.
BATON is an log_noop_baton_t *.
Add LOG_ENTRY->REVISION to BATON->OPERATIVE_RANGES.
If LOG_ENTRY->REVISION has already been fully merged to
BATON->target->abspath per the mergeinfo in BATON->CHILDREN_WITH_MERGEINFO,
then add LOG_ENTRY->REVISION to BATON->MERGED_RANGES.
Use SCRATCH_POOL for temporary allocations. Allocate additions to
BATON->MERGED_RANGES and BATON->OPERATIVE_RANGES in BATON->POOL.
Note: This callback must be invoked from oldest LOG_ENTRY->REVISION
to youngest LOG_ENTRY->REVISION -- see rangelist_merge_revision().
*/
static svn_error_t *
log_noop_revs(void *baton,
svn_log_entry_t *log_entry,
apr_pool_t *scratch_pool)
{
log_noop_baton_t *log_gap_baton = baton;
apr_hash_index_t *hi;
svn_revnum_t revision;
svn_boolean_t log_entry_rev_required = FALSE;
revision = log_entry->revision;
/* It's possible that authz restrictions on the merge source prevent us
from knowing about any of the changes for LOG_ENTRY->REVISION. */
if (!log_entry->changed_paths2)
return SVN_NO_ERROR;
/* Unconditionally add LOG_ENTRY->REVISION to BATON->OPERATIVE_MERGES. */
SVN_ERR(rangelist_merge_revision(log_gap_baton->operative_ranges,
revision,
log_gap_baton->pool));
/* Examine each path affected by LOG_ENTRY->REVISION. If the explicit or
inherited mergeinfo for *all* of the corresponding paths under
BATON->target->abspath reflects that LOG_ENTRY->REVISION has been
merged, then add LOG_ENTRY->REVISION to BATON->MERGED_RANGES. */
for (hi = apr_hash_first(scratch_pool, log_entry->changed_paths2);
hi;
hi = apr_hash_next(hi))
{
const char *fspath = svn__apr_hash_index_key(hi);
const char *rel_path;
const char *cwmi_abspath;
svn_rangelist_t *paths_explicit_rangelist = NULL;
svn_boolean_t mergeinfo_inherited = FALSE;
/* Adjust REL_PATH so it is relative to the merge source then use it to
calculate what path in the merge target would be affected by this
revision. */
rel_path = svn_fspath__skip_ancestor(log_gap_baton->source_fspath,
fspath);
/* Is PATH even within the merge target? If it isn't we
can disregard it altogether. */
if (rel_path == NULL)
continue;
cwmi_abspath = svn_dirent_join(log_gap_baton->target->abspath,
rel_path, scratch_pool);
/* Find any explicit or inherited mergeinfo for PATH. */
while (!log_entry_rev_required)
{
svn_client__merge_path_t *child = get_child_with_mergeinfo(
log_gap_baton->children_with_mergeinfo, cwmi_abspath);
if (child && child->pre_merge_mergeinfo)
{
/* Found some explicit mergeinfo, grab any ranges
for PATH. */
paths_explicit_rangelist =
svn_hash_gets(child->pre_merge_mergeinfo, fspath);
break;
}
if (cwmi_abspath[0] == '\0'
|| svn_dirent_is_root(cwmi_abspath, strlen(cwmi_abspath))
|| strcmp(log_gap_baton->target->abspath, cwmi_abspath) == 0)
{
/* Can't crawl any higher. */
break;
}
/* Didn't find anything so crawl up to the parent. */
cwmi_abspath = svn_dirent_dirname(cwmi_abspath, scratch_pool);
fspath = svn_fspath__dirname(fspath, scratch_pool);
/* At this point *if* we find mergeinfo it will be inherited. */
mergeinfo_inherited = TRUE;
}
if (paths_explicit_rangelist)
{
svn_rangelist_t *intersecting_range;
svn_rangelist_t *rangelist;
rangelist = svn_rangelist__initialize(revision - 1, revision, TRUE,
scratch_pool);
/* If PATH inherited mergeinfo we must consider inheritance in the
event the inherited mergeinfo is actually non-inheritable. */
SVN_ERR(svn_rangelist_intersect(&intersecting_range,
paths_explicit_rangelist,
rangelist,
mergeinfo_inherited, scratch_pool));
if (intersecting_range->nelts == 0)
log_entry_rev_required = TRUE;
}
else
{
log_entry_rev_required = TRUE;
}
}
if (!log_entry_rev_required)
SVN_ERR(rangelist_merge_revision(log_gap_baton->merged_ranges,
revision,
log_gap_baton->pool));
return SVN_NO_ERROR;
}
/* Helper for do_directory_merge().
SOURCE is cascaded from the argument of the same name in
do_directory_merge(). TARGET is the merge target. RA_SESSION is the
session for SOURCE->loc2.
Find all the ranges required by subtrees in
CHILDREN_WITH_MERGEINFO that are *not* required by
TARGET->abspath (i.e. CHILDREN_WITH_MERGEINFO[0]). If such
ranges exist, then find any subset of ranges which, if merged, would be
inoperative. Finally, if any inoperative ranges are found then remove
these ranges from all of the subtree's REMAINING_RANGES.
This function should only be called when honoring mergeinfo during
forward merges (i.e. SOURCE->rev1 < SOURCE->rev2).
*/
static svn_error_t *
remove_noop_subtree_ranges(const merge_source_t *source,
const merge_target_t *target,
svn_ra_session_t *ra_session,
apr_array_header_t *children_with_mergeinfo,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
/* ### Do we need to check that we are at a uniform working revision? */
int i;
svn_client__merge_path_t *root_child =
APR_ARRAY_IDX(children_with_mergeinfo, 0, svn_client__merge_path_t *);
svn_rangelist_t *requested_ranges;
svn_rangelist_t *subtree_gap_ranges;
svn_rangelist_t *subtree_remaining_ranges;
log_noop_baton_t log_gap_baton;
svn_merge_range_t *oldest_gap_rev;
svn_merge_range_t *youngest_gap_rev;
svn_rangelist_t *inoperative_ranges;
apr_pool_t *iterpool;
const char *longest_common_subtree_ancestor = NULL;
svn_error_t *err;
assert(session_url_is(ra_session, source->loc2->url, scratch_pool));
/* This function is only intended to work with forward merges. */
if (source->loc1->rev > source->loc2->rev)
return SVN_NO_ERROR;
/* Another easy out: There are no subtrees. */
if (children_with_mergeinfo->nelts < 2)
return SVN_NO_ERROR;
subtree_remaining_ranges = apr_array_make(scratch_pool, 1,
sizeof(svn_merge_range_t *));
/* Given the requested merge of SOURCE->rev1:rev2 might there be any
part of this range required for subtrees but not for the target? */
requested_ranges = svn_rangelist__initialize(MIN(source->loc1->rev,
source->loc2->rev),
MAX(source->loc1->rev,
source->loc2->rev),
TRUE, scratch_pool);
SVN_ERR(svn_rangelist_remove(&subtree_gap_ranges,
root_child->remaining_ranges,
requested_ranges, FALSE, scratch_pool));
/* Early out, nothing to operate on */
if (!subtree_gap_ranges->nelts)
return SVN_NO_ERROR;
/* Create a rangelist describing every range required across all subtrees. */
iterpool = svn_pool_create(scratch_pool);
for (i = 1; i < children_with_mergeinfo->nelts; i++)
{
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
svn_pool_clear(iterpool);
/* Issue #4269: Keep track of the longest common ancestor of all the
subtrees which require merges. This may be a child of
TARGET->ABSPATH, which will allow us to narrow the log request
below. */
if (child->remaining_ranges && child->remaining_ranges->nelts)
{
if (longest_common_subtree_ancestor)
longest_common_subtree_ancestor = svn_dirent_get_longest_ancestor(
longest_common_subtree_ancestor, child->abspath, scratch_pool);
else
longest_common_subtree_ancestor = child->abspath;
}
/* CHILD->REMAINING_RANGES will be NULL if child is absent. */
if (child->remaining_ranges && child->remaining_ranges->nelts)
SVN_ERR(svn_rangelist_merge2(subtree_remaining_ranges,
child->remaining_ranges,
scratch_pool, iterpool));
}
svn_pool_destroy(iterpool);
/* It's possible that none of the subtrees had any remaining ranges. */
if (!subtree_remaining_ranges->nelts)
return SVN_NO_ERROR;
/* Ok, *finally* we can answer what part(s) of SOURCE->rev1:rev2 are
required for the subtrees but not the target. */
SVN_ERR(svn_rangelist_intersect(&subtree_gap_ranges,
subtree_gap_ranges,
subtree_remaining_ranges, FALSE,
scratch_pool));
/* Another early out */
if (!subtree_gap_ranges->nelts)
return SVN_NO_ERROR;
/* One or more subtrees need some revisions that the target doesn't need.
Use log to determine if any of these revisions are inoperative. */
oldest_gap_rev = APR_ARRAY_IDX(subtree_gap_ranges, 0, svn_merge_range_t *);
youngest_gap_rev = APR_ARRAY_IDX(subtree_gap_ranges,
subtree_gap_ranges->nelts - 1, svn_merge_range_t *);
/* Set up the log baton. */
log_gap_baton.children_with_mergeinfo = children_with_mergeinfo;
log_gap_baton.source_fspath
= svn_client__pathrev_fspath(source->loc2, result_pool);
log_gap_baton.target = target;
log_gap_baton.merged_ranges = apr_array_make(scratch_pool, 0,
sizeof(svn_revnum_t *));
log_gap_baton.operative_ranges = apr_array_make(scratch_pool, 0,
sizeof(svn_revnum_t *));
log_gap_baton.pool = svn_pool_create(scratch_pool);
/* Find the longest common ancestor of all subtrees relative to
RA_SESSION's URL. */
if (longest_common_subtree_ancestor)
longest_common_subtree_ancestor =
svn_dirent_skip_ancestor(target->abspath,
longest_common_subtree_ancestor);
else
longest_common_subtree_ancestor = "";
/* Invoke the svn_log_entry_receiver_t receiver log_noop_revs() from
oldest to youngest. The receiver is optimized to add ranges to
log_gap_baton.merged_ranges and log_gap_baton.operative_ranges, but
requires that the revs arrive oldest to youngest -- see log_noop_revs()
and rangelist_merge_revision(). */
err = get_log(ra_session, longest_common_subtree_ancestor,
oldest_gap_rev->start + 1, youngest_gap_rev->end, TRUE,
log_noop_revs, &log_gap_baton, scratch_pool);
/* It's possible that the only subtrees with mergeinfo in TARGET don't have
any corresponding subtree in SOURCE between SOURCE->REV1 < SOURCE->REV2.
So it's also possible that we may ask for the logs of non-existent paths.
If we do, then assume that no subtree requires any ranges that are not
already required by the TARGET. */
if (err)
{
if (err->apr_err != SVN_ERR_FS_NOT_FOUND
&& longest_common_subtree_ancestor[0] != '\0')
return svn_error_trace(err);
/* Asked about a non-existent subtree in SOURCE. */
svn_error_clear(err);
log_gap_baton.merged_ranges =
svn_rangelist__initialize(oldest_gap_rev->start,
youngest_gap_rev->end,
TRUE, scratch_pool);
}
else
{
inoperative_ranges = svn_rangelist__initialize(oldest_gap_rev->start,
youngest_gap_rev->end,
TRUE, scratch_pool);
SVN_ERR(svn_rangelist_remove(&(inoperative_ranges),
log_gap_baton.operative_ranges,
inoperative_ranges, FALSE, scratch_pool));
SVN_ERR(svn_rangelist_merge2(log_gap_baton.merged_ranges, inoperative_ranges,
scratch_pool, scratch_pool));
}
for (i = 1; i < children_with_mergeinfo->nelts; i++)
{
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
/* CHILD->REMAINING_RANGES will be NULL if child is absent. */
if (child->remaining_ranges && child->remaining_ranges->nelts)
{
/* Remove inoperative ranges from all children so we don't perform
inoperative editor drives. */
SVN_ERR(svn_rangelist_remove(&(child->remaining_ranges),
log_gap_baton.merged_ranges,
child->remaining_ranges,
FALSE, result_pool));
}
}
svn_pool_destroy(log_gap_baton.pool);
return SVN_NO_ERROR;
}
/* Perform a merge of changes in SOURCE to the working copy path
TARGET_ABSPATH. Both URLs in SOURCE, and TARGET_ABSPATH all represent
directories -- for the single file case, the caller should use
do_file_merge().
CHILDREN_WITH_MERGEINFO and MERGE_B describe the merge being performed
As this function is for a mergeinfo-aware merge, SOURCE->ancestral
should be TRUE, and SOURCE->loc1 must be a historical ancestor of
SOURCE->loc2, or vice-versa (see `MERGEINFO MERGE SOURCE NORMALIZATION'
for more requirements around SOURCE).
Mergeinfo changes will be recorded unless MERGE_B->dry_run is true.
If mergeinfo is being recorded, SQUELCH_MERGEINFO_NOTIFICATIONS is FALSE,
and MERGE_B->CTX->NOTIFY_FUNC2 is not NULL, then call
MERGE_B->CTX->NOTIFY_FUNC2 with MERGE_B->CTX->NOTIFY_BATON2 and a
svn_wc_notify_merge_record_info_begin notification before any mergeinfo
changes are made to describe the merge performed.
If mergeinfo is being recorded to describe this merge, and RESULT_CATALOG
is not NULL, then don't record the new mergeinfo on the WC, but instead
record it in RESULT_CATALOG, where the keys are absolute working copy
paths and the values are the new mergeinfos for each. Allocate additions
to RESULT_CATALOG in pool which RESULT_CATALOG was created in.
Handle DEPTH as documented for svn_client_merge5().
CONFLICT_REPORT is as documented for do_directory_merge().
Perform any temporary allocations in SCRATCH_POOL.
NOTE: This is a wrapper around drive_merge_report_editor() which
handles the complexities inherent to situations where a given
directory's children may have intersecting merges (because they
meet one or more of the criteria described in get_mergeinfo_paths()).
*/
static svn_error_t *
do_mergeinfo_aware_dir_merge(svn_mergeinfo_catalog_t result_catalog,
single_range_conflict_report_t **conflict_report,
const merge_source_t *source,
const char *target_abspath,
apr_array_header_t *children_with_mergeinfo,
const svn_diff_tree_processor_t *processor,
svn_depth_t depth,
svn_boolean_t squelch_mergeinfo_notifications,
merge_cmd_baton_t *merge_b,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
/* The range defining the mergeinfo we will record to describe the merge
(assuming we are recording mergeinfo
Note: This may be a subset of SOURCE->rev1:rev2 if
populate_remaining_ranges() determines that some part of
SOURCE->rev1:rev2 has already been wholly merged to TARGET_ABSPATH.
Also, the actual editor drive(s) may be a subset of RANGE, if
remove_noop_subtree_ranges() and/or fix_deleted_subtree_ranges()
further tweak things. */
svn_merge_range_t range;
svn_ra_session_t *ra_session;
svn_client__merge_path_t *target_merge_path;
svn_boolean_t is_rollback = (source->loc1->rev > source->loc2->rev);
SVN_ERR_ASSERT(source->ancestral);
/*** If we get here, we're dealing with related sources from the
same repository as the target -- merge tracking might be
happenin'! ***/
*conflict_report = NULL;
/* Point our RA_SESSION to the URL of our youngest merge source side. */
ra_session = is_rollback ? merge_b->ra_session1 : merge_b->ra_session2;
/* Fill NOTIFY_B->CHILDREN_WITH_MERGEINFO with child paths (const
svn_client__merge_path_t *) which might have intersecting merges
because they meet one or more of the criteria described in
get_mergeinfo_paths(). Here the paths are arranged in a depth
first order. */
SVN_ERR(get_mergeinfo_paths(children_with_mergeinfo,
merge_b->target, depth,
merge_b->dry_run, merge_b->same_repos,
merge_b->ctx, scratch_pool, scratch_pool));
/* The first item from the NOTIFY_B->CHILDREN_WITH_MERGEINFO is always
the target thanks to depth-first ordering. */
target_merge_path = APR_ARRAY_IDX(children_with_mergeinfo, 0,
svn_client__merge_path_t *);
/* If we are honoring mergeinfo, then for each item in
NOTIFY_B->CHILDREN_WITH_MERGEINFO, we need to calculate what needs to be
merged, and then merge it. Otherwise, we just merge what we were asked
to merge across the whole tree. */
SVN_ERR(populate_remaining_ranges(children_with_mergeinfo,
source, ra_session,
merge_b, scratch_pool, scratch_pool));
/* Always start with a range which describes the most inclusive merge
possible, i.e. SOURCE->rev1:rev2. */
range.start = source->loc1->rev;
range.end = source->loc2->rev;
range.inheritable = TRUE;
if (!merge_b->reintegrate_merge)
{
svn_revnum_t new_range_start, start_rev;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
/* The merge target TARGET_ABSPATH and/or its subtrees may not need all
of SOURCE->rev1:rev2 applied. So examine
NOTIFY_B->CHILDREN_WITH_MERGEINFO to find the oldest starting
revision that actually needs to be merged (for reverse merges this is
the youngest starting revision).
We'll do this twice, right now for the start of the mergeinfo we will
ultimately record to describe this merge and then later for the
start of the actual editor drive. */
new_range_start = get_most_inclusive_rev(
children_with_mergeinfo, is_rollback, TRUE);
if (SVN_IS_VALID_REVNUM(new_range_start))
range.start = new_range_start;
/* Remove inoperative ranges from any subtrees' remaining_ranges
to spare the expense of noop editor drives. */
if (!is_rollback)
SVN_ERR(remove_noop_subtree_ranges(source, merge_b->target,
ra_session,
children_with_mergeinfo,
scratch_pool, iterpool));
/* Adjust subtrees' remaining_ranges to deal with issue #3067:
* "subtrees that don't exist at the start or end of a merge range
* shouldn't break the merge". */
SVN_ERR(fix_deleted_subtree_ranges(source, merge_b->target,
ra_session,
children_with_mergeinfo,
merge_b->ctx, scratch_pool, iterpool));
/* remove_noop_subtree_ranges() and/or fix_deleted_subtree_range()
may have further refined the starting revision for our editor
drive. */
start_rev =
get_most_inclusive_rev(children_with_mergeinfo,
is_rollback, TRUE);
/* Is there anything to merge? */
if (SVN_IS_VALID_REVNUM(start_rev))
{
/* Now examine NOTIFY_B->CHILDREN_WITH_MERGEINFO to find the oldest
ending revision that actually needs to be merged (for reverse
merges this is the youngest ending revision). */
svn_revnum_t end_rev =
get_most_inclusive_rev(children_with_mergeinfo,
is_rollback, FALSE);
/* While END_REV is valid, do the following:
1. Tweak each NOTIFY_B->CHILDREN_WITH_MERGEINFO element so that
the element's remaining_ranges member has as its first element
a range that ends with end_rev.
2. Starting with start_rev, call drive_merge_report_editor()
on MERGE_B->target->abspath for start_rev:end_rev.
3. Remove the first element from each
NOTIFY_B->CHILDREN_WITH_MERGEINFO element's remaining_ranges
member.
4. Again examine NOTIFY_B->CHILDREN_WITH_MERGEINFO to find the most
inclusive starting revision that actually needs to be merged and
update start_rev. This prevents us from needlessly contacting the
repository and doing a diff where we describe the entire target
tree as *not* needing any of the requested range. This can happen
whenever we have mergeinfo with gaps in it for the merge source.
5. Again examine NOTIFY_B->CHILDREN_WITH_MERGEINFO to find the most
inclusive ending revision that actually needs to be merged and
update end_rev.
6. Lather, rinse, repeat.
*/
while (end_rev != SVN_INVALID_REVNUM)
{
merge_source_t *real_source;
svn_merge_range_t *first_target_range
= (target_merge_path->remaining_ranges->nelts == 0 ? NULL
: APR_ARRAY_IDX(target_merge_path->remaining_ranges, 0,
svn_merge_range_t *));
/* Issue #3324: Stop editor abuse! Don't call
drive_merge_report_editor() in such a way that we request an
editor with svn_client__get_diff_editor() for some rev X,
then call svn_ra_do_diff3() for some revision Y, and then
call reporter->set_path(PATH=="") to set the root revision
for the editor drive to revision Z where
(X != Z && X < Z < Y). This is bogus because the server will
send us the diff between X:Y but the client is expecting the
diff between Y:Z. See issue #3324 for full details on the
problems this can cause. */
if (first_target_range
&& start_rev != first_target_range->start)
{
if (is_rollback)
{
if (end_rev < first_target_range->start)
end_rev = first_target_range->start;
}
else
{
if (end_rev > first_target_range->start)
end_rev = first_target_range->start;
}
}
svn_pool_clear(iterpool);
slice_remaining_ranges(children_with_mergeinfo,
is_rollback, end_rev, scratch_pool);
/* Reset variables that must be reset for every drive */
merge_b->notify_begin.last_abspath = NULL;
real_source = subrange_source(source, start_rev, end_rev, iterpool);
SVN_ERR(drive_merge_report_editor(
merge_b->target->abspath,
real_source,
children_with_mergeinfo,
processor,
depth,
merge_b,
iterpool));
/* If any paths picked up explicit mergeinfo as a result of
the merge we need to make sure any mergeinfo those paths
inherited is recorded and then add these paths to
NOTIFY_B->CHILDREN_WITH_MERGEINFO.*/
SVN_ERR(process_children_with_new_mergeinfo(
merge_b, children_with_mergeinfo,
scratch_pool));
/* If any subtrees had their explicit mergeinfo deleted as a
result of the merge then remove these paths from
NOTIFY_B->CHILDREN_WITH_MERGEINFO since there is no need
to consider these subtrees for subsequent editor drives
nor do we want to record mergeinfo on them describing
the merge itself. */
remove_children_with_deleted_mergeinfo(
merge_b, children_with_mergeinfo);
/* Prepare for the next iteration (if any). */
remove_first_range_from_remaining_ranges(
end_rev, children_with_mergeinfo, scratch_pool);
/* If we raised any conflicts, break out and report how much
we have merged. */
if (is_path_conflicted_by_merge(merge_b))
{
merge_source_t *remaining_range = NULL;
if (real_source->loc2->rev != source->loc2->rev)
remaining_range = subrange_source(source,
real_source->loc2->rev,
source->loc2->rev,
scratch_pool);
*conflict_report = single_range_conflict_report_create(
real_source, remaining_range,
result_pool);
range.end = end_rev;
break;
}
start_rev =
get_most_inclusive_rev(children_with_mergeinfo,
is_rollback, TRUE);
end_rev =
get_most_inclusive_rev(children_with_mergeinfo,
is_rollback, FALSE);
}
}
svn_pool_destroy(iterpool);
}
else
{
if (!merge_b->record_only)
{
/* Reset cur_ancestor_abspath to null so that subsequent cherry
picked revision ranges will be notified upon subsequent
operative merge. */
merge_b->notify_begin.last_abspath = NULL;
SVN_ERR(drive_merge_report_editor(merge_b->target->abspath,
source,
NULL,
processor,
depth,
merge_b,
scratch_pool));
}
}
/* Record mergeinfo where appropriate.*/
if (RECORD_MERGEINFO(merge_b))
{
const svn_client__pathrev_t *primary_src
= is_rollback ? source->loc1 : source->loc2;
const char *mergeinfo_path
= svn_client__pathrev_fspath(primary_src, scratch_pool);
SVN_ERR(record_mergeinfo_for_dir_merge(result_catalog,
&range,
mergeinfo_path,
children_with_mergeinfo,
depth,
squelch_mergeinfo_notifications,
merge_b,
scratch_pool));
/* If a path has an immediate parent with non-inheritable mergeinfo at
this point, then it meets criteria 3 or 5 described in
get_mergeinfo_paths' doc string. For paths which exist prior to a
merge explicit mergeinfo has already been set. But for paths added
during the merge this is not the case. The path might have explicit
mergeinfo from the merge source, but no mergeinfo yet exists
describing *this* merge. So the added path has either incomplete
explicit mergeinfo or inherits incomplete mergeinfo from its
immediate parent (if any, the parent might have only non-inheritable
ranges in which case the path simply inherits empty mergeinfo).
So here we look at the root path of each subtree added during the
merge and set explicit mergeinfo on it if it meets the aforementioned
conditions. */
if (range.start < range.end) /* Nothing to record on added subtrees
resulting from reverse merges. */
{
SVN_ERR(record_mergeinfo_for_added_subtrees(
&range, mergeinfo_path, depth,
squelch_mergeinfo_notifications,
merge_b->added_abspaths, merge_b, scratch_pool));
}
}
return SVN_NO_ERROR;
}
/* Helper for do_merge() when the merge target is a directory.
*
* If any conflict is raised during the merge, set *CONFLICTED_RANGE to
* the revision sub-range that raised the conflict. In this case, the
* merge will have ended at revision CONFLICTED_RANGE and mergeinfo will
* have been recorded for all revision sub-ranges up to and including
* CONFLICTED_RANGE. Otherwise, set *CONFLICTED_RANGE to NULL.
*/
static svn_error_t *
do_directory_merge(svn_mergeinfo_catalog_t result_catalog,
single_range_conflict_report_t **conflict_report,
const merge_source_t *source,
const char *target_abspath,
const svn_diff_tree_processor_t *processor,
svn_depth_t depth,
svn_boolean_t squelch_mergeinfo_notifications,
merge_cmd_baton_t *merge_b,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_array_header_t *children_with_mergeinfo;
/* Initialize CHILDREN_WITH_MERGEINFO. See the comment
'THE CHILDREN_WITH_MERGEINFO ARRAY' at the start of this file. */
children_with_mergeinfo =
apr_array_make(scratch_pool, 16, sizeof(svn_client__merge_path_t *));
/* And make it read-only accessible from the baton */
merge_b->notify_begin.nodes_with_mergeinfo = children_with_mergeinfo;
/* If we are not honoring mergeinfo we can skip right to the
business of merging changes! */
if (HONOR_MERGEINFO(merge_b))
SVN_ERR(do_mergeinfo_aware_dir_merge(result_catalog, conflict_report,
source, target_abspath,
children_with_mergeinfo,
processor, depth,
squelch_mergeinfo_notifications,
merge_b, result_pool, scratch_pool));
else
SVN_ERR(do_mergeinfo_unaware_dir_merge(conflict_report,
source, target_abspath,
children_with_mergeinfo,
processor, depth,
merge_b, result_pool, scratch_pool));
merge_b->notify_begin.nodes_with_mergeinfo = NULL;
return SVN_NO_ERROR;
}
/** Ensure that *RA_SESSION is opened to URL, either by reusing
* *RA_SESSION if it is non-null and already opened to URL's
* repository, or by allocating a new *RA_SESSION in POOL.
* (RA_SESSION itself cannot be null, of course.)
*
* CTX is used as for svn_client_open_ra_session().
*/
static svn_error_t *
ensure_ra_session_url(svn_ra_session_t **ra_session,
const char *url,
const char *wri_abspath,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
svn_error_t *err = SVN_NO_ERROR;
if (*ra_session)
{
err = svn_ra_reparent(*ra_session, url, pool);
}
/* SVN_ERR_RA_ILLEGAL_URL is raised when url doesn't point to the same
repository as ra_session. */
if (! *ra_session || (err && err->apr_err == SVN_ERR_RA_ILLEGAL_URL))
{
svn_error_clear(err);
err = svn_client_open_ra_session2(ra_session, url, wri_abspath,
ctx, pool, pool);
}
SVN_ERR(err);
return SVN_NO_ERROR;
}
/* Drive a merge of MERGE_SOURCES into working copy node TARGET
and possibly record mergeinfo describing the merge -- see
RECORD_MERGEINFO().
If MODIFIED_SUBTREES is not NULL and all the MERGE_SOURCES are 'ancestral'
or REINTEGRATE_MERGE is true, then replace *MODIFIED_SUBTREES with a new
hash containing all the paths that *MODIFIED_SUBTREES contained before,
and also every path modified, skipped, added, or tree-conflicted
by the merge. Keys and values of the hash are both (const char *)
absolute paths. The contents of the hash are allocated in RESULT_POOL.
If the merge raises any conflicts while merging a revision range, return
early and set *CONFLICT_REPORT to describe the details. (In this case,
notify that the merge is complete if and only if this was the last
revision range of the merge.) If there are no conflicts, set
*CONFLICT_REPORT to NULL. A revision range here can be one specified
in MERGE_SOURCES or an internally generated sub-range of one of those
when merge tracking is in use.
For every (const merge_source_t *) merge source in MERGE_SOURCES, if
SOURCE->ANCESTRAL is set, then the "left" and "right" side are
ancestrally related. (See 'MERGEINFO MERGE SOURCE NORMALIZATION'
for more on what that means and how it matters.)
If SOURCES_RELATED is set, the "left" and "right" sides of the
merge source are historically related (ancestors, uncles, second
cousins thrice removed, etc...). (This is passed through to
do_file_merge() to simulate the history checks that the repository
logic does in the directory case.)
SAME_REPOS is TRUE iff the merge sources live in the same
repository as the one from which the target working copy has been
checked out.
If mergeinfo is being recorded, SQUELCH_MERGEINFO_NOTIFICATIONS is FALSE,
and CTX->NOTIFY_FUNC2 is not NULL, then call CTX->NOTIFY_FUNC2 with
CTX->NOTIFY_BATON2 and a svn_wc_notify_merge_record_info_begin
notification before any mergeinfo changes are made to describe the merge
performed.
If mergeinfo is being recorded to describe this merge, and RESULT_CATALOG
is not NULL, then don't record the new mergeinfo on the WC, but instead
record it in RESULT_CATALOG, where the keys are absolute working copy
paths and the values are the new mergeinfos for each. Allocate additions
to RESULT_CATALOG in pool which RESULT_CATALOG was created in.
FORCE_DELETE, DRY_RUN, RECORD_ONLY, DEPTH, MERGE_OPTIONS,
and CTX are as described in the docstring for svn_client_merge_peg3().
If IGNORE_MERGEINFO is true, disable merge tracking, by treating the two
sources as unrelated even if they actually have a common ancestor. See
the macro HONOR_MERGEINFO().
If DIFF_IGNORE_ANCESTRY is true, diff the 'left' and 'right' versions
of a node (if they are the same kind) as if they were related, even if
they are not related. Otherwise, diff unrelated items as a deletion
of one thing and the addition of another.
If not NULL, RECORD_ONLY_PATHS is a hash of (const char *) paths mapped
to the same. If RECORD_ONLY is true and RECORD_ONLY_PATHS is not NULL,
then record mergeinfo describing the merge only on subtrees which contain
items from RECORD_ONLY_PATHS. If RECORD_ONLY is true and RECORD_ONLY_PATHS
is NULL, then record mergeinfo on every subtree with mergeinfo in
TARGET.
REINTEGRATE_MERGE is TRUE if this is a reintegrate merge.
*USE_SLEEP will be set TRUE if a sleep is required to ensure timestamp
integrity, *USE_SLEEP will be unchanged if no sleep is required.
SCRATCH_POOL is used for all temporary allocations.
*/
static svn_error_t *
do_merge(apr_hash_t **modified_subtrees,
svn_mergeinfo_catalog_t result_catalog,
conflict_report_t **conflict_report,
svn_boolean_t *use_sleep,
const apr_array_header_t *merge_sources,
const merge_target_t *target,
svn_ra_session_t *src_session,
svn_boolean_t sources_related,
svn_boolean_t same_repos,
svn_boolean_t ignore_mergeinfo,
svn_boolean_t diff_ignore_ancestry,
svn_boolean_t force_delete,
svn_boolean_t dry_run,
svn_boolean_t record_only,
apr_hash_t *record_only_paths,
svn_boolean_t reintegrate_merge,
svn_boolean_t squelch_mergeinfo_notifications,
svn_depth_t depth,
const apr_array_header_t *merge_options,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t merge_cmd_baton = { 0 };
svn_config_t *cfg;
const char *diff3_cmd;
const char *preserved_exts_str;
int i;
svn_boolean_t checked_mergeinfo_capability = FALSE;
svn_ra_session_t *ra_session1 = NULL, *ra_session2 = NULL;
const char *old_src_session_url = NULL;
apr_pool_t *iterpool;
const svn_diff_tree_processor_t *processor;
SVN_ERR_ASSERT(svn_dirent_is_absolute(target->abspath));
*conflict_report = NULL;
/* Check from some special conditions when in record-only mode
(which is a merge-tracking thing). */
if (record_only)
{
svn_boolean_t sources_ancestral = TRUE;
int j;
/* Find out whether all of the sources are 'ancestral'. */
for (j = 0; j < merge_sources->nelts; j++)
if (! APR_ARRAY_IDX(merge_sources, j, merge_source_t *)->ancestral)
{
sources_ancestral = FALSE;
break;
}
/* We can't do a record-only merge if the sources aren't related. */
if (! sources_ancestral)
return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
_("Use of two URLs is not compatible with "
"mergeinfo modification"));
/* We can't do a record-only merge if the sources aren't from
the same repository as the target. */
if (! same_repos)
return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
_("Merge from foreign repository is not "
"compatible with mergeinfo modification"));
/* If this is a dry-run record-only merge, there's nothing to do. */
if (dry_run)
return SVN_NO_ERROR;
}
iterpool = svn_pool_create(scratch_pool);
/* Ensure a known depth. */
if (depth == svn_depth_unknown)
depth = svn_depth_infinity;
/* Set up the diff3 command, so various callers don't have to. */
cfg = ctx->config
? svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_CONFIG)
: NULL;
svn_config_get(cfg, &diff3_cmd, SVN_CONFIG_SECTION_HELPERS,
SVN_CONFIG_OPTION_DIFF3_CMD, NULL);
if (diff3_cmd != NULL)
SVN_ERR(svn_path_cstring_to_utf8(&diff3_cmd, diff3_cmd, scratch_pool));
/* See which files the user wants to preserve the extension of when
conflict files are made. */
svn_config_get(cfg, &preserved_exts_str, SVN_CONFIG_SECTION_MISCELLANY,
SVN_CONFIG_OPTION_PRESERVED_CF_EXTS, "");
/* Build the merge context baton (or at least the parts of it that
don't need to be reset for each merge source). */
merge_cmd_baton.force_delete = force_delete;
merge_cmd_baton.dry_run = dry_run;
merge_cmd_baton.record_only = record_only;
merge_cmd_baton.ignore_mergeinfo = ignore_mergeinfo;
merge_cmd_baton.diff_ignore_ancestry = diff_ignore_ancestry;
merge_cmd_baton.same_repos = same_repos;
merge_cmd_baton.mergeinfo_capable = FALSE;
merge_cmd_baton.ctx = ctx;
merge_cmd_baton.reintegrate_merge = reintegrate_merge;
merge_cmd_baton.target = target;
merge_cmd_baton.pool = iterpool;
merge_cmd_baton.merge_options = merge_options;
merge_cmd_baton.diff3_cmd = diff3_cmd;
merge_cmd_baton.ext_patterns = *preserved_exts_str
? svn_cstring_split(preserved_exts_str, "\n\r\t\v ",
FALSE, scratch_pool)
: NULL;
merge_cmd_baton.use_sleep = use_sleep;
/* Do we already know the specific subtrees with mergeinfo we want
to record-only mergeinfo on? */
if (record_only && record_only_paths)
merge_cmd_baton.merged_abspaths = record_only_paths;
else
merge_cmd_baton.merged_abspaths = apr_hash_make(result_pool);
merge_cmd_baton.skipped_abspaths = apr_hash_make(result_pool);
merge_cmd_baton.added_abspaths = apr_hash_make(result_pool);
merge_cmd_baton.tree_conflicted_abspaths = apr_hash_make(result_pool);
{
svn_diff_tree_processor_t *merge_processor;
merge_processor = svn_diff__tree_processor_create(&merge_cmd_baton,
scratch_pool);
merge_processor->dir_opened = merge_dir_opened;
merge_processor->dir_changed = merge_dir_changed;
merge_processor->dir_added = merge_dir_added;
merge_processor->dir_deleted = merge_dir_deleted;
merge_processor->dir_closed = merge_dir_closed;
merge_processor->file_opened = merge_file_opened;
merge_processor->file_changed = merge_file_changed;
merge_processor->file_added = merge_file_added;
merge_processor->file_deleted = merge_file_deleted;
/* Not interested in file_closed() */
merge_processor->node_absent = merge_node_absent;
processor = merge_processor;
}
if (src_session)
{
SVN_ERR(svn_ra_get_session_url(src_session, &old_src_session_url,
scratch_pool));
ra_session1 = src_session;
}
for (i = 0; i < merge_sources->nelts; i++)
{
svn_node_kind_t src1_kind;
merge_source_t *source =
APR_ARRAY_IDX(merge_sources, i, merge_source_t *);
single_range_conflict_report_t *conflicted_range_report;
svn_pool_clear(iterpool);
/* Sanity check: if our left- and right-side merge sources are
the same, there's nothing to here. */
if ((strcmp(source->loc1->url, source->loc2->url) == 0)
&& (source->loc1->rev == source->loc2->rev))
continue;
/* Establish RA sessions to our URLs, reuse where possible. */
SVN_ERR(ensure_ra_session_url(&ra_session1, source->loc1->url,
target->abspath, ctx, scratch_pool));
SVN_ERR(ensure_ra_session_url(&ra_session2, source->loc2->url,
target->abspath, ctx, scratch_pool));
/* Populate the portions of the merge context baton that need to
be reset for each merge source iteration. */
merge_cmd_baton.merge_source = *source;
merge_cmd_baton.implicit_src_gap = NULL;
merge_cmd_baton.conflicted_paths = NULL;
merge_cmd_baton.paths_with_new_mergeinfo = NULL;
merge_cmd_baton.paths_with_deleted_mergeinfo = NULL;
merge_cmd_baton.ra_session1 = ra_session1;
merge_cmd_baton.ra_session2 = ra_session2;
merge_cmd_baton.notify_begin.last_abspath = NULL;
/* Populate the portions of the merge context baton that require
an RA session to set, but shouldn't be reset for each iteration. */
if (! checked_mergeinfo_capability)
{
SVN_ERR(svn_ra_has_capability(ra_session1,
&merge_cmd_baton.mergeinfo_capable,
SVN_RA_CAPABILITY_MERGEINFO,
iterpool));
checked_mergeinfo_capability = TRUE;
}
SVN_ERR(svn_ra_check_path(ra_session1, "", source->loc1->rev,
&src1_kind, iterpool));
/* Run the merge; if there are conflicts, allow the callback to
* resolve them, and if it resolves all of them, then run the
* merge again with the remaining revision range, until it is all
* done. */
do
{
/* Merge as far as possible without resolving any conflicts */
if (src1_kind != svn_node_dir)
{
SVN_ERR(do_file_merge(result_catalog, &conflicted_range_report,
source, target->abspath,
processor,
sources_related,
squelch_mergeinfo_notifications,
&merge_cmd_baton, iterpool, iterpool));
}
else /* Directory */
{
SVN_ERR(do_directory_merge(result_catalog, &conflicted_range_report,
source, target->abspath,
processor,
depth, squelch_mergeinfo_notifications,
&merge_cmd_baton, iterpool, iterpool));
}
/* Give the conflict resolver callback the opportunity to
* resolve any conflicts that were raised. If it resolves all
* of them, go around again to merge the next sub-range (if any). */
if (conflicted_range_report && ctx->conflict_func2 && ! dry_run)
{
svn_boolean_t conflicts_remain;
SVN_ERR(svn_client__resolve_conflicts(
&conflicts_remain, merge_cmd_baton.conflicted_paths,
ctx, iterpool));
if (conflicts_remain)
break;
merge_cmd_baton.conflicted_paths = NULL;
/* Caution: this source is in iterpool */
source = conflicted_range_report->remaining_source;
conflicted_range_report = NULL;
}
else
break;
}
while (source);
/* The final mergeinfo on TARGET_WCPATH may itself elide. */
if (! dry_run)
SVN_ERR(svn_client__elide_mergeinfo(target->abspath, NULL,
ctx, iterpool));
/* If conflicts occurred while merging any but the very last
* range of a multi-pass merge, we raise an error that aborts
* the merge. The user will be asked to resolve conflicts
* before merging subsequent revision ranges. */
if (conflicted_range_report)
{
*conflict_report = conflict_report_create(
target->abspath, conflicted_range_report->conflicted_range,
(i == merge_sources->nelts - 1
&& ! conflicted_range_report->remaining_source),
result_pool);
break;
}
}
if (! *conflict_report || (*conflict_report)->was_last_range)
{
/* Let everyone know we're finished here. */
notify_merge_completed(target->abspath, ctx, iterpool);
}
/* Does the caller want to know what the merge has done? */
if (modified_subtrees)
{
*modified_subtrees =
apr_hash_overlay(result_pool, *modified_subtrees,
merge_cmd_baton.merged_abspaths);
*modified_subtrees =
apr_hash_overlay(result_pool, *modified_subtrees,
merge_cmd_baton.added_abspaths);
*modified_subtrees =
apr_hash_overlay(result_pool, *modified_subtrees,
merge_cmd_baton.skipped_abspaths);
*modified_subtrees =
apr_hash_overlay(result_pool, *modified_subtrees,
merge_cmd_baton.tree_conflicted_abspaths);
}
if (src_session)
SVN_ERR(svn_ra_reparent(src_session, old_src_session_url, iterpool));
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Perform a two-URL merge between URLs which are related, but neither
is a direct ancestor of the other. This first does a real two-URL
merge (unless this is record-only), followed by record-only merges
to represent the changed mergeinfo.
Set *CONFLICT_REPORT to indicate if there were any conflicts, as in
do_merge().
The diff to be merged is between SOURCE->loc1 (in URL1_RA_SESSION1)
and SOURCE->loc2 (in URL2_RA_SESSION2); YCA is their youngest
common ancestor.
SAME_REPOS must be true if and only if the source URLs are in the same
repository as the target working copy.
DIFF_IGNORE_ANCESTRY is as in do_merge().
Other arguments are as in all of the public merge APIs.
*USE_SLEEP will be set TRUE if a sleep is required to ensure timestamp
integrity, *USE_SLEEP will be unchanged if no sleep is required.
SCRATCH_POOL is used for all temporary allocations.
*/
static svn_error_t *
merge_cousins_and_supplement_mergeinfo(conflict_report_t **conflict_report,
svn_boolean_t *use_sleep,
const merge_target_t *target,
svn_ra_session_t *URL1_ra_session,
svn_ra_session_t *URL2_ra_session,
const merge_source_t *source,
const svn_client__pathrev_t *yca,
svn_boolean_t same_repos,
svn_depth_t depth,
svn_boolean_t diff_ignore_ancestry,
svn_boolean_t force_delete,
svn_boolean_t record_only,
svn_boolean_t dry_run,
const apr_array_header_t *merge_options,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_array_header_t *remove_sources, *add_sources;
apr_hash_t *modified_subtrees = NULL;
/* Sure we could use SCRATCH_POOL throughout this function, but since this
is a wrapper around three separate merges we'll create a subpool we can
clear between each of the three. If the merge target has a lot of
subtree mergeinfo, then this will help keep memory use in check. */
apr_pool_t *subpool = svn_pool_create(scratch_pool);
assert(session_url_is(URL1_ra_session, source->loc1->url, scratch_pool));
assert(session_url_is(URL2_ra_session, source->loc2->url, scratch_pool));
SVN_ERR_ASSERT(svn_dirent_is_absolute(target->abspath));
SVN_ERR_ASSERT(! source->ancestral);
SVN_ERR(normalize_merge_sources_internal(
&remove_sources, source->loc1,
svn_rangelist__initialize(source->loc1->rev, yca->rev, TRUE,
scratch_pool),
URL1_ra_session, ctx, scratch_pool, subpool));
SVN_ERR(normalize_merge_sources_internal(
&add_sources, source->loc2,
svn_rangelist__initialize(yca->rev, source->loc2->rev, TRUE,
scratch_pool),
URL2_ra_session, ctx, scratch_pool, subpool));
*conflict_report = NULL;
/* If this isn't a record-only merge, we'll first do a stupid
point-to-point merge... */
if (! record_only)
{
apr_array_header_t *faux_sources =
apr_array_make(scratch_pool, 1, sizeof(merge_source_t *));
modified_subtrees = apr_hash_make(scratch_pool);
APR_ARRAY_PUSH(faux_sources, const merge_source_t *) = source;
SVN_ERR(do_merge(&modified_subtrees, NULL, conflict_report, use_sleep,
faux_sources, target,
URL1_ra_session, TRUE, same_repos,
FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry,
force_delete, dry_run, FALSE, NULL, TRUE,
FALSE, depth, merge_options, ctx,
scratch_pool, subpool));
if (*conflict_report)
{
*conflict_report = conflict_report_dup(*conflict_report, result_pool);
if (! (*conflict_report)->was_last_range)
return SVN_NO_ERROR;
}
}
else if (! same_repos)
{
return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
_("Merge from foreign repository is not "
"compatible with mergeinfo modification"));
}
/* ... and now, if we're doing the mergeinfo thang, we execute a
pair of record-only merges using the real sources we've
calculated.
Issue #3648: We don't actually perform these two record-only merges
on the WC at first, but rather see what each would do and store that
in two mergeinfo catalogs. We then merge the catalogs together and
then record the result in the WC. This prevents the second record
only merge from removing legitimate mergeinfo history, from the same
source, that was made in prior merges. */
if (same_repos && !dry_run)
{
svn_mergeinfo_catalog_t add_result_catalog =
apr_hash_make(scratch_pool);
svn_mergeinfo_catalog_t remove_result_catalog =
apr_hash_make(scratch_pool);
notify_mergeinfo_recording(target->abspath, NULL, ctx, scratch_pool);
svn_pool_clear(subpool);
SVN_ERR(do_merge(NULL, add_result_catalog, conflict_report, use_sleep,
add_sources, target,
URL1_ra_session, TRUE, same_repos,
FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry,
force_delete, dry_run, TRUE,
modified_subtrees, TRUE,
TRUE, depth, merge_options, ctx,
scratch_pool, subpool));
if (*conflict_report)
{
*conflict_report = conflict_report_dup(*conflict_report, result_pool);
if (! (*conflict_report)->was_last_range)
return SVN_NO_ERROR;
}
svn_pool_clear(subpool);
SVN_ERR(do_merge(NULL, remove_result_catalog, conflict_report, use_sleep,
remove_sources, target,
URL1_ra_session, TRUE, same_repos,
FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry,
force_delete, dry_run, TRUE,
modified_subtrees, TRUE,
TRUE, depth, merge_options, ctx,
scratch_pool, subpool));
if (*conflict_report)
{
*conflict_report = conflict_report_dup(*conflict_report, result_pool);
if (! (*conflict_report)->was_last_range)
return SVN_NO_ERROR;
}
SVN_ERR(svn_mergeinfo_catalog_merge(add_result_catalog,
remove_result_catalog,
scratch_pool, scratch_pool));
SVN_ERR(svn_client__record_wc_mergeinfo_catalog(add_result_catalog,
ctx, scratch_pool));
}
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
/* Perform checks to determine whether the working copy at TARGET_ABSPATH
* can safely be used as a merge target. Checks are performed according to
* the ALLOW_MIXED_REV, ALLOW_LOCAL_MODS, and ALLOW_SWITCHED_SUBTREES
* parameters. If any checks fail, raise SVN_ERR_CLIENT_NOT_READY_TO_MERGE.
*
* E.g. if all the ALLOW_* parameters are FALSE, TARGET_ABSPATH must
* be a single-revision, pristine, unswitched working copy.
* In other words, it must reflect a subtree of the repository as found
* at single revision -- although sparse checkouts are permitted. */
static svn_error_t *
ensure_wc_is_suitable_merge_target(const char *target_abspath,
svn_client_ctx_t *ctx,
svn_boolean_t allow_mixed_rev,
svn_boolean_t allow_local_mods,
svn_boolean_t allow_switched_subtrees,
apr_pool_t *scratch_pool)
{
svn_node_kind_t target_kind;
/* Check the target exists. */
SVN_ERR(svn_io_check_path(target_abspath, &target_kind, scratch_pool));
if (target_kind == svn_node_none)
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("Path '%s' does not exist"),
svn_dirent_local_style(target_abspath,
scratch_pool));
SVN_ERR(svn_wc_read_kind2(&target_kind, ctx->wc_ctx, target_abspath,
FALSE, FALSE, scratch_pool));
if (target_kind != svn_node_dir && target_kind != svn_node_file)
return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
_("Merge target '%s' does not exist in the "
"working copy"), target_abspath);
/* Perform the mixed-revision check first because it's the cheapest one. */
if (! allow_mixed_rev)
{
svn_revnum_t min_rev;
svn_revnum_t max_rev;
SVN_ERR(svn_client_min_max_revisions(&min_rev, &max_rev, target_abspath,
FALSE, ctx, scratch_pool));
if (!(SVN_IS_VALID_REVNUM(min_rev) && SVN_IS_VALID_REVNUM(max_rev)))
{
svn_boolean_t is_added;
/* Allow merge into added nodes. */
SVN_ERR(svn_wc__node_is_added(&is_added, ctx->wc_ctx, target_abspath,
scratch_pool));
if (is_added)
return SVN_NO_ERROR;
else
return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
_("Cannot determine revision of working "
"copy"));
}
if (min_rev != max_rev)
return svn_error_createf(SVN_ERR_CLIENT_MERGE_UPDATE_REQUIRED, NULL,
_("Cannot merge into mixed-revision working "
"copy [%ld:%ld]; try updating first"),
min_rev, max_rev);
}
/* Next, check for switched subtrees. */
if (! allow_switched_subtrees)
{
svn_boolean_t is_switched;
SVN_ERR(svn_wc__has_switched_subtrees(&is_switched, ctx->wc_ctx,
target_abspath, NULL,
scratch_pool));
if (is_switched)
return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
_("Cannot merge into a working copy "
"with a switched subtree"));
}
/* This is the most expensive check, so it is performed last.*/
if (! allow_local_mods)
{
svn_boolean_t is_modified;
SVN_ERR(svn_wc__has_local_mods(&is_modified, ctx->wc_ctx,
target_abspath,
ctx->cancel_func,
ctx->cancel_baton,
scratch_pool));
if (is_modified)
return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
_("Cannot merge into a working copy "
"that has local modifications"));
}
return SVN_NO_ERROR;
}
/* Throw an error if PATH_OR_URL is a path and REVISION isn't a repository
* revision. */
static svn_error_t *
ensure_wc_path_has_repo_revision(const char *path_or_url,
const svn_opt_revision_t *revision,
apr_pool_t *scratch_pool)
{
if (revision->kind != svn_opt_revision_number
&& revision->kind != svn_opt_revision_date
&& revision->kind != svn_opt_revision_head
&& ! svn_path_is_url(path_or_url))
return svn_error_createf(
SVN_ERR_CLIENT_BAD_REVISION, NULL,
_("Invalid merge source '%s'; a working copy path can only be "
"used with a repository revision (a number, a date, or head)"),
svn_dirent_local_style(path_or_url, scratch_pool));
return SVN_NO_ERROR;
}
/* "Open" the target WC for a merge. That means:
* - find out its exact repository location
* - check the WC for suitability (throw an error if unsuitable)
*
* Set *TARGET_P to a new, fully initialized, target description structure.
*
* ALLOW_MIXED_REV, ALLOW_LOCAL_MODS, ALLOW_SWITCHED_SUBTREES determine
* whether the WC is deemed suitable; see ensure_wc_is_suitable_merge_target()
* for details.
*
* If the node is locally added, the rev and URL will be null/invalid. Some
* kinds of merge can use such a target; others can't.
*/
static svn_error_t *
open_target_wc(merge_target_t **target_p,
const char *wc_abspath,
svn_boolean_t allow_mixed_rev,
svn_boolean_t allow_local_mods,
svn_boolean_t allow_switched_subtrees,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
merge_target_t *target = apr_palloc(result_pool, sizeof(*target));
svn_client__pathrev_t *origin;
target->abspath = apr_pstrdup(result_pool, wc_abspath);
SVN_ERR(svn_client__wc_node_get_origin(&origin, wc_abspath, ctx,
result_pool, scratch_pool));
if (origin)
{
target->loc = *origin;
}
else
{
svn_error_t *err;
/* The node has no location in the repository. It's unversioned or
* locally added or locally deleted.
*
* If it's locally added or deleted, find the repository root
* URL and UUID anyway, and leave the node URL and revision as NULL
* and INVALID. If it's unversioned, this will throw an error. */
err = svn_wc__node_get_repos_info(NULL, NULL,
&target->loc.repos_root_url,
&target->loc.repos_uuid,
ctx->wc_ctx, wc_abspath,
result_pool, scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND
&& err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY
&& err->apr_err != SVN_ERR_WC_UPGRADE_REQUIRED)
return svn_error_trace(err);
return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, err,
_("Merge target '%s' does not exist in the "
"working copy"),
svn_dirent_local_style(wc_abspath,
scratch_pool));
}
target->loc.rev = SVN_INVALID_REVNUM;
target->loc.url = NULL;
}
SVN_ERR(ensure_wc_is_suitable_merge_target(
wc_abspath, ctx,
allow_mixed_rev, allow_local_mods, allow_switched_subtrees,
scratch_pool));
*target_p = target;
return SVN_NO_ERROR;
}
/*-----------------------------------------------------------------------*/
/*** Public APIs ***/
/* The body of svn_client_merge5(), which see for details.
*
* If SOURCE1 @ REVISION1 is related to SOURCE2 @ REVISION2 then use merge
* tracking (subject to other constraints -- see HONOR_MERGEINFO());
* otherwise disable merge tracking.
*
* IGNORE_MERGEINFO and DIFF_IGNORE_ANCESTRY are as in do_merge().
*/
static svn_error_t *
merge_locked(conflict_report_t **conflict_report,
const char *source1,
const svn_opt_revision_t *revision1,
const char *source2,
const svn_opt_revision_t *revision2,
const char *target_abspath,
svn_depth_t depth,
svn_boolean_t ignore_mergeinfo,
svn_boolean_t diff_ignore_ancestry,
svn_boolean_t force_delete,
svn_boolean_t record_only,
svn_boolean_t dry_run,
svn_boolean_t allow_mixed_rev,
const apr_array_header_t *merge_options,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
merge_target_t *target;
svn_client__pathrev_t *source1_loc, *source2_loc;
svn_boolean_t sources_related = FALSE;
svn_ra_session_t *ra_session1, *ra_session2;
apr_array_header_t *merge_sources;
svn_error_t *err;
svn_boolean_t use_sleep = FALSE;
svn_client__pathrev_t *yca = NULL;
apr_pool_t *sesspool;
svn_boolean_t same_repos;
/* ### FIXME: This function really ought to do a history check on
the left and right sides of the merge source, and -- if one is an
ancestor of the other -- just call svn_client_merge_peg3() with
the appropriate args. */
SVN_ERR(open_target_wc(&target, target_abspath,
allow_mixed_rev, TRUE, TRUE,
ctx, scratch_pool, scratch_pool));
/* Open RA sessions to both sides of our merge source, and resolve URLs
* and revisions. */
sesspool = svn_pool_create(scratch_pool);
SVN_ERR(svn_client__ra_session_from_path2(
&ra_session1, &source1_loc,
source1, NULL, revision1, revision1, ctx, sesspool));
SVN_ERR(svn_client__ra_session_from_path2(
&ra_session2, &source2_loc,
source2, NULL, revision2, revision2, ctx, sesspool));
/* We can't do a diff between different repositories. */
/* ### We should also insist that the root URLs of the two sources match,
* as we are only carrying around a single source-repos-root from now
* on, and URL calculations will go wrong if they differ.
* Alternatively, teach the code to cope with differing root URLs. */
SVN_ERR(check_same_repos(source1_loc, source1_loc->url,
source2_loc, source2_loc->url,
FALSE /* strict_urls */, scratch_pool));
/* Do our working copy and sources come from the same repository? */
same_repos = is_same_repos(&target->loc, source1_loc, TRUE /* strict_urls */);
/* Unless we're ignoring ancestry, see if the two sources are related. */
if (! ignore_mergeinfo)
SVN_ERR(svn_client__get_youngest_common_ancestor(
&yca, source1_loc, source2_loc, ra_session1, ctx,
scratch_pool, scratch_pool));
/* Check for a youngest common ancestor. If we have one, we'll be
doing merge tracking.
So, given a requested merge of the differences between A and
B, and a common ancestor of C, we will find ourselves in one of
four positions, and four different approaches:
A == B == C there's nothing to merge
A == C != B we merge the changes between A (or C) and B
B == C != A we merge the changes between B (or C) and A
A != B != C we merge the changes between A and B without
merge recording, then record-only two merges:
from A to C, and from C to B
*/
if (yca)
{
/* Note that our merge sources are related. */
sources_related = TRUE;
/* If the common ancestor matches the right side of our merge,
then we only need to reverse-merge the left side. */
if ((strcmp(yca->url, source2_loc->url) == 0)
&& (yca->rev == source2_loc->rev))
{
SVN_ERR(normalize_merge_sources_internal(
&merge_sources, source1_loc,
svn_rangelist__initialize(source1_loc->rev, yca->rev, TRUE,
scratch_pool),
ra_session1, ctx, scratch_pool, scratch_pool));
}
/* If the common ancestor matches the left side of our merge,
then we only need to merge the right side. */
else if ((strcmp(yca->url, source1_loc->url) == 0)
&& (yca->rev == source1_loc->rev))
{
SVN_ERR(normalize_merge_sources_internal(
&merge_sources, source2_loc,
svn_rangelist__initialize(yca->rev, source2_loc->rev, TRUE,
scratch_pool),
ra_session2, ctx, scratch_pool, scratch_pool));
}
/* And otherwise, we need to do both: reverse merge the left
side, and merge the right. */
else
{
merge_source_t source;
source.loc1 = source1_loc;
source.loc2 = source2_loc;
source.ancestral = FALSE;
err = merge_cousins_and_supplement_mergeinfo(conflict_report,
&use_sleep,
target,
ra_session1,
ra_session2,
&source,
yca,
same_repos,
depth,
diff_ignore_ancestry,
force_delete,
record_only, dry_run,
merge_options,
ctx,
result_pool,
scratch_pool);
/* Close our temporary RA sessions (this could've happened
after the second call to normalize_merge_sources() inside
the merge_cousins_and_supplement_mergeinfo() routine). */
svn_pool_destroy(sesspool);
if (use_sleep)
svn_io_sleep_for_timestamps(target->abspath, scratch_pool);
SVN_ERR(err);
return SVN_NO_ERROR;
}
}
else
{
/* Build a single-item merge_source_t array. */
merge_sources = apr_array_make(scratch_pool, 1, sizeof(merge_source_t *));
APR_ARRAY_PUSH(merge_sources, merge_source_t *)
= merge_source_create(source1_loc, source2_loc, FALSE, scratch_pool);
}
err = do_merge(NULL, NULL, conflict_report, &use_sleep,
merge_sources, target,
ra_session1, sources_related, same_repos,
ignore_mergeinfo, diff_ignore_ancestry, force_delete, dry_run,
record_only, NULL, FALSE, FALSE, depth, merge_options,
ctx, result_pool, scratch_pool);
/* Close our temporary RA sessions. */
svn_pool_destroy(sesspool);
if (use_sleep)
svn_io_sleep_for_timestamps(target->abspath, scratch_pool);
SVN_ERR(err);
return SVN_NO_ERROR;
}
/* Set *TARGET_ABSPATH to the absolute path of, and *LOCK_ABSPATH to
the absolute path to lock for, TARGET_WCPATH. */
static svn_error_t *
get_target_and_lock_abspath(const char **target_abspath,
const char **lock_abspath,
const char *target_wcpath,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool)
{
svn_node_kind_t kind;
SVN_ERR(svn_dirent_get_absolute(target_abspath, target_wcpath,
result_pool));
SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, *target_abspath,
FALSE, FALSE, result_pool));
if (kind == svn_node_dir)
*lock_abspath = *target_abspath;
else
*lock_abspath = svn_dirent_dirname(*target_abspath, result_pool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_client_merge5(const char *source1,
const svn_opt_revision_t *revision1,
const char *source2,
const svn_opt_revision_t *revision2,
const char *target_wcpath,
svn_depth_t depth,
svn_boolean_t ignore_mergeinfo,
svn_boolean_t diff_ignore_ancestry,
svn_boolean_t force_delete,
svn_boolean_t record_only,
svn_boolean_t dry_run,
svn_boolean_t allow_mixed_rev,
const apr_array_header_t *merge_options,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
const char *target_abspath, *lock_abspath;
conflict_report_t *conflict_report;
/* Sanity check our input -- we require specified revisions,
* and either 2 paths or 2 URLs. */
if ((revision1->kind == svn_opt_revision_unspecified)
|| (revision2->kind == svn_opt_revision_unspecified))
return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
_("Not all required revisions are specified"));
if (svn_path_is_url(source1) != svn_path_is_url(source2))
return svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL,
_("Merge sources must both be "
"either paths or URLs"));
/* A WC path must be used with a repository revision, as we can't
* (currently) use the WC itself as a source, we can only read the URL
* from it and use that. */
SVN_ERR(ensure_wc_path_has_repo_revision(source1, revision1, pool));
SVN_ERR(ensure_wc_path_has_repo_revision(source2, revision2, pool));
SVN_ERR(get_target_and_lock_abspath(&target_abspath, &lock_abspath,
target_wcpath, ctx, pool));
if (!dry_run)
SVN_WC__CALL_WITH_WRITE_LOCK(
merge_locked(&conflict_report,
source1, revision1, source2, revision2,
target_abspath, depth, ignore_mergeinfo,
diff_ignore_ancestry,
force_delete, record_only, dry_run,
allow_mixed_rev, merge_options, ctx, pool, pool),
ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
else
SVN_ERR(merge_locked(&conflict_report,
source1, revision1, source2, revision2,
target_abspath, depth, ignore_mergeinfo,
diff_ignore_ancestry,
force_delete, record_only, dry_run,
allow_mixed_rev, merge_options, ctx, pool, pool));
SVN_ERR(make_merge_conflict_error(conflict_report, pool));
return SVN_NO_ERROR;
}
/* Check if mergeinfo for a given path is described explicitly or via
inheritance in a mergeinfo catalog.
If REPOS_REL_PATH exists in CATALOG and has mergeinfo containing
MERGEINFO, then set *IN_CATALOG to TRUE. If REPOS_REL_PATH does
not exist in CATALOG, then find its nearest parent which does exist.
If the mergeinfo REPOS_REL_PATH would inherit from that parent
contains MERGEINFO then set *IN_CATALOG to TRUE. Set *IN_CATALOG
to FALSE in all other cases.
Set *CAT_KEY_PATH to the key path in CATALOG for REPOS_REL_PATH's
explicit or inherited mergeinfo. If no explicit or inherited mergeinfo
is found for REPOS_REL_PATH then set *CAT_KEY_PATH to NULL.
User RESULT_POOL to allocate *CAT_KEY_PATH. Use SCRATCH_POOL for
temporary allocations. */
static svn_error_t *
mergeinfo_in_catalog(svn_boolean_t *in_catalog,
const char **cat_key_path,
const char *repos_rel_path,
svn_mergeinfo_t mergeinfo,
svn_mergeinfo_catalog_t catalog,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *walk_path = NULL;
*in_catalog = FALSE;
*cat_key_path = NULL;
if (mergeinfo && catalog && apr_hash_count(catalog))
{
const char *path = repos_rel_path;
/* Start with the assumption there is no explicit or inherited
mergeinfo for REPOS_REL_PATH in CATALOG. */
svn_mergeinfo_t mergeinfo_in_cat = NULL;
while (1)
{
mergeinfo_in_cat = svn_hash_gets(catalog, path);
if (mergeinfo_in_cat) /* Found it! */
{
*cat_key_path = apr_pstrdup(result_pool, path);
break;
}
else /* Look for inherited mergeinfo. */
{
walk_path = svn_relpath_join(svn_relpath_basename(path,
scratch_pool),
walk_path ? walk_path : "",
scratch_pool);
path = svn_relpath_dirname(path, scratch_pool);
if (path[0] == '\0') /* No mergeinfo to inherit. */
break;
}
}
if (mergeinfo_in_cat)
{
if (walk_path)
SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(&mergeinfo_in_cat,
mergeinfo_in_cat,
walk_path,
scratch_pool,
scratch_pool));
SVN_ERR(svn_mergeinfo_intersect2(&mergeinfo_in_cat,
mergeinfo_in_cat, mergeinfo,
TRUE,
scratch_pool, scratch_pool));
SVN_ERR(svn_mergeinfo__equals(in_catalog, mergeinfo_in_cat,
mergeinfo, TRUE, scratch_pool));
}
}
return SVN_NO_ERROR;
}
/* A svn_log_entry_receiver_t baton for log_find_operative_revs(). */
typedef struct log_find_operative_baton_t
{
/* The catalog of explicit mergeinfo on a reintegrate source. */
svn_mergeinfo_catalog_t merged_catalog;
/* The catalog of unmerged history from the reintegrate target to
the source which we will create. Allocated in RESULT_POOL. */
svn_mergeinfo_catalog_t unmerged_catalog;
/* The repository absolute path of the reintegrate target. */
const char *target_fspath;
/* The path of the reintegrate source relative to the repository root. */
const char *source_repos_rel_path;
apr_pool_t *result_pool;
} log_find_operative_baton_t;
/* A svn_log_entry_receiver_t callback for find_unsynced_ranges(). */
static svn_error_t *
log_find_operative_revs(void *baton,
svn_log_entry_t *log_entry,
apr_pool_t *pool)
{
log_find_operative_baton_t *log_baton = baton;
apr_hash_index_t *hi;
svn_revnum_t revision;
/* It's possible that authz restrictions on the merge source prevent us
from knowing about any of the changes for LOG_ENTRY->REVISION. */
if (!log_entry->changed_paths2)
return SVN_NO_ERROR;
revision = log_entry->revision;
for (hi = apr_hash_first(pool, log_entry->changed_paths2);
hi;
hi = apr_hash_next(hi))
{
const char *subtree_missing_this_rev;
const char *path = svn__apr_hash_index_key(hi);
const char *rel_path;
const char *source_rel_path;
svn_boolean_t in_catalog;
svn_mergeinfo_t log_entry_as_mergeinfo;
rel_path = svn_fspath__skip_ancestor(log_baton->target_fspath, path);
/* Easy out: The path is not within the tree of interest. */
if (rel_path == NULL)
continue;
source_rel_path = svn_relpath_join(log_baton->source_repos_rel_path,
rel_path, pool);
SVN_ERR(svn_mergeinfo_parse(&log_entry_as_mergeinfo,
apr_psprintf(pool, "%s:%ld",
path, revision),
pool));
SVN_ERR(mergeinfo_in_catalog(&in_catalog, &subtree_missing_this_rev,
source_rel_path, log_entry_as_mergeinfo,
log_baton->merged_catalog,
pool, pool));
if (!in_catalog)
{
svn_mergeinfo_t unmerged_for_key;
const char *suffix, *missing_path;
/* If there is no mergeinfo on the source tree we'll say
the "subtree" missing this revision is the root of the
source. */
if (!subtree_missing_this_rev)
subtree_missing_this_rev = log_baton->source_repos_rel_path;
suffix = svn_relpath_skip_ancestor(subtree_missing_this_rev,
source_rel_path);
if (suffix && suffix[0] != '\0')
{
missing_path = apr_pstrmemdup(pool, path,
strlen(path) - strlen(suffix) - 1);
}
else
{
missing_path = path;
}
SVN_ERR(svn_mergeinfo_parse(&log_entry_as_mergeinfo,
apr_psprintf(pool, "%s:%ld",
missing_path, revision),
log_baton->result_pool));
unmerged_for_key = svn_hash_gets(log_baton->unmerged_catalog,
subtree_missing_this_rev);
if (unmerged_for_key)
{
SVN_ERR(svn_mergeinfo_merge2(unmerged_for_key,
log_entry_as_mergeinfo,
log_baton->result_pool,
pool));
}
else
{
svn_hash_sets(log_baton->unmerged_catalog,
apr_pstrdup(log_baton->result_pool,
subtree_missing_this_rev),
log_entry_as_mergeinfo);
}
}
}
return SVN_NO_ERROR;
}
/* Determine if the mergeinfo on a reintegrate source SOURCE_LOC,
reflects that the source is fully synced with the reintegrate target
TARGET_LOC, even if a naive interpretation of the source's
mergeinfo says otherwise -- See issue #3577.
UNMERGED_CATALOG represents the history (as mergeinfo) from
TARGET_LOC that is not represented in SOURCE_LOC's
explicit/inherited mergeinfo as represented by MERGED_CATALOG.
MERGED_CATALOG may be empty if the source has no explicit or inherited
mergeinfo.
Check that all of the unmerged revisions in UNMERGED_CATALOG's
mergeinfos are "phantoms", that is, one of the following conditions holds:
1) The revision affects no corresponding paths in SOURCE_LOC.
2) The revision affects corresponding paths in SOURCE_LOC,
but based on the mergeinfo in MERGED_CATALOG, the change was
previously merged.
Make a deep copy, allocated in RESULT_POOL, of any portions of
UNMERGED_CATALOG that are not phantoms, to TRUE_UNMERGED_CATALOG.
Note: The keys in all mergeinfo catalogs used here are relative to the
root of the repository.
RA_SESSION is an RA session open to the repository of TARGET_LOC; it may
be temporarily reparented within this function.
Use SCRATCH_POOL for all temporary allocations. */
static svn_error_t *
find_unsynced_ranges(const svn_client__pathrev_t *source_loc,
const svn_client__pathrev_t *target_loc,
svn_mergeinfo_catalog_t unmerged_catalog,
svn_mergeinfo_catalog_t merged_catalog,
svn_mergeinfo_catalog_t true_unmerged_catalog,
svn_ra_session_t *ra_session,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_rangelist_t *potentially_unmerged_ranges = NULL;
/* Convert all the unmerged history to a rangelist. */
if (apr_hash_count(unmerged_catalog))
{
apr_hash_index_t *hi_catalog;
potentially_unmerged_ranges =
apr_array_make(scratch_pool, 1, sizeof(svn_merge_range_t *));
for (hi_catalog = apr_hash_first(scratch_pool, unmerged_catalog);
hi_catalog;
hi_catalog = apr_hash_next(hi_catalog))
{
svn_mergeinfo_t mergeinfo = svn__apr_hash_index_val(hi_catalog);
SVN_ERR(svn_rangelist__merge_many(potentially_unmerged_ranges,
mergeinfo,
scratch_pool, scratch_pool));
}
}
/* Find any unmerged revisions which both affect the source and
are not yet merged to it. */
if (potentially_unmerged_ranges)
{
svn_revnum_t oldest_rev =
(APR_ARRAY_IDX(potentially_unmerged_ranges,
0,
svn_merge_range_t *))->start + 1;
svn_revnum_t youngest_rev =
(APR_ARRAY_IDX(potentially_unmerged_ranges,
potentially_unmerged_ranges->nelts - 1,
svn_merge_range_t *))->end;
log_find_operative_baton_t log_baton;
const char *old_session_url;
svn_error_t *err;
log_baton.merged_catalog = merged_catalog;
log_baton.unmerged_catalog = true_unmerged_catalog;
log_baton.source_repos_rel_path
= svn_client__pathrev_relpath(source_loc, scratch_pool);
log_baton.target_fspath
= svn_client__pathrev_fspath(target_loc, scratch_pool);
log_baton.result_pool = result_pool;
SVN_ERR(svn_client__ensure_ra_session_url(
&old_session_url, ra_session, target_loc->url, scratch_pool));
err = get_log(ra_session, "", youngest_rev, oldest_rev,
TRUE, /* discover_changed_paths */
log_find_operative_revs, &log_baton,
scratch_pool);
SVN_ERR(svn_error_compose_create(
err, svn_ra_reparent(ra_session, old_session_url, scratch_pool)));
}
return SVN_NO_ERROR;
}
/* Find the youngest revision that has been merged from target to source.
*
* If any location in TARGET_HISTORY_AS_MERGEINFO is mentioned in
* SOURCE_MERGEINFO, then we know that at least one merge was done from the
* target to the source. In that case, set *YOUNGEST_MERGED_REV to the
* youngest revision of that intersection (unless *YOUNGEST_MERGED_REV is
* already younger than that). Otherwise, leave *YOUNGEST_MERGED_REV alone.
*/
static svn_error_t *
find_youngest_merged_rev(svn_revnum_t *youngest_merged_rev,
svn_mergeinfo_t target_history_as_mergeinfo,
svn_mergeinfo_t source_mergeinfo,
apr_pool_t *scratch_pool)
{
svn_mergeinfo_t explicit_source_target_history_intersection;
SVN_ERR(svn_mergeinfo_intersect2(
&explicit_source_target_history_intersection,
source_mergeinfo, target_history_as_mergeinfo, TRUE,
scratch_pool, scratch_pool));
if (apr_hash_count(explicit_source_target_history_intersection))
{
svn_revnum_t old_rev, young_rev;
/* Keep track of the youngest revision merged from target to source. */
SVN_ERR(svn_mergeinfo__get_range_endpoints(
&young_rev, &old_rev,
explicit_source_target_history_intersection, scratch_pool));
if (!SVN_IS_VALID_REVNUM(*youngest_merged_rev)
|| (young_rev > *youngest_merged_rev))
*youngest_merged_rev = young_rev;
}
return SVN_NO_ERROR;
}
/* Set *FILTERED_MERGEINFO_P to the parts of TARGET_HISTORY_AS_MERGEINFO
* that are not present in the source branch.
*
* SOURCE_MERGEINFO is the explicit or inherited mergeinfo of the source
* branch SOURCE_PATHREV. Extend SOURCE_MERGEINFO, modifying it in
* place, to include the natural history (implicit mergeinfo) of
* SOURCE_PATHREV. ### But make these additions in SCRATCH_POOL.
*
* SOURCE_RA_SESSION is an RA session open to the repository containing
* SOURCE_PATHREV; it may be temporarily reparented within this function.
*
* ### [JAF] This function is named '..._subroutine' simply because I
* factored it out based on code similarity, without knowing what it's
* purpose is. We should clarify its purpose and choose a better name.
*/
static svn_error_t *
find_unmerged_mergeinfo_subroutine(svn_mergeinfo_t *filtered_mergeinfo_p,
svn_mergeinfo_t target_history_as_mergeinfo,
svn_mergeinfo_t source_mergeinfo,
const svn_client__pathrev_t *source_pathrev,
svn_ra_session_t *source_ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_mergeinfo_t source_history_as_mergeinfo;
/* Get the source path's natural history and merge it into source
path's explicit or inherited mergeinfo. */
SVN_ERR(svn_client__get_history_as_mergeinfo(
&source_history_as_mergeinfo, NULL /* has_rev_zero_history */,
source_pathrev, source_pathrev->rev, SVN_INVALID_REVNUM,
source_ra_session, ctx, scratch_pool));
SVN_ERR(svn_mergeinfo_merge2(source_mergeinfo,
source_history_as_mergeinfo,
scratch_pool, scratch_pool));
/* Now source_mergeinfo represents everything we know about
source_path's history. Now we need to know what part, if any, of the
corresponding target's history is *not* part of source_path's total
history; because it is neither shared history nor was it ever merged
from the target to the source. */
SVN_ERR(svn_mergeinfo_remove2(filtered_mergeinfo_p,
source_mergeinfo,
target_history_as_mergeinfo, TRUE,
result_pool, scratch_pool));
return SVN_NO_ERROR;
}
/* Helper for calculate_left_hand_side() which produces a mergeinfo catalog
describing what parts of of the reintegrate target have not previously been
merged to the reintegrate source.
SOURCE_CATALOG is the collection of explicit mergeinfo on SOURCE_LOC and
all its children, i.e. the mergeinfo catalog for the reintegrate source.
TARGET_HISTORY_HASH is a hash of (const char *) paths mapped to
svn_mergeinfo_t representing the location history. Each of these
path keys represent a path in the reintegrate target, relative to the
repository root, which has explicit mergeinfo and/or is the reintegrate
target itself. The svn_mergeinfo_t's contain the natural history of each
path@TARGET_REV. Effectively this is the mergeinfo catalog on the
reintegrate target.
YC_ANCESTOR_REV is the revision of the youngest common ancestor of the
reintegrate source and the reintegrate target.
SOURCE_LOC is the reintegrate source.
SOURCE_RA_SESSION is a session opened to the URL of SOURCE_LOC
and TARGET_RA_SESSION is open to TARGET->loc.url.
For each entry in TARGET_HISTORY_HASH check that the history it
represents is contained in either the explicit mergeinfo for the
corresponding path in SOURCE_CATALOG, the corresponding path's inherited
mergeinfo (if no explicit mergeinfo for the path is found in
SOURCE_CATALOG), or the corresponding path's natural history. Populate
*UNMERGED_TO_SOURCE_CATALOG with the corresponding source paths mapped to
the mergeinfo from the target's natural history which is *not* found. Also
include any mergeinfo from SOURCE_CATALOG which explicitly describes the
target's history but for which *no* entry was found in
TARGET_HISTORY_HASH.
If no part of TARGET_HISTORY_HASH is found in SOURCE_CATALOG set
*YOUNGEST_MERGED_REV to SVN_INVALID_REVNUM; otherwise set it to the youngest
revision previously merged from the target to the source, and filter
*UNMERGED_TO_SOURCE_CATALOG so that it contains no ranges greater than
*YOUNGEST_MERGED_REV.
*UNMERGED_TO_SOURCE_CATALOG is (deeply) allocated in RESULT_POOL.
SCRATCH_POOL is used for all temporary allocations. */
static svn_error_t *
find_unmerged_mergeinfo(svn_mergeinfo_catalog_t *unmerged_to_source_catalog,
svn_revnum_t *youngest_merged_rev,
svn_revnum_t yc_ancestor_rev,
svn_mergeinfo_catalog_t source_catalog,
apr_hash_t *target_history_hash,
const svn_client__pathrev_t *source_loc,
const merge_target_t *target,
svn_ra_session_t *source_ra_session,
svn_ra_session_t *target_ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *source_repos_rel_path
= svn_client__pathrev_relpath(source_loc, scratch_pool);
const char *target_repos_rel_path
= svn_client__pathrev_relpath(&target->loc, scratch_pool);
apr_hash_index_t *hi;
svn_mergeinfo_catalog_t new_catalog = apr_hash_make(result_pool);
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
assert(session_url_is(source_ra_session, source_loc->url, scratch_pool));
assert(session_url_is(target_ra_session, target->loc.url, scratch_pool));
*youngest_merged_rev = SVN_INVALID_REVNUM;
/* Examine the natural history of each path in the reintegrate target
with explicit mergeinfo. */
for (hi = apr_hash_first(scratch_pool, target_history_hash);
hi;
hi = apr_hash_next(hi))
{
const char *target_path = svn__apr_hash_index_key(hi);
svn_mergeinfo_t target_history_as_mergeinfo = svn__apr_hash_index_val(hi);
const char *path_rel_to_session
= svn_relpath_skip_ancestor(target_repos_rel_path, target_path);
const char *source_path;
svn_client__pathrev_t *source_pathrev;
svn_mergeinfo_t source_mergeinfo, filtered_mergeinfo;
svn_pool_clear(iterpool);
source_path = svn_relpath_join(source_repos_rel_path,
path_rel_to_session, iterpool);
source_pathrev = svn_client__pathrev_join_relpath(
source_loc, path_rel_to_session, iterpool);
/* Remove any target history that is also part of the source's history,
i.e. their common ancestry. By definition this has already been
"merged" from the target to the source. If the source has explicit
self referential mergeinfo it would intersect with the target's
history below, making it appear that some merges had been done from
the target to the source, when this might not actually be the case. */
SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(
&target_history_as_mergeinfo, target_history_as_mergeinfo,
source_loc->rev, yc_ancestor_rev, TRUE, iterpool, iterpool));
/* Look for any explicit mergeinfo on the source path corresponding to
the target path. If we find any remove that from SOURCE_CATALOG.
When this iteration over TARGET_HISTORY_HASH is complete all that
should be left in SOURCE_CATALOG are subtrees that have explicit
mergeinfo on the reintegrate source where there is no corresponding
explicit mergeinfo on the reintegrate target. */
source_mergeinfo = svn_hash_gets(source_catalog, source_path);
if (source_mergeinfo)
{
svn_hash_sets(source_catalog, source_path, NULL);
SVN_ERR(find_youngest_merged_rev(youngest_merged_rev,
target_history_as_mergeinfo,
source_mergeinfo,
iterpool));
}
else
{
/* There is no mergeinfo on source_path *or* source_path doesn't
exist at all. If simply doesn't exist we can ignore it
altogether. */
svn_node_kind_t kind;
SVN_ERR(svn_ra_check_path(source_ra_session,
path_rel_to_session,
source_loc->rev, &kind, iterpool));
if (kind == svn_node_none)
continue;
/* Else source_path does exist though it has no explicit mergeinfo.
Find its inherited mergeinfo. If it doesn't have any then simply
set source_mergeinfo to an empty hash. */
SVN_ERR(svn_client__get_repos_mergeinfo(
&source_mergeinfo, source_ra_session,
source_pathrev->url, source_pathrev->rev,
svn_mergeinfo_inherited, FALSE /*squelch_incapable*/,
iterpool));
if (!source_mergeinfo)
source_mergeinfo = apr_hash_make(iterpool);
}
/* Use scratch_pool rather than iterpool because filtered_mergeinfo
is going into new_catalog below and needs to last to the end of
this function. */
SVN_ERR(find_unmerged_mergeinfo_subroutine(
&filtered_mergeinfo, target_history_as_mergeinfo,
source_mergeinfo, source_pathrev,
source_ra_session, ctx, scratch_pool, iterpool));
svn_hash_sets(new_catalog, apr_pstrdup(scratch_pool, source_path),
filtered_mergeinfo);
}
/* Are there any subtrees with explicit mergeinfo still left in the merge
source where there was no explicit mergeinfo for the corresponding path
in the merge target? If so, add the intersection of those path's
mergeinfo and the corresponding target path's mergeinfo to
new_catalog. */
for (hi = apr_hash_first(scratch_pool, source_catalog);
hi;
hi = apr_hash_next(hi))
{
const char *source_path = svn__apr_hash_index_key(hi);
const char *path_rel_to_session =
svn_relpath_skip_ancestor(source_repos_rel_path, source_path);
const char *source_url;
svn_mergeinfo_t source_mergeinfo = svn__apr_hash_index_val(hi);
svn_mergeinfo_t filtered_mergeinfo;
svn_client__pathrev_t *target_pathrev;
svn_mergeinfo_t target_history_as_mergeinfo;
svn_error_t *err;
svn_pool_clear(iterpool);
source_url = svn_path_url_add_component2(source_loc->url,
path_rel_to_session, iterpool);
target_pathrev = svn_client__pathrev_join_relpath(
&target->loc, path_rel_to_session, iterpool);
err = svn_client__get_history_as_mergeinfo(&target_history_as_mergeinfo,
NULL /* has_rev_zero_history */,
target_pathrev,
target->loc.rev,
SVN_INVALID_REVNUM,
target_ra_session,
ctx, iterpool);
if (err)
{
if (err->apr_err == SVN_ERR_FS_NOT_FOUND
|| err->apr_err == SVN_ERR_RA_DAV_REQUEST_FAILED)
{
/* This path with explicit mergeinfo in the source doesn't
exist on the target. */
svn_error_clear(err);
err = NULL;
}
else
{
return svn_error_trace(err);
}
}
else
{
svn_client__pathrev_t *pathrev;
SVN_ERR(find_youngest_merged_rev(youngest_merged_rev,
target_history_as_mergeinfo,
source_mergeinfo,
iterpool));
/* Use scratch_pool rather than iterpool because filtered_mergeinfo
is going into new_catalog below and needs to last to the end of
this function. */
/* ### Why looking at SOURCE_url at TARGET_rev? */
SVN_ERR(svn_client__pathrev_create_with_session(
&pathrev, source_ra_session, target->loc.rev, source_url,
iterpool));
SVN_ERR(find_unmerged_mergeinfo_subroutine(
&filtered_mergeinfo, target_history_as_mergeinfo,
source_mergeinfo, pathrev,
source_ra_session, ctx, scratch_pool, iterpool));
if (apr_hash_count(filtered_mergeinfo))
svn_hash_sets(new_catalog,
apr_pstrdup(scratch_pool, source_path),
filtered_mergeinfo);
}
}
/* Limit new_catalog to the youngest revisions previously merged from
the target to the source. */
if (SVN_IS_VALID_REVNUM(*youngest_merged_rev))
SVN_ERR(svn_mergeinfo__filter_catalog_by_ranges(&new_catalog,
new_catalog,
*youngest_merged_rev,
0, /* No oldest bound. */
TRUE,
scratch_pool,
scratch_pool));
/* Make a shiny new copy before blowing away all the temporary pools. */
*unmerged_to_source_catalog = svn_mergeinfo_catalog_dup(new_catalog,
result_pool);
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Helper for svn_client_merge_reintegrate() which calculates the
'left hand side' of the underlying two-URL merge that a --reintegrate
merge actually performs. If no merge should be performed, set
*LEFT_P to NULL.
TARGET->abspath is the absolute working copy path of the reintegrate
merge.
SOURCE_LOC is the reintegrate source.
SUBTREES_WITH_MERGEINFO is a hash of (const char *) absolute paths mapped
to (svn_mergeinfo_t *) mergeinfo values for each working copy path with
explicit mergeinfo in TARGET->abspath. Actually we only need to know the
paths, not the mergeinfo.
TARGET->loc.rev is the working revision the entire WC tree rooted at
TARGET is at.
Populate *UNMERGED_TO_SOURCE_CATALOG with the mergeinfo describing what
parts of TARGET->loc have not been merged to SOURCE_LOC, up to the
youngest revision ever merged from the TARGET->abspath to the source if
such exists, see doc string for find_unmerged_mergeinfo().
SOURCE_RA_SESSION is a session opened to the SOURCE_LOC
and TARGET_RA_SESSION is open to TARGET->loc.url.
*LEFT_P, *MERGED_TO_SOURCE_CATALOG , and *UNMERGED_TO_SOURCE_CATALOG are
allocated in RESULT_POOL. SCRATCH_POOL is used for all temporary
allocations. */
static svn_error_t *
calculate_left_hand_side(svn_client__pathrev_t **left_p,
svn_mergeinfo_catalog_t *merged_to_source_catalog,
svn_mergeinfo_catalog_t *unmerged_to_source_catalog,
const merge_target_t *target,
apr_hash_t *subtrees_with_mergeinfo,
const svn_client__pathrev_t *source_loc,
svn_ra_session_t *source_ra_session,
svn_ra_session_t *target_ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_mergeinfo_catalog_t mergeinfo_catalog, unmerged_catalog;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
apr_hash_index_t *hi;
/* hash of paths mapped to arrays of svn_mergeinfo_t. */
apr_hash_t *target_history_hash = apr_hash_make(scratch_pool);
svn_revnum_t youngest_merged_rev;
svn_client__pathrev_t *yc_ancestor;
assert(session_url_is(source_ra_session, source_loc->url, scratch_pool));
assert(session_url_is(target_ra_session, target->loc.url, scratch_pool));
/* Initialize our return variables. */
*left_p = NULL;
/* TARGET->abspath may not have explicit mergeinfo and thus may not be
contained within SUBTREES_WITH_MERGEINFO. If this is the case then
add a dummy item for TARGET->abspath so we get its history (i.e. implicit
mergeinfo) below. */
if (!svn_hash_gets(subtrees_with_mergeinfo, target->abspath))
svn_hash_sets(subtrees_with_mergeinfo, target->abspath,
apr_hash_make(result_pool));
/* Get the history segments (as mergeinfo) for TARGET->abspath and any of
its subtrees with explicit mergeinfo. */
for (hi = apr_hash_first(scratch_pool, subtrees_with_mergeinfo);
hi;
hi = apr_hash_next(hi))
{
const char *local_abspath = svn__apr_hash_index_key(hi);
svn_client__pathrev_t *target_child;
const char *repos_relpath;
svn_mergeinfo_t target_history_as_mergeinfo;
svn_pool_clear(iterpool);
/* Convert the absolute path with mergeinfo on it to a path relative
to the session root. */
SVN_ERR(svn_wc__node_get_repos_info(NULL, &repos_relpath, NULL, NULL,
ctx->wc_ctx, local_abspath,
scratch_pool, iterpool));
target_child = svn_client__pathrev_create_with_relpath(
target->loc.repos_root_url, target->loc.repos_uuid,
target->loc.rev, repos_relpath, iterpool);
SVN_ERR(svn_client__get_history_as_mergeinfo(&target_history_as_mergeinfo,
NULL /* has_rev_zero_hist */,
target_child,
target->loc.rev,
SVN_INVALID_REVNUM,
target_ra_session,
ctx, scratch_pool));
svn_hash_sets(target_history_hash, repos_relpath,
target_history_as_mergeinfo);
}
/* Check that SOURCE_LOC and TARGET->loc are
actually related, we can't reintegrate if they are not. Also
get an initial value for the YCA revision number. */
SVN_ERR(svn_client__get_youngest_common_ancestor(
&yc_ancestor, source_loc, &target->loc, target_ra_session, ctx,
iterpool, iterpool));
if (! yc_ancestor)
return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
_("'%s@%ld' must be ancestrally related to "
"'%s@%ld'"), source_loc->url, source_loc->rev,
target->loc.url, target->loc.rev);
/* If the source revision is the same as the youngest common
revision, then there can't possibly be any unmerged revisions
that we need to apply to target. */
if (source_loc->rev == yc_ancestor->rev)
{
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Get the mergeinfo from the source, including its descendants
with differing explicit mergeinfo. */
SVN_ERR(svn_client__get_repos_mergeinfo_catalog(
&mergeinfo_catalog, source_ra_session,
source_loc->url, source_loc->rev,
svn_mergeinfo_inherited, FALSE /* squelch_incapable */,
TRUE /* include_descendants */, iterpool, iterpool));
if (!mergeinfo_catalog)
mergeinfo_catalog = apr_hash_make(iterpool);
*merged_to_source_catalog = svn_mergeinfo_catalog_dup(mergeinfo_catalog,
result_pool);
/* Filter the source's mergeinfo catalog so that we are left with
mergeinfo that describes what has *not* previously been merged from
TARGET->loc to SOURCE_LOC. */
SVN_ERR(find_unmerged_mergeinfo(&unmerged_catalog,
&youngest_merged_rev,
yc_ancestor->rev,
mergeinfo_catalog,
target_history_hash,
source_loc,
target,
source_ra_session,
target_ra_session,
ctx,
iterpool, iterpool));
/* Simplify unmerged_catalog through elision then make a copy in POOL. */
SVN_ERR(svn_client__elide_mergeinfo_catalog(unmerged_catalog,
iterpool));
*unmerged_to_source_catalog = svn_mergeinfo_catalog_dup(unmerged_catalog,
result_pool);
if (youngest_merged_rev == SVN_INVALID_REVNUM)
{
/* We never merged to the source. Just return the branch point. */
*left_p = svn_client__pathrev_dup(yc_ancestor, result_pool);
}
else
{
/* We've previously merged some or all of the target, up to
youngest_merged_rev, to the source. Set
*LEFT_P to cover the youngest part of this range. */
SVN_ERR(svn_client__repos_location(left_p, target_ra_session,
&target->loc, youngest_merged_rev,
ctx, result_pool, iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Determine the URLs and revisions needed to perform a reintegrate merge
* from SOURCE_LOC into the working copy at TARGET.
*
* SOURCE_RA_SESSION and TARGET_RA_SESSION are RA sessions opened to the
* URLs of SOURCE_LOC and TARGET->loc respectively.
*
* Set *SOURCE_P to
* the source-left and source-right locations of the required merge. Set
* *YC_ANCESTOR_P to the location of the youngest ancestor.
* Any of these output pointers may be NULL if not wanted.
*
* See svn_client_find_reintegrate_merge() for other details.
*/
static svn_error_t *
find_reintegrate_merge(merge_source_t **source_p,
svn_client__pathrev_t **yc_ancestor_p,
svn_ra_session_t *source_ra_session,
const svn_client__pathrev_t *source_loc,
svn_ra_session_t *target_ra_session,
const merge_target_t *target,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_client__pathrev_t *yc_ancestor;
svn_client__pathrev_t *loc1;
merge_source_t source;
svn_mergeinfo_catalog_t unmerged_to_source_mergeinfo_catalog;
svn_mergeinfo_catalog_t merged_to_source_mergeinfo_catalog;
svn_error_t *err;
apr_hash_t *subtrees_with_mergeinfo;
assert(session_url_is(source_ra_session, source_loc->url, scratch_pool));
assert(session_url_is(target_ra_session, target->loc.url, scratch_pool));
/* As the WC tree is "pure", use its last-updated-to revision as
the default revision for the left side of our merge, since that's
what the repository sub-tree is required to be up to date with
(with regard to the WC). */
/* ### Bogus/obsolete comment? */
/* Can't reintegrate to or from the root of the repository. */
if (strcmp(source_loc->url, source_loc->repos_root_url) == 0
|| strcmp(target->loc.url, target->loc.repos_root_url) == 0)
return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
_("Neither the reintegrate source nor target "
"can be the root of the repository"));
/* Find all the subtrees in TARGET_WCPATH that have explicit mergeinfo. */
err = get_wc_explicit_mergeinfo_catalog(&subtrees_with_mergeinfo,
target->abspath, svn_depth_infinity,
ctx, scratch_pool, scratch_pool);
/* Issue #3896: If invalid mergeinfo in the reintegrate target
prevents us from proceeding, then raise the best error possible. */
if (err && err->apr_err == SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING)
err = svn_error_quick_wrap(err, _("Reintegrate merge not possible"));
SVN_ERR(err);
SVN_ERR(calculate_left_hand_side(&loc1,
&merged_to_source_mergeinfo_catalog,
&unmerged_to_source_mergeinfo_catalog,
target,
subtrees_with_mergeinfo,
source_loc,
source_ra_session,
target_ra_session,
ctx,
scratch_pool, scratch_pool));
/* Did calculate_left_hand_side() decide that there was no merge to
be performed here? */
if (! loc1)
{
if (source_p)
*source_p = NULL;
if (yc_ancestor_p)
*yc_ancestor_p = NULL;
return SVN_NO_ERROR;
}
source.loc1 = loc1;
source.loc2 = source_loc;
/* If the target was moved after the source was branched from it,
it is possible that the left URL differs from the target's current
URL. If so, then adjust TARGET_RA_SESSION to point to the old URL. */
if (strcmp(source.loc1->url, target->loc.url))
SVN_ERR(svn_ra_reparent(target_ra_session, source.loc1->url, scratch_pool));
SVN_ERR(svn_client__get_youngest_common_ancestor(
&yc_ancestor, source.loc2, source.loc1, target_ra_session,
ctx, scratch_pool, scratch_pool));
if (! yc_ancestor)
return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
_("'%s@%ld' must be ancestrally related to "
"'%s@%ld'"),
source.loc1->url, source.loc1->rev,
source.loc2->url, source.loc2->rev);
/* The source side of a reintegrate merge is not 'ancestral', except in
* the degenerate case where source == YCA. */
source.ancestral = (loc1->rev == yc_ancestor->rev);
if (source.loc1->rev > yc_ancestor->rev)
{
/* Have we actually merged anything to the source from the
target? If so, make sure we've merged a contiguous
prefix. */
svn_mergeinfo_catalog_t final_unmerged_catalog = apr_hash_make(scratch_pool);
SVN_ERR(find_unsynced_ranges(source_loc, &target->loc,
unmerged_to_source_mergeinfo_catalog,
merged_to_source_mergeinfo_catalog,
final_unmerged_catalog,
target_ra_session, scratch_pool,
scratch_pool));
if (apr_hash_count(final_unmerged_catalog))
{
svn_string_t *source_mergeinfo_cat_string;
SVN_ERR(svn_mergeinfo__catalog_to_formatted_string(
&source_mergeinfo_cat_string,
final_unmerged_catalog,
" ", " Missing ranges: ", scratch_pool));
return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE,
NULL,
_("Reintegrate can only be used if "
"revisions %ld through %ld were "
"previously merged from %s to the "
"reintegrate source, but this is "
"not the case:\n%s"),
yc_ancestor->rev + 1, source.loc2->rev,
target->loc.url,
source_mergeinfo_cat_string->data);
}
}
/* Left side: trunk@youngest-trunk-rev-merged-to-branch-at-specified-peg-rev
* Right side: branch@specified-peg-revision */
if (source_p)
*source_p = merge_source_dup(&source, result_pool);
if (yc_ancestor_p)
*yc_ancestor_p = svn_client__pathrev_dup(yc_ancestor, result_pool);
return SVN_NO_ERROR;
}
/* Resolve the source and target locations and open RA sessions to them, and
* perform some checks appropriate for a reintegrate merge.
*
* Set *SOURCE_RA_SESSION_P and *SOURCE_LOC_P to a new session and the
* repository location of SOURCE_PATH_OR_URL at SOURCE_PEG_REVISION. Set
* *TARGET_RA_SESSION_P and *TARGET_P to a new session and the repository
* location of the WC at TARGET_ABSPATH.
*
* Throw a SVN_ERR_CLIENT_UNRELATED_RESOURCES error if the target WC node is
* a locally added node or if the source and target are not in the same
* repository. Throw a SVN_ERR_CLIENT_NOT_READY_TO_MERGE error if the
* target WC is not at a single revision without switched subtrees and
* without local mods.
*
* Allocate all the outputs in RESULT_POOL.
*/
static svn_error_t *
open_reintegrate_source_and_target(svn_ra_session_t **source_ra_session_p,
svn_client__pathrev_t **source_loc_p,
svn_ra_session_t **target_ra_session_p,
merge_target_t **target_p,
const char *source_path_or_url,
const svn_opt_revision_t *source_peg_revision,
const char *target_abspath,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_client__pathrev_t *source_loc;
merge_target_t *target;
/* Open the target WC. A reintegrate merge requires the merge target to
* reflect a subtree of the repository as found at a single revision. */
SVN_ERR(open_target_wc(&target, target_abspath,
FALSE, FALSE, FALSE,
ctx, scratch_pool, scratch_pool));
SVN_ERR(svn_client_open_ra_session2(target_ra_session_p,
target->loc.url, target->abspath,
ctx, result_pool, scratch_pool));
if (! target->loc.url)
return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
_("Can't reintegrate into '%s' because it is "
"locally added and therefore not related to "
"the merge source"),
svn_dirent_local_style(target->abspath,
scratch_pool));
SVN_ERR(svn_client__ra_session_from_path2(
source_ra_session_p, &source_loc,
source_path_or_url, NULL, source_peg_revision, source_peg_revision,
ctx, result_pool));
/* source_loc and target->loc are required to be in the same repository,
as mergeinfo doesn't come into play for cross-repository merging. */
SVN_ERR(check_same_repos(source_loc,
svn_dirent_local_style(source_path_or_url,
scratch_pool),
&target->loc,
svn_dirent_local_style(target->abspath,
scratch_pool),
TRUE /* strict_urls */, scratch_pool));
*source_loc_p = source_loc;
*target_p = target;
return SVN_NO_ERROR;
}
/* The body of svn_client_merge_reintegrate(), which see for details. */
static svn_error_t *
merge_reintegrate_locked(conflict_report_t **conflict_report,
const char *source_path_or_url,
const svn_opt_revision_t *source_peg_revision,
const char *target_abspath,
svn_boolean_t diff_ignore_ancestry,
svn_boolean_t dry_run,
const apr_array_header_t *merge_options,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_ra_session_t *target_ra_session, *source_ra_session;
merge_target_t *target;
svn_client__pathrev_t *source_loc;
merge_source_t *source;
svn_client__pathrev_t *yc_ancestor;
svn_boolean_t use_sleep = FALSE;
svn_error_t *err;
SVN_ERR(open_reintegrate_source_and_target(
&source_ra_session, &source_loc, &target_ra_session, &target,
source_path_or_url, source_peg_revision, target_abspath,
ctx, scratch_pool, scratch_pool));
SVN_ERR(find_reintegrate_merge(&source, &yc_ancestor,
source_ra_session, source_loc,
target_ra_session, target,
ctx, scratch_pool, scratch_pool));
if (! source)
{
return SVN_NO_ERROR;
}
/* Do the real merge! */
/* ### TODO(reint): Make sure that one isn't the same line ancestor
### of the other (what's erroneously referred to as "ancestrally
### related" in this source file). For now, we just say the source
### isn't "ancestral" even if it is (in the degenerate case where
### source-left equals YCA). */
source->ancestral = FALSE;
err = merge_cousins_and_supplement_mergeinfo(conflict_report,
&use_sleep,
target,
target_ra_session,
source_ra_session,
source, yc_ancestor,
TRUE /* same_repos */,
svn_depth_infinity,
diff_ignore_ancestry,
FALSE /* force_delete */,
FALSE /* record_only */,
dry_run,
merge_options,
ctx,
result_pool, scratch_pool);
if (use_sleep)
svn_io_sleep_for_timestamps(target_abspath, scratch_pool);
SVN_ERR(err);
return SVN_NO_ERROR;
}
svn_error_t *
svn_client_merge_reintegrate(const char *source_path_or_url,
const svn_opt_revision_t *source_peg_revision,
const char *target_wcpath,
svn_boolean_t dry_run,
const apr_array_header_t *merge_options,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
const char *target_abspath, *lock_abspath;
conflict_report_t *conflict_report;
SVN_ERR(get_target_and_lock_abspath(&target_abspath, &lock_abspath,
target_wcpath, ctx, pool));
if (!dry_run)
SVN_WC__CALL_WITH_WRITE_LOCK(
merge_reintegrate_locked(&conflict_report,
source_path_or_url, source_peg_revision,
target_abspath,
FALSE /*diff_ignore_ancestry*/,
dry_run, merge_options, ctx, pool, pool),
ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
else
SVN_ERR(merge_reintegrate_locked(&conflict_report,
source_path_or_url, source_peg_revision,
target_abspath,
FALSE /*diff_ignore_ancestry*/,
dry_run, merge_options, ctx, pool, pool));
SVN_ERR(make_merge_conflict_error(conflict_report, pool));
return SVN_NO_ERROR;
}
/* The body of svn_client_merge_peg5(), which see for details.
*
* IGNORE_MERGEINFO and DIFF_IGNORE_ANCESTRY are as in do_merge().
*/
static svn_error_t *
merge_peg_locked(conflict_report_t **conflict_report,
const char *source_path_or_url,
const svn_opt_revision_t *source_peg_revision,
const svn_rangelist_t *ranges_to_merge,
const char *target_abspath,
svn_depth_t depth,
svn_boolean_t ignore_mergeinfo,
svn_boolean_t diff_ignore_ancestry,
svn_boolean_t force_delete,
svn_boolean_t record_only,
svn_boolean_t dry_run,
svn_boolean_t allow_mixed_rev,
const apr_array_header_t *merge_options,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
merge_target_t *target;
svn_client__pathrev_t *source_loc;
apr_array_header_t *merge_sources;
svn_ra_session_t *ra_session;
apr_pool_t *sesspool;
svn_boolean_t use_sleep = FALSE;
svn_error_t *err;
svn_boolean_t same_repos;
SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
SVN_ERR(open_target_wc(&target, target_abspath,
allow_mixed_rev, TRUE, TRUE,
ctx, scratch_pool, scratch_pool));
/* Create a short lived session pool */
sesspool = svn_pool_create(scratch_pool);
/* Open an RA session to our source URL, and determine its root URL. */
SVN_ERR(svn_client__ra_session_from_path2(
&ra_session, &source_loc,
source_path_or_url, NULL, source_peg_revision, source_peg_revision,
ctx, sesspool));
/* Normalize our merge sources. */
SVN_ERR(normalize_merge_sources(&merge_sources, source_path_or_url,
source_loc,
ranges_to_merge, ra_session, ctx,
scratch_pool, scratch_pool));
/* Check for same_repos. */
same_repos = is_same_repos(&target->loc, source_loc, TRUE /* strict_urls */);
/* Do the real merge! (We say with confidence that our merge
sources are both ancestral and related.) */
err = do_merge(NULL, NULL, conflict_report, &use_sleep,
merge_sources, target, ra_session,
TRUE /*sources_related*/, same_repos, ignore_mergeinfo,
diff_ignore_ancestry, force_delete, dry_run,
record_only, NULL, FALSE, FALSE, depth, merge_options,
ctx, result_pool, scratch_pool);
/* We're done with our RA session. */
svn_pool_destroy(sesspool);
if (use_sleep)
svn_io_sleep_for_timestamps(target_abspath, scratch_pool);
SVN_ERR(err);
return SVN_NO_ERROR;
}
/* Details of an automatic merge. */
typedef struct automatic_merge_t
{
svn_client__pathrev_t *yca, *base, *right, *target;
svn_boolean_t is_reintegrate_like;
svn_boolean_t allow_mixed_rev, allow_local_mods, allow_switched_subtrees;
} automatic_merge_t;
static svn_error_t *
client_find_automatic_merge(automatic_merge_t **merge_p,
const char *source_path_or_url,
const svn_opt_revision_t *source_revision,
const char *target_abspath,
svn_boolean_t allow_mixed_rev,
svn_boolean_t allow_local_mods,
svn_boolean_t allow_switched_subtrees,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
static svn_error_t *
do_automatic_merge_locked(conflict_report_t **conflict_report,
const automatic_merge_t *merge,
const char *target_abspath,
svn_depth_t depth,
svn_boolean_t diff_ignore_ancestry,
svn_boolean_t force_delete,
svn_boolean_t record_only,
svn_boolean_t dry_run,
const apr_array_header_t *merge_options,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
svn_error_t *
svn_client_merge_peg5(const char *source_path_or_url,
const apr_array_header_t *ranges_to_merge,
const svn_opt_revision_t *source_peg_revision,
const char *target_wcpath,
svn_depth_t depth,
svn_boolean_t ignore_mergeinfo,
svn_boolean_t diff_ignore_ancestry,
svn_boolean_t force_delete,
svn_boolean_t record_only,
svn_boolean_t dry_run,
svn_boolean_t allow_mixed_rev,
const apr_array_header_t *merge_options,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
const char *target_abspath, *lock_abspath;
conflict_report_t *conflict_report;
/* No ranges to merge? No problem. */
if (ranges_to_merge != NULL && ranges_to_merge->nelts == 0)
return SVN_NO_ERROR;
SVN_ERR(get_target_and_lock_abspath(&target_abspath, &lock_abspath,
target_wcpath, ctx, pool));
/* Do an automatic merge if no revision ranges are specified. */
if (ranges_to_merge == NULL)
{
automatic_merge_t *merge;
if (ignore_mergeinfo)
return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
_("Cannot merge automatically while "
"ignoring mergeinfo"));
/* Find the details of the merge needed. */
SVN_ERR(client_find_automatic_merge(
&merge,
source_path_or_url, source_peg_revision,
target_abspath,
allow_mixed_rev,
TRUE /*allow_local_mods*/,
TRUE /*allow_switched_subtrees*/,
ctx, pool, pool));
if (!dry_run)
SVN_WC__CALL_WITH_WRITE_LOCK(
do_automatic_merge_locked(&conflict_report,
merge,
target_abspath, depth,
diff_ignore_ancestry,
force_delete, record_only, dry_run,
merge_options, ctx, pool, pool),
ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
else
SVN_ERR(do_automatic_merge_locked(&conflict_report,
merge,
target_abspath, depth,
diff_ignore_ancestry,
force_delete, record_only, dry_run,
merge_options, ctx, pool, pool));
}
else if (!dry_run)
SVN_WC__CALL_WITH_WRITE_LOCK(
merge_peg_locked(&conflict_report,
source_path_or_url, source_peg_revision,
ranges_to_merge,
target_abspath, depth, ignore_mergeinfo,
diff_ignore_ancestry,
force_delete, record_only, dry_run,
allow_mixed_rev, merge_options, ctx, pool, pool),
ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
else
SVN_ERR(merge_peg_locked(&conflict_report,
source_path_or_url, source_peg_revision,
ranges_to_merge,
target_abspath, depth, ignore_mergeinfo,
diff_ignore_ancestry,
force_delete, record_only, dry_run,
allow_mixed_rev, merge_options, ctx, pool, pool));
SVN_ERR(make_merge_conflict_error(conflict_report, pool));
return SVN_NO_ERROR;
}
/* The location-history of a branch.
*
* This structure holds the set of path-revisions occupied by a branch,
* from an externally chosen 'tip' location back to its origin. The
* 'tip' location is the youngest location that we are considering on
* the branch. */
typedef struct branch_history_t
{
/* The tip location of the branch. That is, the youngest location that's
* in the repository and that we're considering. If we're considering a
* target branch right up to an uncommitted WC, then this is the WC base
* (pristine) location. */
svn_client__pathrev_t *tip;
/* The location-segment history, as mergeinfo. */
svn_mergeinfo_t history;
/* Whether the location-segment history reached as far as (necessarily
the root path in) revision 0 -- a fact that can't be represented as
mergeinfo. */
svn_boolean_t has_r0_history;
} branch_history_t;
/* Return the location on BRANCH_HISTORY at revision REV, or NULL if none. */
static svn_client__pathrev_t *
location_on_branch_at_rev(const branch_history_t *branch_history,
svn_revnum_t rev,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_hash_index_t *hi;
for (hi = apr_hash_first(scratch_pool, branch_history->history); hi;
hi = apr_hash_next(hi))
{
const char *fspath = svn__apr_hash_index_key(hi);
svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
int i;
for (i = 0; i < rangelist->nelts; i++)
{
svn_merge_range_t *r = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
if (r->start < rev && rev <= r->end)
{
return svn_client__pathrev_create_with_relpath(
branch_history->tip->repos_root_url,
branch_history->tip->repos_uuid,
rev, fspath + 1, result_pool);
}
}
}
return NULL;
}
/* */
typedef struct source_and_target_t
{
svn_client__pathrev_t *source;
svn_ra_session_t *source_ra_session;
branch_history_t source_branch;
merge_target_t *target;
svn_ra_session_t *target_ra_session;
branch_history_t target_branch;
/* Repos location of the youngest common ancestor of SOURCE and TARGET. */
svn_client__pathrev_t *yca;
} source_and_target_t;
/* Set *INTERSECTION_P to the intersection of BRANCH_HISTORY with the
* revision range OLDEST_REV to YOUNGEST_REV (inclusive).
*
* If the intersection is empty, the result will be a branch history object
* containing an empty (not null) history.
*
* ### The 'tip' of the result is currently unchanged.
*/
static svn_error_t *
branch_history_intersect_range(branch_history_t **intersection_p,
const branch_history_t *branch_history,
svn_revnum_t oldest_rev,
svn_revnum_t youngest_rev,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
branch_history_t *result = apr_palloc(result_pool, sizeof(*result));
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(oldest_rev));
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
SVN_ERR_ASSERT(oldest_rev >= 1);
/* Allow a just-empty range (oldest = youngest + 1) but not an
* arbitrary reverse range (such as oldest = youngest + 2). */
SVN_ERR_ASSERT(oldest_rev <= youngest_rev + 1);
if (oldest_rev <= youngest_rev)
{
SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(
&result->history, branch_history->history,
youngest_rev, oldest_rev - 1, TRUE /* include_range */,
result_pool, scratch_pool));
result->history = svn_mergeinfo_dup(result->history, result_pool);
}
else
{
result->history = apr_hash_make(result_pool);
}
result->has_r0_history = FALSE;
/* ### TODO: Set RESULT->tip to the tip of the intersection. */
result->tip = svn_client__pathrev_dup(branch_history->tip, result_pool);
*intersection_p = result;
return SVN_NO_ERROR;
}
/* Set *OLDEST_P and *YOUNGEST_P to the oldest and youngest locations
* (inclusive) along BRANCH. OLDEST_P and/or YOUNGEST_P may be NULL if not
* wanted.
*/
static svn_error_t *
branch_history_get_endpoints(svn_client__pathrev_t **oldest_p,
svn_client__pathrev_t **youngest_p,
const branch_history_t *branch,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_revnum_t youngest_rev, oldest_rev;
SVN_ERR(svn_mergeinfo__get_range_endpoints(
&youngest_rev, &oldest_rev,
branch->history, scratch_pool));
if (oldest_p)
*oldest_p = location_on_branch_at_rev(
branch, oldest_rev + 1, result_pool, scratch_pool);
if (youngest_p)
*youngest_p = location_on_branch_at_rev(
branch, youngest_rev, result_pool, scratch_pool);
return SVN_NO_ERROR;
}
/* Implements the svn_log_entry_receiver_t interface.
Set *BATON to LOG_ENTRY->revision and return SVN_ERR_CEASE_INVOCATION. */
static svn_error_t *
operative_rev_receiver(void *baton,
svn_log_entry_t *log_entry,
apr_pool_t *pool)
{
svn_revnum_t *operative_rev = baton;
*operative_rev = log_entry->revision;
/* We've found the youngest merged or oldest eligible revision, so
we're done...
...but wait, shouldn't we care if LOG_ENTRY->NON_INHERITABLE is
true? Because if it is, then LOG_ENTRY->REVISION is only
partially merged/elgibile! And our only caller,
find_last_merged_location (via short_circuit_mergeinfo_log) is
interested in *fully* merged revisions. That's all true, but if
find_last_merged_location() finds the youngest merged revision it
will also check for the oldest eligible revision. So in the case
the youngest merged rev is non-inheritable, the *same* non-inheritable
rev will be found as the oldest eligible rev -- and
find_last_merged_location() handles that situation. */
return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
}
/* Wrapper around svn_client__mergeinfo_log. All arguments are as per
that private API. The discover_changed_paths, depth, and revprops args to
svn_client__mergeinfo_log are always TRUE, svn_depth_infinity_t,
and empty array respectively.
If RECEIVER raises a SVN_ERR_CEASE_INVOCATION error, but still sets
*REVISION to a valid revnum, then clear the error. Otherwise return
any error. */
static svn_error_t*
short_circuit_mergeinfo_log(svn_mergeinfo_catalog_t *target_mergeinfo_cat,
svn_boolean_t finding_merged,
const char *target_path_or_url,
const svn_opt_revision_t *target_peg_revision,
const char *source_path_or_url,
const svn_opt_revision_t *source_peg_revision,
const svn_opt_revision_t *source_start_revision,
const svn_opt_revision_t *source_end_revision,
svn_log_entry_receiver_t receiver,
svn_revnum_t *revision,
svn_client_ctx_t *ctx,
svn_ra_session_t *ra_session,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_array_header_t *revprops;
svn_error_t *err;
const char *session_url;
SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, scratch_pool));
revprops = apr_array_make(scratch_pool, 0, sizeof(const char *));
err = svn_client__mergeinfo_log(finding_merged,
target_path_or_url,
target_peg_revision,
target_mergeinfo_cat,
source_path_or_url,
source_peg_revision,
source_start_revision,
source_end_revision,
receiver, revision,
TRUE, svn_depth_infinity,
revprops, ctx, ra_session,
result_pool, scratch_pool);
err = svn_error_compose_create(
err,
svn_ra_reparent(ra_session, session_url, scratch_pool));
if (err)
{
/* We expect RECEIVER to short-circuit the (potentially expensive) log
by raising an SVN_ERR_CEASE_INVOCATION -- see operative_rev_receiver.
So we can ignore that error, but only as long as we actually found a
valid revision. */
if (SVN_IS_VALID_REVNUM(*revision)
&& err->apr_err == SVN_ERR_CEASE_INVOCATION)
{
svn_error_clear(err);
err = NULL;
}
else
{
return svn_error_trace(err);
}
}
return SVN_NO_ERROR;
}
/* Set *BASE_P to the last location on SOURCE_BRANCH such that all changes
* on SOURCE_BRANCH after YCA up to and including *BASE_P have already
* been fully merged into TARGET.
*
* *BASE_P TIP
* o-------o-----------o--- SOURCE_BRANCH
* / \
* -----o prev. \
* YCA \ merges \
* o-----------o----------- TARGET branch
*
* In terms of mergeinfo:
*
* Source a--... o=change, -=no-op revision
* branch / \
* YCA --> o a---o---o---o---o--- d=delete, a=add-as-a-copy
*
* Eligible -.eee.eeeeeeeeeeeeeeeeeeee .=not a source branch location
*
* Tgt-mi -.mmm.mm-mm-------m------- m=merged to root of TARGET or
* subtree of TARGET with no
* operative changes outside of that
* subtree, -=not merged
*
* Eligible -.---.--e--eeeeeee-eeeeeee
*
* Next --------^----------------- BASE is just before here.
*
* / \
* -----o prev. \
* YCA \ merges \
* o-----------o-------------
*
* If no revisions from SOURCE_BRANCH have been completely merged to TARGET,
* then set *BASE_P to the YCA.
*/
static svn_error_t *
find_last_merged_location(svn_client__pathrev_t **base_p,
svn_client__pathrev_t *yca,
const branch_history_t *source_branch,
svn_client__pathrev_t *target,
svn_client_ctx_t *ctx,
svn_ra_session_t *ra_session,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_opt_revision_t source_peg_rev, source_start_rev, source_end_rev,
target_opt_rev;
svn_revnum_t youngest_merged_rev = SVN_INVALID_REVNUM;
svn_mergeinfo_catalog_t target_mergeinfo_cat = NULL;
source_peg_rev.kind = svn_opt_revision_number;
source_peg_rev.value.number = source_branch->tip->rev;
source_start_rev.kind = svn_opt_revision_number;
source_start_rev.value.number = yca->rev;
source_end_rev.kind = svn_opt_revision_number;
source_end_rev.value.number = source_branch->tip->rev;
target_opt_rev.kind = svn_opt_revision_number;
target_opt_rev.value.number = target->rev;
/* Find the youngest revision fully merged from SOURCE_BRANCH to TARGET,
if such a revision exists. */
SVN_ERR(short_circuit_mergeinfo_log(&target_mergeinfo_cat,
TRUE, /* Find merged */
target->url, &target_opt_rev,
source_branch->tip->url,
&source_peg_rev,
&source_end_rev, &source_start_rev,
operative_rev_receiver,
&youngest_merged_rev,
ctx, ra_session,
result_pool, scratch_pool));
if (!SVN_IS_VALID_REVNUM(youngest_merged_rev))
{
/* No revisions have been completely merged from SOURCE_BRANCH to
TARGET so the base for the next merge is the YCA. */
*base_p = yca;
}
else
{
/* One or more revisions have already been completely merged from
SOURCE_BRANCH to TARGET, now find the oldest revision, older
than the youngest merged revision, which is still eligible to
be merged, if such exists. */
branch_history_t *contiguous_source;
svn_revnum_t base_rev;
svn_revnum_t oldest_eligible_rev = SVN_INVALID_REVNUM;
/* If the only revisions eligible are younger than the youngest merged
revision we can simply assume that the youngest eligible revision
is the youngest merged revision. Obviously this may not be true!
The revisions between the youngest merged revision and the tip of
the branch may have several inoperative revisions -- they may *all*
be inoperative revisions! But for the purpose of this function
(i.e. finding the youngest revision after the YCA where all revs have
been merged) that doesn't matter. */
source_end_rev.value.number = youngest_merged_rev;
SVN_ERR(short_circuit_mergeinfo_log(&target_mergeinfo_cat,
FALSE, /* Find eligible */
target->url, &target_opt_rev,
source_branch->tip->url,
&source_peg_rev,
&source_start_rev, &source_end_rev,
operative_rev_receiver,
&oldest_eligible_rev,
ctx, ra_session,
scratch_pool, scratch_pool));
/* If there are revisions eligible for merging, use the oldest one
to calculate the base. Otherwise there are no operative revisions
to merge and we can simple set the base to the youngest revision
already merged. */
if (SVN_IS_VALID_REVNUM(oldest_eligible_rev))
base_rev = oldest_eligible_rev - 1;
else
base_rev = youngest_merged_rev;
/* Find the branch location just before the oldest eligible rev.
(We can't just use the base revs calculated above because the branch
might have a gap there.) */
SVN_ERR(branch_history_intersect_range(&contiguous_source,
source_branch, yca->rev,
base_rev,
scratch_pool, scratch_pool));
SVN_ERR(branch_history_get_endpoints(NULL, base_p, contiguous_source,
result_pool, scratch_pool));
}
return SVN_NO_ERROR;
}
/* Find a merge base location on the target branch, like in a sync
* merge.
*
* BASE S_T->source
* o-------o-----------o---
* / \ \
* -----o prev. \ \ this
* YCA \ merge \ \ merge
* o-----------o-----------o
* S_T->target
*
* Set *BASE_P to BASE, the youngest location in the history of S_T->source
* (at or after the YCA) at which all revisions up to BASE are effectively
* merged into S_T->target.
*
* If no locations on the history of S_T->source are effectively merged to
* S_T->target, set *BASE_P to the YCA.
*/
static svn_error_t *
find_base_on_source(svn_client__pathrev_t **base_p,
source_and_target_t *s_t,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
SVN_ERR(find_last_merged_location(base_p,
s_t->yca,
&s_t->source_branch,
s_t->target_branch.tip,
ctx,
s_t->source_ra_session,
result_pool, scratch_pool));
return SVN_NO_ERROR;
}
/* Find a merge base location on the target branch, like in a reintegrate
* merge.
*
* S_T->source
* o-----------o-------o---
* / prev. / \
* -----o merge / \ this
* YCA \ / \ merge
* o-------o---------------o
* BASE S_T->target
*
* Set *BASE_P to BASE, the youngest location in the history of S_T->target
* (at or after the YCA) at which all revisions up to BASE are effectively
* merged into S_T->source.
*
* If no locations on the history of S_T->target are effectively merged to
* S_T->source, set *BASE_P to the YCA.
*/
static svn_error_t *
find_base_on_target(svn_client__pathrev_t **base_p,
source_and_target_t *s_t,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
SVN_ERR(find_last_merged_location(base_p,
s_t->yca,
&s_t->target_branch,
s_t->source,
ctx,
s_t->target_ra_session,
result_pool, scratch_pool));
return SVN_NO_ERROR;
}
/* The body of client_find_automatic_merge(), which see.
*/
static svn_error_t *
find_automatic_merge(svn_client__pathrev_t **base_p,
svn_boolean_t *is_reintegrate_like,
source_and_target_t *s_t,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_client__pathrev_t *base_on_source, *base_on_target;
/* Get the location-history of each branch. */
s_t->source_branch.tip = s_t->source;
SVN_ERR(svn_client__get_history_as_mergeinfo(
&s_t->source_branch.history, &s_t->source_branch.has_r0_history,
s_t->source, SVN_INVALID_REVNUM, SVN_INVALID_REVNUM,
s_t->source_ra_session, ctx, scratch_pool));
s_t->target_branch.tip = &s_t->target->loc;
SVN_ERR(svn_client__get_history_as_mergeinfo(
&s_t->target_branch.history, &s_t->target_branch.has_r0_history,
&s_t->target->loc, SVN_INVALID_REVNUM, SVN_INVALID_REVNUM,
s_t->target_ra_session, ctx, scratch_pool));
SVN_ERR(svn_client__calc_youngest_common_ancestor(
&s_t->yca, s_t->source, s_t->source_branch.history,
s_t->source_branch.has_r0_history,
&s_t->target->loc, s_t->target_branch.history,
s_t->target_branch.has_r0_history,
result_pool, scratch_pool));
if (! s_t->yca)
return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
_("'%s@%ld' must be ancestrally related to "
"'%s@%ld'"),
s_t->source->url, s_t->source->rev,
s_t->target->loc.url, s_t->target->loc.rev);
/* Find the latest revision of A synced to B and the latest
* revision of B synced to A.
*
* base_on_source = youngest_complete_synced_point(source, target)
* base_on_target = youngest_complete_synced_point(target, source)
*/
SVN_ERR(find_base_on_source(&base_on_source, s_t,
ctx, scratch_pool, scratch_pool));
SVN_ERR(find_base_on_target(&base_on_target, s_t,
ctx, scratch_pool, scratch_pool));
/* Choose a base. */
if (base_on_source->rev >= base_on_target->rev)
{
*base_p = base_on_source;
*is_reintegrate_like = FALSE;
}
else
{
*base_p = base_on_target;
*is_reintegrate_like = TRUE;
}
return SVN_NO_ERROR;
}
/** Find out what kind of automatic merge would be needed, when the target
* is only known as a repository location rather than a WC.
*
* Like find_automatic_merge() except that the target is
* specified by @a target_path_or_url at @a target_revision, which must
* refer to a repository location, instead of by a WC path argument.
*/
static svn_error_t *
find_automatic_merge_no_wc(automatic_merge_t **merge_p,
const char *source_path_or_url,
const svn_opt_revision_t *source_revision,
const char *target_path_or_url,
const svn_opt_revision_t *target_revision,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
source_and_target_t *s_t = apr_palloc(scratch_pool, sizeof(*s_t));
svn_client__pathrev_t *target_loc;
automatic_merge_t *merge = apr_palloc(result_pool, sizeof(*merge));
/* Source */
SVN_ERR(svn_client__ra_session_from_path2(
&s_t->source_ra_session, &s_t->source,
source_path_or_url, NULL, source_revision, source_revision,
ctx, result_pool));
/* Target */
SVN_ERR(svn_client__ra_session_from_path2(
&s_t->target_ra_session, &target_loc,
target_path_or_url, NULL, target_revision, target_revision,
ctx, result_pool));
s_t->target = apr_palloc(scratch_pool, sizeof(*s_t->target));
s_t->target->abspath = NULL; /* indicate the target is not a WC */
s_t->target->loc = *target_loc;
SVN_ERR(find_automatic_merge(&merge->base, &merge->is_reintegrate_like, s_t,
ctx, result_pool, scratch_pool));
merge->right = s_t->source;
merge->target = &s_t->target->loc;
merge->yca = s_t->yca;
*merge_p = merge;
return SVN_NO_ERROR;
}
/* Find the information needed to merge all unmerged changes from a source
* branch into a target branch.
*
* Set @a *merge_p to the information needed to merge all unmerged changes
* (up to @a source_revision) from the source branch @a source_path_or_url
* at @a source_revision into the target WC at @a target_abspath.
*
* The flags @a allow_mixed_rev, @a allow_local_mods and
* @a allow_switched_subtrees enable merging into a WC that is in any or all
* of the states described by their names, but only if this function decides
* that the merge will be in the same direction as the last automatic merge.
* If, on the other hand, the last automatic merge was in the opposite
* direction, then such states of the WC are not allowed regardless
* of these flags. This function merely records these flags in the
* @a *merge_p structure; do_automatic_merge_locked() checks the WC
* state for compliance.
*
* Allocate the @a *merge_p structure in @a result_pool.
*/
static svn_error_t *
client_find_automatic_merge(automatic_merge_t **merge_p,
const char *source_path_or_url,
const svn_opt_revision_t *source_revision,
const char *target_abspath,
svn_boolean_t allow_mixed_rev,
svn_boolean_t allow_local_mods,
svn_boolean_t allow_switched_subtrees,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
source_and_target_t *s_t = apr_palloc(result_pool, sizeof(*s_t));
automatic_merge_t *merge = apr_palloc(result_pool, sizeof(*merge));
/* "Open" the target WC. Check the target WC for mixed-rev, local mods and
* switched subtrees yet to faster exit and notify user before contacting
* with server. After we find out what kind of merge is required, then if a
* reintegrate-like merge is required we'll do the stricter checks, in
* do_automatic_merge_locked(). */
SVN_ERR(open_target_wc(&s_t->target, target_abspath,
allow_mixed_rev,
allow_local_mods,
allow_switched_subtrees,
ctx, result_pool, scratch_pool));
/* Open RA sessions to the source and target trees. */
SVN_ERR(svn_client_open_ra_session2(&s_t->target_ra_session,
s_t->target->loc.url,
s_t->target->abspath,
ctx, result_pool, scratch_pool));
/* ### check for null URL (i.e. added path) here, like in reintegrate? */
SVN_ERR(svn_client__ra_session_from_path2(
&s_t->source_ra_session, &s_t->source,
source_path_or_url, NULL, source_revision, source_revision,
ctx, result_pool));
/* Check source is in same repos as target. */
SVN_ERR(check_same_repos(s_t->source, source_path_or_url,
&s_t->target->loc, target_abspath,
TRUE /* strict_urls */, scratch_pool));
SVN_ERR(find_automatic_merge(&merge->base, &merge->is_reintegrate_like, s_t,
ctx, result_pool, scratch_pool));
merge->yca = s_t->yca;
merge->right = s_t->source;
merge->allow_mixed_rev = allow_mixed_rev;
merge->allow_local_mods = allow_local_mods;
merge->allow_switched_subtrees = allow_switched_subtrees;
*merge_p = merge;
/* TODO: Close the source and target sessions here? */
return SVN_NO_ERROR;
}
/* Perform an automatic merge, given the information in MERGE which
* must have come from calling client_find_automatic_merge().
*
* Four locations are inputs: YCA, BASE, RIGHT, TARGET, as shown
* depending on whether the base is on the source branch or the target
* branch of this merge.
*
* RIGHT (is_reintegrate_like)
* o-----------o-------o---
* / prev. / \
* -----o merge / \ this
* YCA \ / \ merge
* o-------o---------------o
* BASE TARGET
*
* or
*
* BASE RIGHT (! is_reintegrate_like)
* o-------o-----------o---
* / \ \
* -----o prev. \ \ this
* YCA \ merge \ \ merge
* o-----------o-----------o
* TARGET
*
* ### TODO: The reintegrate-like code path does not yet
* eliminate already-cherry-picked revisions from the source.
*/
static svn_error_t *
do_automatic_merge_locked(conflict_report_t **conflict_report,
const automatic_merge_t *merge,
const char *target_abspath,
svn_depth_t depth,
svn_boolean_t diff_ignore_ancestry,
svn_boolean_t force_delete,
svn_boolean_t record_only,
svn_boolean_t dry_run,
const apr_array_header_t *merge_options,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
merge_target_t *target;
svn_boolean_t reintegrate_like = merge->is_reintegrate_like;
svn_boolean_t use_sleep = FALSE;
svn_error_t *err;
SVN_ERR(open_target_wc(&target, target_abspath,
merge->allow_mixed_rev && ! reintegrate_like,
merge->allow_local_mods && ! reintegrate_like,
merge->allow_switched_subtrees && ! reintegrate_like,
ctx, scratch_pool, scratch_pool));
if (reintegrate_like)
{
merge_source_t source;
svn_ra_session_t *base_ra_session = NULL;
svn_ra_session_t *right_ra_session = NULL;
svn_ra_session_t *target_ra_session = NULL;
if (record_only)
return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
_("The required merge is reintegrate-like, "
"and the record-only option "
"cannot be used with this kind of merge"));
if (depth != svn_depth_unknown)
return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
_("The required merge is reintegrate-like, "
"and the depth option "
"cannot be used with this kind of merge"));
if (force_delete)
return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
_("The required merge is reintegrate-like, "
"and the force_delete option "
"cannot be used with this kind of merge"));
SVN_ERR(ensure_ra_session_url(&base_ra_session, merge->base->url,
target->abspath, ctx, scratch_pool));
SVN_ERR(ensure_ra_session_url(&right_ra_session, merge->right->url,
target->abspath, ctx, scratch_pool));
SVN_ERR(ensure_ra_session_url(&target_ra_session, target->loc.url,
target->abspath, ctx, scratch_pool));
/* Check for and reject any abnormalities -- such as revisions that
* have not yet been merged in the opposite direction -- that a
* 'reintegrate' merge would have rejected. */
{
merge_source_t *source2;
SVN_ERR(find_reintegrate_merge(&source2, NULL,
right_ra_session, merge->right,
target_ra_session, target,
ctx, scratch_pool, scratch_pool));
}
source.loc1 = merge->base;
source.loc2 = merge->right;
source.ancestral = ! merge->is_reintegrate_like;
err = merge_cousins_and_supplement_mergeinfo(conflict_report,
&use_sleep,
target,
base_ra_session,
right_ra_session,
&source, merge->yca,
TRUE /* same_repos */,
depth,
FALSE /*diff_ignore_ancestry*/,
force_delete, record_only,
dry_run,
merge_options,
ctx,
result_pool, scratch_pool);
}
else /* ! merge->is_reintegrate_like */
{
/* Ignoring the base that we found, we pass the YCA instead and let
do_merge() work out which subtrees need which revision ranges to
be merged. This enables do_merge() to fill in revision-range
gaps that are older than the base that we calculated (which is
for the root path of the merge).
An improvement would be to change find_automatic_merge() to
find the base for each sutree, and then here use the oldest base
among all subtrees. */
apr_array_header_t *merge_sources;
svn_ra_session_t *ra_session = NULL;
/* Normalize our merge sources, do_merge() requires this. See the
'MERGEINFO MERGE SOURCE NORMALIZATION' global comment. */
SVN_ERR(ensure_ra_session_url(&ra_session, merge->right->url,
target->abspath, ctx, scratch_pool));
SVN_ERR(normalize_merge_sources_internal(
&merge_sources, merge->right,
svn_rangelist__initialize(merge->yca->rev, merge->right->rev, TRUE,
scratch_pool),
ra_session, ctx, scratch_pool, scratch_pool));
err = do_merge(NULL, NULL, conflict_report, &use_sleep,
merge_sources, target, ra_session,
TRUE /*related*/, TRUE /*same_repos*/,
FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry,
force_delete, dry_run,
record_only, NULL, FALSE, FALSE, depth, merge_options,
ctx, result_pool, scratch_pool);
}
if (use_sleep)
svn_io_sleep_for_timestamps(target_abspath, scratch_pool);
SVN_ERR(err);
return SVN_NO_ERROR;
}
svn_error_t *
svn_client_get_merging_summary(svn_boolean_t *needs_reintegration,
const char **yca_url, svn_revnum_t *yca_rev,
const char **base_url, svn_revnum_t *base_rev,
const char **right_url, svn_revnum_t *right_rev,
const char **target_url, svn_revnum_t *target_rev,
const char **repos_root_url,
const char *source_path_or_url,
const svn_opt_revision_t *source_revision,
const char *target_path_or_url,
const svn_opt_revision_t *target_revision,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_boolean_t target_is_wc;
automatic_merge_t *merge;
target_is_wc = (! svn_path_is_url(target_path_or_url))
&& (target_revision->kind == svn_opt_revision_unspecified
|| target_revision->kind == svn_opt_revision_working);
if (target_is_wc)
SVN_ERR(client_find_automatic_merge(
&merge,
source_path_or_url, source_revision,
target_path_or_url,
TRUE, TRUE, TRUE, /* allow_* */
ctx, scratch_pool, scratch_pool));
else
SVN_ERR(find_automatic_merge_no_wc(
&merge,
source_path_or_url, source_revision,
target_path_or_url, target_revision,
ctx, scratch_pool, scratch_pool));
if (needs_reintegration)
*needs_reintegration = merge->is_reintegrate_like;
if (yca_url)
*yca_url = apr_pstrdup(result_pool, merge->yca->url);
if (yca_rev)
*yca_rev = merge->yca->rev;
if (base_url)
*base_url = apr_pstrdup(result_pool, merge->base->url);
if (base_rev)
*base_rev = merge->base->rev;
if (right_url)
*right_url = apr_pstrdup(result_pool, merge->right->url);
if (right_rev)
*right_rev = merge->right->rev;
if (target_url)
*target_url = apr_pstrdup(result_pool, merge->target->url);
if (target_rev)
*target_rev = merge->target->rev;
if (repos_root_url)
*repos_root_url = apr_pstrdup(result_pool, merge->yca->repos_root_url);
return SVN_NO_ERROR;
}
Index: vendor/subversion/dist/subversion/libsvn_client/patch.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_client/patch.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_client/patch.c (revision 286501)
@@ -1,3043 +1,3097 @@
/*
* patch.c: patch application support
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
/* ==================================================================== */
/*** Includes. ***/
#include <apr_hash.h>
#include <apr_fnmatch.h>
#include "svn_client.h"
#include "svn_dirent_uri.h"
#include "svn_diff.h"
#include "svn_hash.h"
#include "svn_io.h"
#include "svn_path.h"
#include "svn_pools.h"
#include "svn_props.h"
#include "svn_sorts.h"
#include "svn_subst.h"
#include "svn_wc.h"
#include "client.h"
#include "svn_private_config.h"
#include "private/svn_eol_private.h"
#include "private/svn_wc_private.h"
#include "private/svn_dep_compat.h"
#include "private/svn_string_private.h"
#include "private/svn_subr_private.h"
typedef struct hunk_info_t {
/* The hunk. */
svn_diff_hunk_t *hunk;
/* The line where the hunk matched in the target file. */
svn_linenum_t matched_line;
/* Whether this hunk has been rejected. */
svn_boolean_t rejected;
/* Whether this hunk has already been applied (either manually
* or by an earlier run of patch). */
svn_boolean_t already_applied;
/* The fuzz factor used when matching this hunk, i.e. how many
* lines of leading and trailing context to ignore during matching. */
svn_linenum_t fuzz;
} hunk_info_t;
/* A struct carrying information related to the patched and unpatched
* content of a target, be it a property or the text of a file. */
typedef struct target_content_t {
/* Indicates whether unpatched content existed prior to patching. */
svn_boolean_t existed;
/* The line last read from the unpatched content. */
svn_linenum_t current_line;
/* The EOL-style of the unpatched content. Either 'none', 'fixed',
* or 'native'. See the documentation of svn_subst_eol_style_t. */
svn_subst_eol_style_t eol_style;
/* If the EOL_STYLE above is not 'none', this is the EOL string
* corresponding to the EOL-style. Else, it is the EOL string the
* last line read from the target file was using. */
const char *eol_str;
/* An array containing apr_off_t offsets marking the beginning of
* each line in the unpatched content. */
apr_array_header_t *lines;
/* An array containing hunk_info_t structures for hunks already matched. */
apr_array_header_t *hunks;
/* True if end-of-file was reached while reading from the unpatched
* content. */
svn_boolean_t eof;
/* The keywords of the target. They will be contracted when reading
* unpatched content and expanded when writing patched content.
* When patching properties this hash is always empty. */
apr_hash_t *keywords;
/* A callback, with an associated baton, to read a line of unpatched
* content. */
svn_error_t *(*readline)(void *baton, svn_stringbuf_t **line,
const char **eol_str, svn_boolean_t *eof,
apr_pool_t *result_pool, apr_pool_t *scratch_pool);
void *read_baton;
/* A callback to get the current byte offset within the unpatched
* content. Uses the read baton. */
svn_error_t * (*tell)(void *baton, apr_off_t *offset,
apr_pool_t *scratch_pool);
/* A callback to seek to an offset within the unpatched content.
* Uses the read baton. */
svn_error_t * (*seek)(void *baton, apr_off_t offset,
apr_pool_t *scratch_pool);
/* A callback to write data to the patched content, with an
* associated baton. */
svn_error_t * (*write)(void *baton, const char *buf, apr_size_t len,
apr_pool_t *scratch_pool);
void *write_baton;
} target_content_t;
typedef struct prop_patch_target_t {
/* The name of the property */
const char *name;
/* The property value. This is NULL in case the property did not exist
* prior to patch application (see also CONTENT->existed).
* Note that the patch implementation does not support binary properties,
* so this string is not expected to contain embedded NUL characters. */
const svn_string_t *value;
/* The patched property value.
* This is equivalent to the target, except that in appropriate
* places it contains the modified text as it appears in the patch file. */
svn_stringbuf_t *patched_value;
/* All information that is specific to the content of the property. */
target_content_t *content;
/* Represents the operation performed on the property. It can be added,
* deleted or modified.
* ### Should we use flags instead since we're not using all enum values? */
svn_diff_operation_kind_t operation;
/* ### Here we'll add flags telling if the prop was added, deleted,
* ### had_rejects, had_local_mods prior to patching and so on. */
} prop_patch_target_t;
typedef struct patch_target_t {
/* The target path as it appeared in the patch file,
* but in canonicalised form. */
const char *canon_path_from_patchfile;
/* The target path, relative to the working copy directory the
* patch is being applied to. A patch strip count applies to this
* and only this path. This is never NULL. */
const char *local_relpath;
/* The absolute path of the target on the filesystem.
* Any symlinks the path from the patch file may contain are resolved.
* Is not always known, so it may be NULL. */
const char *local_abspath;
/* The target file, read-only. This is NULL in case the target
* file did not exist prior to patch application (see also
* CONTENT->existed). */
apr_file_t *file;
/* The target file is a symlink */
svn_boolean_t is_symlink;
/* The patched file.
* This is equivalent to the target, except that in appropriate
* places it contains the modified text as it appears in the patch file.
* The data in this file is written in repository-normal form.
* EOL transformation and keyword contraction is performed when the
* patched result is installed in the working copy. */
apr_file_t *patched_file;
/* Path to the patched file. */
const char *patched_path;
/* Hunks that are rejected will be written to this file. */
apr_file_t *reject_file;
/* Path to the reject file. */
const char *reject_path;
/* The node kind of the target as found in WC-DB prior
* to patch application. */
svn_node_kind_t db_kind;
/* The target's kind on disk prior to patch application. */
svn_node_kind_t kind_on_disk;
/* True if the target was locally deleted prior to patching. */
svn_boolean_t locally_deleted;
/* True if the target had to be skipped for some reason. */
svn_boolean_t skipped;
/* True if the target has been filtered by the patch callback. */
svn_boolean_t filtered;
/* True if at least one hunk was rejected. */
svn_boolean_t had_rejects;
/* True if at least one property hunk was rejected. */
svn_boolean_t had_prop_rejects;
/* True if the target file had local modifications before the
* patch was applied to it. */
svn_boolean_t local_mods;
/* True if the target was added by the patch, which means that it did
* not exist on disk before patching and has content after patching. */
svn_boolean_t added;
/* True if the target ended up being deleted by the patch. */
svn_boolean_t deleted;
/* True if the target ended up being replaced by the patch
* (i.e. a new file was added on top locally deleted node). */
svn_boolean_t replaced;
/* True if the target has the executable bit set. */
svn_boolean_t executable;
/* True if the patch changed the text of the target. */
svn_boolean_t has_text_changes;
/* True if the patch changed any of the properties of the target. */
svn_boolean_t has_prop_changes;
/* True if the patch contained a svn:special property. */
svn_boolean_t is_special;
/* All the information that is specific to the content of the target. */
target_content_t *content;
/* A hash table of prop_patch_target_t objects keyed by property names. */
apr_hash_t *prop_targets;
} patch_target_t;
/* A smaller struct containing a subset of patch_target_t.
* Carries the minimal amount of information we still need for a
* target after we're done patching it so we can free other resources. */
typedef struct patch_target_info_t {
const char *local_abspath;
svn_boolean_t deleted;
} patch_target_info_t;
/* Strip STRIP_COUNT components from the front of PATH, returning
* the result in *RESULT, allocated in RESULT_POOL.
* Do temporary allocations in SCRATCH_POOL. */
static svn_error_t *
strip_path(const char **result, const char *path, int strip_count,
apr_pool_t *result_pool, apr_pool_t *scratch_pool)
{
int i;
apr_array_header_t *components;
apr_array_header_t *stripped;
components = svn_path_decompose(path, scratch_pool);
if (strip_count > components->nelts)
return svn_error_createf(SVN_ERR_CLIENT_PATCH_BAD_STRIP_COUNT, NULL,
_("Cannot strip %u components from '%s'"),
strip_count,
svn_dirent_local_style(path, scratch_pool));
stripped = apr_array_make(scratch_pool, components->nelts - strip_count,
sizeof(const char *));
for (i = strip_count; i < components->nelts; i++)
{
const char *component;
component = APR_ARRAY_IDX(components, i, const char *);
APR_ARRAY_PUSH(stripped, const char *) = component;
}
*result = svn_path_compose(stripped, result_pool);
return SVN_NO_ERROR;
}
/* Obtain KEYWORDS, EOL_STYLE and EOL_STR for LOCAL_ABSPATH.
* WC_CTX is a context for the working copy the patch is applied to.
* Use RESULT_POOL for allocations of fields in TARGET.
* Use SCRATCH_POOL for all other allocations. */
static svn_error_t *
obtain_eol_and_keywords_for_file(apr_hash_t **keywords,
svn_subst_eol_style_t *eol_style,
const char **eol_str,
svn_wc_context_t *wc_ctx,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_hash_t *props;
svn_string_t *keywords_val, *eol_style_val;
SVN_ERR(svn_wc_prop_list2(&props, wc_ctx, local_abspath,
scratch_pool, scratch_pool));
keywords_val = svn_hash_gets(props, SVN_PROP_KEYWORDS);
if (keywords_val)
{
svn_revnum_t changed_rev;
apr_time_t changed_date;
const char *rev_str;
const char *author;
const char *url;
const char *root_url;
SVN_ERR(svn_wc__node_get_changed_info(&changed_rev,
&changed_date,
&author, wc_ctx,
local_abspath,
scratch_pool,
scratch_pool));
rev_str = apr_psprintf(scratch_pool, "%ld", changed_rev);
SVN_ERR(svn_wc__node_get_url(&url, wc_ctx,
local_abspath,
scratch_pool, scratch_pool));
SVN_ERR(svn_wc__node_get_repos_info(NULL, NULL, &root_url, NULL,
wc_ctx, local_abspath,
scratch_pool, scratch_pool));
SVN_ERR(svn_subst_build_keywords3(keywords,
keywords_val->data,
rev_str, url, root_url, changed_date,
author, result_pool));
}
eol_style_val = svn_hash_gets(props, SVN_PROP_EOL_STYLE);
if (eol_style_val)
{
svn_subst_eol_style_from_value(eol_style,
eol_str,
eol_style_val->data);
}
return SVN_NO_ERROR;
}
/* Resolve the exact path for a patch TARGET at path PATH_FROM_PATCHFILE,
* which is the path of the target as it appeared in the patch file.
* Put a canonicalized version of PATH_FROM_PATCHFILE into
* TARGET->CANON_PATH_FROM_PATCHFILE.
* WC_CTX is a context for the working copy the patch is applied to.
* If possible, determine TARGET->WC_PATH, TARGET->ABS_PATH, TARGET->KIND,
* TARGET->ADDED, and TARGET->PARENT_DIR_EXISTS.
* Indicate in TARGET->SKIPPED whether the target should be skipped.
* STRIP_COUNT specifies the number of leading path components
* which should be stripped from target paths in the patch.
* PROP_CHANGES_ONLY specifies whether the target path is allowed to have
* only property changes, and no content changes (in which case the target
* must be a directory).
* Use RESULT_POOL for allocations of fields in TARGET.
* Use SCRATCH_POOL for all other allocations. */
static svn_error_t *
resolve_target_path(patch_target_t *target,
const char *path_from_patchfile,
const char *wcroot_abspath,
int strip_count,
svn_boolean_t prop_changes_only,
svn_wc_context_t *wc_ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *stripped_path;
svn_wc_status3_t *status;
svn_error_t *err;
svn_boolean_t under_root;
target->canon_path_from_patchfile = svn_dirent_internal_style(
path_from_patchfile, result_pool);
/* We allow properties to be set on the wc root dir. */
if (! prop_changes_only && target->canon_path_from_patchfile[0] == '\0')
{
/* An empty patch target path? What gives? Skip this. */
target->skipped = TRUE;
target->local_abspath = NULL;
target->local_relpath = "";
return SVN_NO_ERROR;
}
if (strip_count > 0)
SVN_ERR(strip_path(&stripped_path, target->canon_path_from_patchfile,
strip_count, result_pool, scratch_pool));
else
stripped_path = target->canon_path_from_patchfile;
if (svn_dirent_is_absolute(stripped_path))
{
target->local_relpath = svn_dirent_is_child(wcroot_abspath,
stripped_path,
result_pool);
if (! target->local_relpath)
{
/* The target path is either outside of the working copy
* or it is the working copy itself. Skip it. */
target->skipped = TRUE;
target->local_abspath = NULL;
target->local_relpath = stripped_path;
return SVN_NO_ERROR;
}
}
else
{
target->local_relpath = stripped_path;
}
/* Make sure the path is secure to use. We want the target to be inside
* of the working copy and not be fooled by symlinks it might contain. */
SVN_ERR(svn_dirent_is_under_root(&under_root,
&target->local_abspath, wcroot_abspath,
target->local_relpath, result_pool));
if (! under_root)
{
/* The target path is outside of the working copy. Skip it. */
target->skipped = TRUE;
target->local_abspath = NULL;
return SVN_NO_ERROR;
}
/* Skip things we should not be messing with. */
err = svn_wc_status3(&status, wc_ctx, target->local_abspath,
result_pool, scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
svn_error_clear(err);
target->locally_deleted = TRUE;
target->db_kind = svn_node_none;
status = NULL;
}
else if (status->node_status == svn_wc_status_ignored ||
status->node_status == svn_wc_status_unversioned ||
status->node_status == svn_wc_status_missing ||
status->node_status == svn_wc_status_obstructed ||
status->conflicted)
{
target->skipped = TRUE;
return SVN_NO_ERROR;
}
else if (status->node_status == svn_wc_status_deleted)
{
target->locally_deleted = TRUE;
}
if (status && (status->kind != svn_node_unknown))
target->db_kind = status->kind;
else
target->db_kind = svn_node_none;
SVN_ERR(svn_io_check_special_path(target->local_abspath,
&target->kind_on_disk, &target->is_symlink,
scratch_pool));
if (target->locally_deleted)
{
const char *moved_to_abspath;
SVN_ERR(svn_wc__node_was_moved_away(&moved_to_abspath, NULL,
wc_ctx, target->local_abspath,
result_pool, scratch_pool));
if (moved_to_abspath)
{
target->local_abspath = moved_to_abspath;
target->local_relpath = svn_dirent_skip_ancestor(wcroot_abspath,
moved_to_abspath);
SVN_ERR_ASSERT(target->local_relpath &&
target->local_relpath[0] != '\0');
/* As far as we are concerned this target is not locally deleted. */
target->locally_deleted = FALSE;
SVN_ERR(svn_io_check_special_path(target->local_abspath,
&target->kind_on_disk,
&target->is_symlink,
scratch_pool));
}
else if (target->kind_on_disk != svn_node_none)
{
target->skipped = TRUE;
return SVN_NO_ERROR;
}
}
return SVN_NO_ERROR;
}
/* Baton for reading from properties. */
typedef struct prop_read_baton_t {
const svn_string_t *value;
apr_off_t offset;
} prop_read_baton_t;
/* Allocate *STRINGBUF in RESULT_POOL, and read into it one line from
* the unpatched property value accessed via BATON.
* Reading stops either after a line-terminator was found, or if
* the property value runs out in which case *EOF is set to TRUE.
* The line-terminator is not stored in *STRINGBUF.
*
* If the line is empty or could not be read, *line is set to NULL.
*
* The line-terminator is detected automatically and stored in *EOL
* if EOL is not NULL. If the end of the property value is reached
* and does not end with a newline character, and EOL is not NULL,
* *EOL is set to NULL.
*
* SCRATCH_POOL is used for temporary allocations.
*/
static svn_error_t *
readline_prop(void *baton, svn_stringbuf_t **line, const char **eol_str,
svn_boolean_t *eof, apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
prop_read_baton_t *b = (prop_read_baton_t *)baton;
svn_stringbuf_t *str = NULL;
const char *c;
svn_boolean_t found_eof;
if ((apr_uint64_t)b->offset >= (apr_uint64_t)b->value->len)
{
*eol_str = NULL;
*eof = TRUE;
*line = NULL;
return SVN_NO_ERROR;
}
/* Read bytes into STR up to and including, but not storing,
* the next EOL sequence. */
*eol_str = NULL;
found_eof = FALSE;
do
{
c = b->value->data + b->offset;
b->offset++;
if (*c == '\0')
{
found_eof = TRUE;
break;
}
else if (*c == '\n')
{
*eol_str = "\n";
}
else if (*c == '\r')
{
*eol_str = "\r";
if (*(c + 1) == '\n')
{
*eol_str = "\r\n";
b->offset++;
}
}
else
{
if (str == NULL)
str = svn_stringbuf_create_ensure(80, result_pool);
svn_stringbuf_appendbyte(str, *c);
}
if (*eol_str)
break;
}
while (c < b->value->data + b->value->len);
if (eof)
*eof = found_eof;
*line = str;
return SVN_NO_ERROR;
}
/* Return in *OFFSET the current byte offset for reading from the
* unpatched property value accessed via BATON.
* Use SCRATCH_POOL for temporary allocations. */
static svn_error_t *
tell_prop(void *baton, apr_off_t *offset, apr_pool_t *scratch_pool)
{
prop_read_baton_t *b = (prop_read_baton_t *)baton;
*offset = b->offset;
return SVN_NO_ERROR;
}
/* Seek to the specified by OFFSET in the unpatched property value accessed
* via BATON. Use SCRATCH_POOL for temporary allocations. */
static svn_error_t *
seek_prop(void *baton, apr_off_t offset, apr_pool_t *scratch_pool)
{
prop_read_baton_t *b = (prop_read_baton_t *)baton;
b->offset = offset;
return SVN_NO_ERROR;
}
/* Write LEN bytes from BUF into the patched property value accessed
* via BATON. Use SCRATCH_POOL for temporary allocations. */
static svn_error_t *
write_prop(void *baton, const char *buf, apr_size_t len,
apr_pool_t *scratch_pool)
{
svn_stringbuf_t *patched_value = (svn_stringbuf_t *)baton;
svn_stringbuf_appendbytes(patched_value, buf, len);
return SVN_NO_ERROR;
}
/* Initialize a PROP_TARGET structure for PROP_NAME on the patch target
* at LOCAL_ABSPATH. OPERATION indicates the operation performed on the
* property. Use working copy context WC_CTX.
* Allocate results in RESULT_POOL.
* Use SCRATCH_POOL for temporary allocations. */
static svn_error_t *
init_prop_target(prop_patch_target_t **prop_target,
const char *prop_name,
svn_diff_operation_kind_t operation,
svn_wc_context_t *wc_ctx,
const char *local_abspath,
apr_pool_t *result_pool, apr_pool_t *scratch_pool)
{
prop_patch_target_t *new_prop_target;
target_content_t *content;
const svn_string_t *value;
svn_error_t *err;
prop_read_baton_t *prop_read_baton;
content = apr_pcalloc(result_pool, sizeof(*content));
/* All other fields are FALSE or NULL due to apr_pcalloc(). */
content->current_line = 1;
content->eol_style = svn_subst_eol_style_none;
content->lines = apr_array_make(result_pool, 0, sizeof(apr_off_t));
content->hunks = apr_array_make(result_pool, 0, sizeof(hunk_info_t *));
content->keywords = apr_hash_make(result_pool);
new_prop_target = apr_pcalloc(result_pool, sizeof(*new_prop_target));
new_prop_target->name = apr_pstrdup(result_pool, prop_name);
new_prop_target->operation = operation;
new_prop_target->content = content;
err = svn_wc_prop_get2(&value, wc_ctx, local_abspath, prop_name,
result_pool, scratch_pool);
if (err)
{
if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
{
svn_error_clear(err);
value = NULL;
}
else
return svn_error_trace(err);
}
content->existed = (value != NULL);
new_prop_target->value = value;
new_prop_target->patched_value = svn_stringbuf_create_empty(result_pool);
/* Wire up the read and write callbacks. */
prop_read_baton = apr_pcalloc(result_pool, sizeof(*prop_read_baton));
prop_read_baton->value = value;
prop_read_baton->offset = 0;
content->readline = readline_prop;
content->tell = tell_prop;
content->seek = seek_prop;
content->read_baton = prop_read_baton;
content->write = write_prop;
content->write_baton = new_prop_target->patched_value;
*prop_target = new_prop_target;
return SVN_NO_ERROR;
}
/* Allocate *STRINGBUF in RESULT_POOL, and read into it one line from
* the unpatched file content accessed via BATON.
* Reading stops either after a line-terminator was found,
* or if EOF is reached in which case *EOF is set to TRUE.
* The line-terminator is not stored in *STRINGBUF.
*
* If the line is empty or could not be read, *line is set to NULL.
*
* The line-terminator is detected automatically and stored in *EOL
* if EOL is not NULL. If EOF is reached and FILE does not end
* with a newline character, and EOL is not NULL, *EOL is set to NULL.
*
* SCRATCH_POOL is used for temporary allocations.
*/
static svn_error_t *
readline_file(void *baton, svn_stringbuf_t **line, const char **eol_str,
svn_boolean_t *eof, apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_file_t *file = (apr_file_t *)baton;
svn_stringbuf_t *str = NULL;
apr_size_t numbytes;
char c;
svn_boolean_t found_eof;
/* Read bytes into STR up to and including, but not storing,
* the next EOL sequence. */
*eol_str = NULL;
numbytes = 1;
found_eof = FALSE;
while (!found_eof)
{
SVN_ERR(svn_io_file_read_full2(file, &c, sizeof(c), &numbytes,
&found_eof, scratch_pool));
if (numbytes != 1)
{
found_eof = TRUE;
break;
}
if (c == '\n')
{
*eol_str = "\n";
}
else if (c == '\r')
{
*eol_str = "\r";
if (!found_eof)
{
apr_off_t pos;
/* Check for "\r\n" by peeking at the next byte. */
pos = 0;
SVN_ERR(svn_io_file_seek(file, APR_CUR, &pos, scratch_pool));
SVN_ERR(svn_io_file_read_full2(file, &c, sizeof(c), &numbytes,
&found_eof, scratch_pool));
if (numbytes == 1 && c == '\n')
{
*eol_str = "\r\n";
}
else
{
/* Pretend we never peeked. */
SVN_ERR(svn_io_file_seek(file, APR_SET, &pos, scratch_pool));
found_eof = FALSE;
numbytes = 1;
}
}
}
else
{
if (str == NULL)
str = svn_stringbuf_create_ensure(80, result_pool);
svn_stringbuf_appendbyte(str, c);
}
if (*eol_str)
break;
}
if (eof)
*eof = found_eof;
*line = str;
return SVN_NO_ERROR;
}
/* Return in *OFFSET the current byte offset for reading from the
* unpatched file content accessed via BATON.
* Use SCRATCH_POOL for temporary allocations. */
static svn_error_t *
tell_file(void *baton, apr_off_t *offset, apr_pool_t *scratch_pool)
{
apr_file_t *file = (apr_file_t *)baton;
*offset = 0;
SVN_ERR(svn_io_file_seek(file, APR_CUR, offset, scratch_pool));
return SVN_NO_ERROR;
}
/* Seek to the specified by OFFSET in the unpatched file content accessed
* via BATON. Use SCRATCH_POOL for temporary allocations. */
static svn_error_t *
seek_file(void *baton, apr_off_t offset, apr_pool_t *scratch_pool)
{
apr_file_t *file = (apr_file_t *)baton;
SVN_ERR(svn_io_file_seek(file, APR_SET, &offset, scratch_pool));
return SVN_NO_ERROR;
}
/* Write LEN bytes from BUF into the patched file content accessed
* via BATON. Use SCRATCH_POOL for temporary allocations. */
static svn_error_t *
write_file(void *baton, const char *buf, apr_size_t len,
apr_pool_t *scratch_pool)
{
apr_file_t *file = (apr_file_t *)baton;
SVN_ERR(svn_io_file_write_full(file, buf, len, &len, scratch_pool));
return SVN_NO_ERROR;
}
/* Handling symbolic links:
*
* In Subversion, symlinks can be represented on disk in two distinct ways.
* On systems which support symlinks, a symlink is created on disk.
* On systems which do not support symlink, a file is created on disk
* which contains the "normal form" of the symlink, which looks like:
* link TARGET
* where TARGET is the file the symlink points to.
*
* When reading symlinks (i.e. the link itself, not the file the symlink
* is pointing to) through the svn_subst_create_specialfile() function
* into a buffer, the buffer always contains the "normal form" of the symlink.
* Due to this representation symlinks always contain a single line of text.
*
* The functions below are needed to deal with the case where a patch
* wants to change the TARGET that a symlink points to.
*/
/* Baton for the (readline|tell|seek|write)_symlink functions. */
struct symlink_baton_t
{
/* The path to the symlink on disk (not the path to the target of the link) */
const char *local_abspath;
/* Indicates whether the "normal form" of the symlink has been read. */
svn_boolean_t at_eof;
};
/* Allocate *STRINGBUF in RESULT_POOL, and store into it the "normal form"
* of the symlink accessed via BATON.
*
* Otherwise behaves like readline_file(), which see.
*/
static svn_error_t *
readline_symlink(void *baton, svn_stringbuf_t **line, const char **eol_str,
svn_boolean_t *eof, apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
struct symlink_baton_t *sb = baton;
if (eof)
*eof = TRUE;
if (eol_str)
*eol_str = NULL;
if (sb->at_eof)
{
*line = NULL;
}
else
{
svn_string_t *dest;
SVN_ERR(svn_io_read_link(&dest, sb->local_abspath, scratch_pool));
*line = svn_stringbuf_createf(result_pool, "link %s", dest->data);
sb->at_eof = TRUE;
}
return SVN_NO_ERROR;
}
/* Set *OFFSET to 1 or 0 depending on whether the "normal form" of
* the symlink has already been read. */
static svn_error_t *
tell_symlink(void *baton, apr_off_t *offset, apr_pool_t *scratch_pool)
{
struct symlink_baton_t *sb = baton;
*offset = sb->at_eof ? 1 : 0;
return SVN_NO_ERROR;
}
/* If offset is non-zero, mark the symlink as having been read in its
* "normal form". Else, mark the symlink as not having been read yet. */
static svn_error_t *
seek_symlink(void *baton, apr_off_t offset, apr_pool_t *scratch_pool)
{
struct symlink_baton_t *sb = baton;
sb->at_eof = (offset != 0);
return SVN_NO_ERROR;
}
/* Set the target of the symlink accessed via BATON.
* The contents of BUF must be a valid "normal form" of a symlink. */
static svn_error_t *
write_symlink(void *baton, const char *buf, apr_size_t len,
apr_pool_t *scratch_pool)
{
const char *target_abspath = baton;
const char *new_name;
const char *link = apr_pstrndup(scratch_pool, buf, len);
if (strncmp(link, "link ", 5) != 0)
return svn_error_create(SVN_ERR_IO_WRITE_ERROR, NULL,
_("Invalid link representation"));
link += 5; /* Skip "link " */
/* We assume the entire symlink is written at once, as the patch
format is line based */
SVN_ERR(svn_io_create_unique_link(&new_name, target_abspath, link,
".tmp", scratch_pool));
SVN_ERR(svn_io_file_rename(new_name, target_abspath, scratch_pool));
return SVN_NO_ERROR;
}
/* Return a suitable filename for the target of PATCH.
* Examine the ``old'' and ``new'' file names, and choose the file name
* with the fewest path components, the shortest basename, and the shortest
* total file name length (in that order). In case of a tie, return the new
* filename. This heuristic is also used by Larry Wall's UNIX patch (except
* that it prompts for a filename in case of a tie).
* Additionally, for compatibility with git, if one of the filenames
* is "/dev/null", use the other filename. */
static const char *
choose_target_filename(const svn_patch_t *patch)
{
apr_size_t old;
apr_size_t new;
if (strcmp(patch->old_filename, "/dev/null") == 0)
return patch->new_filename;
if (strcmp(patch->new_filename, "/dev/null") == 0)
return patch->old_filename;
old = svn_path_component_count(patch->old_filename);
new = svn_path_component_count(patch->new_filename);
if (old == new)
{
old = strlen(svn_dirent_basename(patch->old_filename, NULL));
new = strlen(svn_dirent_basename(patch->new_filename, NULL));
if (old == new)
{
old = strlen(patch->old_filename);
new = strlen(patch->new_filename);
}
}
return (old < new) ? patch->old_filename : patch->new_filename;
}
/* Attempt to initialize a *PATCH_TARGET structure for a target file
* described by PATCH. Use working copy context WC_CTX.
* STRIP_COUNT specifies the number of leading path components
* which should be stripped from target paths in the patch.
* The patch target structure is allocated in RESULT_POOL, but if the target
* should be skipped, PATCH_TARGET->SKIPPED is set and the target should be
* treated as not fully initialized, e.g. the caller should not not do any
* further operations on the target if it is marked to be skipped.
* If REMOVE_TEMPFILES is TRUE, set up temporary files to be removed as
* soon as they are no longer needed.
* Use SCRATCH_POOL for all other allocations. */
static svn_error_t *
init_patch_target(patch_target_t **patch_target,
const svn_patch_t *patch,
const char *wcroot_abspath,
svn_wc_context_t *wc_ctx, int strip_count,
svn_boolean_t remove_tempfiles,
apr_pool_t *result_pool, apr_pool_t *scratch_pool)
{
patch_target_t *target;
target_content_t *content;
svn_boolean_t has_prop_changes = FALSE;
svn_boolean_t prop_changes_only = FALSE;
{
apr_hash_index_t *hi;
for (hi = apr_hash_first(scratch_pool, patch->prop_patches);
hi;
hi = apr_hash_next(hi))
{
svn_prop_patch_t *prop_patch = svn__apr_hash_index_val(hi);
if (! has_prop_changes)
has_prop_changes = prop_patch->hunks->nelts > 0;
else
break;
}
}
prop_changes_only = has_prop_changes && patch->hunks->nelts == 0;
content = apr_pcalloc(result_pool, sizeof(*content));
/* All other fields in content are FALSE or NULL due to apr_pcalloc().*/
content->current_line = 1;
content->eol_style = svn_subst_eol_style_none;
content->lines = apr_array_make(result_pool, 0, sizeof(apr_off_t));
content->hunks = apr_array_make(result_pool, 0, sizeof(hunk_info_t *));
content->keywords = apr_hash_make(result_pool);
target = apr_pcalloc(result_pool, sizeof(*target));
/* All other fields in target are FALSE or NULL due to apr_pcalloc(). */
target->db_kind = svn_node_none;
target->kind_on_disk = svn_node_none;
target->content = content;
target->prop_targets = apr_hash_make(result_pool);
SVN_ERR(resolve_target_path(target, choose_target_filename(patch),
wcroot_abspath, strip_count, prop_changes_only,
wc_ctx, result_pool, scratch_pool));
if (! target->skipped)
{
const char *diff_header;
apr_size_t len;
/* Create a temporary file to write the patched result to.
* Also grab various bits of information about the file. */
if (target->is_symlink)
{
struct symlink_baton_t *sb = apr_pcalloc(result_pool, sizeof(*sb));
content->existed = TRUE;
sb->local_abspath = target->local_abspath;
/* Wire up the read callbacks. */
content->read_baton = sb;
content->readline = readline_symlink;
content->seek = seek_symlink;
content->tell = tell_symlink;
}
else if (target->kind_on_disk == svn_node_file)
{
SVN_ERR(svn_io_file_open(&target->file, target->local_abspath,
APR_READ | APR_BUFFERED,
APR_OS_DEFAULT, result_pool));
SVN_ERR(svn_wc_text_modified_p2(&target->local_mods, wc_ctx,
target->local_abspath, FALSE,
scratch_pool));
SVN_ERR(svn_io_is_file_executable(&target->executable,
target->local_abspath,
scratch_pool));
SVN_ERR(obtain_eol_and_keywords_for_file(&content->keywords,
&content->eol_style,
&content->eol_str,
wc_ctx,
target->local_abspath,
result_pool,
scratch_pool));
content->existed = TRUE;
/* Wire up the read callbacks. */
content->readline = readline_file;
content->seek = seek_file;
content->tell = tell_file;
content->read_baton = target->file;
}
/* ### Is it ok to set the operation of the target already here? Isn't
* ### the target supposed to be marked with an operation after we have
* ### determined that the changes will apply cleanly to the WC? Maybe
* ### we should have kept the patch field in patch_target_t to be
* ### able to distinguish between 'what the patch says we should do'
* ### and 'what we can do with the given state of our WC'. */
if (patch->operation == svn_diff_op_added)
target->added = TRUE;
else if (patch->operation == svn_diff_op_deleted)
target->deleted = TRUE;
if (! target->is_symlink)
{
/* Open a temporary file to write the patched result to. */
SVN_ERR(svn_io_open_unique_file3(&target->patched_file,
&target->patched_path, NULL,
remove_tempfiles ?
svn_io_file_del_on_pool_cleanup :
svn_io_file_del_none,
result_pool, scratch_pool));
/* Put the write callback in place. */
content->write = write_file;
content->write_baton = target->patched_file;
}
else
{
/* Put the write callback in place. */
SVN_ERR(svn_io_open_unique_file3(NULL,
&target->patched_path, NULL,
remove_tempfiles ?
svn_io_file_del_on_pool_cleanup :
svn_io_file_del_none,
result_pool, scratch_pool));
content->write_baton = (void*)target->patched_path;
content->write = write_symlink;
}
/* Open a temporary file to write rejected hunks to. */
SVN_ERR(svn_io_open_unique_file3(&target->reject_file,
&target->reject_path, NULL,
remove_tempfiles ?
svn_io_file_del_on_pool_cleanup :
svn_io_file_del_none,
result_pool, scratch_pool));
/* The reject file needs a diff header. */
diff_header = apr_psprintf(scratch_pool, "--- %s%s+++ %s%s",
target->canon_path_from_patchfile,
APR_EOL_STR,
target->canon_path_from_patchfile,
APR_EOL_STR);
len = strlen(diff_header);
SVN_ERR(svn_io_file_write_full(target->reject_file, diff_header, len,
&len, scratch_pool));
/* Handle properties. */
if (! target->skipped)
{
apr_hash_index_t *hi;
for (hi = apr_hash_first(result_pool, patch->prop_patches);
hi;
hi = apr_hash_next(hi))
{
const char *prop_name = svn__apr_hash_index_key(hi);
svn_prop_patch_t *prop_patch = svn__apr_hash_index_val(hi);
prop_patch_target_t *prop_target;
SVN_ERR(init_prop_target(&prop_target,
prop_name,
prop_patch->operation,
wc_ctx, target->local_abspath,
result_pool, scratch_pool));
svn_hash_sets(target->prop_targets, prop_name, prop_target);
}
}
}
*patch_target = target;
return SVN_NO_ERROR;
}
/* Read a *LINE from CONTENT. If the line has not been read before
* mark the line in CONTENT->LINES.
* If a line could be read successfully, increase CONTENT->CURRENT_LINE,
* and allocate *LINE in RESULT_POOL.
* Do temporary allocations in SCRATCH_POOL.
*/
static svn_error_t *
readline(target_content_t *content,
const char **line,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_stringbuf_t *line_raw;
const char *eol_str;
svn_linenum_t max_line = (svn_linenum_t)content->lines->nelts + 1;
if (content->eof || content->readline == NULL)
{
*line = "";
return SVN_NO_ERROR;
}
SVN_ERR_ASSERT(content->current_line <= max_line);
if (content->current_line == max_line)
{
apr_off_t offset;
SVN_ERR(content->tell(content->read_baton, &offset,
scratch_pool));
APR_ARRAY_PUSH(content->lines, apr_off_t) = offset;
}
SVN_ERR(content->readline(content->read_baton, &line_raw,
&eol_str, &content->eof,
result_pool, scratch_pool));
if (content->eol_style == svn_subst_eol_style_none)
content->eol_str = eol_str;
if (line_raw)
{
/* Contract keywords. */
SVN_ERR(svn_subst_translate_cstring2(line_raw->data, line,
NULL, FALSE,
content->keywords, FALSE,
result_pool));
}
else
*line = "";
if ((line_raw && line_raw->len > 0) || eol_str)
content->current_line++;
SVN_ERR_ASSERT(content->current_line > 0);
return SVN_NO_ERROR;
}
/* Seek to the specified LINE in CONTENT.
* Mark any lines not read before in CONTENT->LINES.
* Do temporary allocations in SCRATCH_POOL.
*/
static svn_error_t *
seek_to_line(target_content_t *content, svn_linenum_t line,
apr_pool_t *scratch_pool)
{
svn_linenum_t saved_line;
svn_boolean_t saved_eof;
SVN_ERR_ASSERT(line > 0);
if (line == content->current_line)
return SVN_NO_ERROR;
saved_line = content->current_line;
saved_eof = content->eof;
if (line <= (svn_linenum_t)content->lines->nelts)
{
apr_off_t offset;
offset = APR_ARRAY_IDX(content->lines, line - 1, apr_off_t);
SVN_ERR(content->seek(content->read_baton, offset,
scratch_pool));
content->current_line = line;
}
else
{
const char *dummy;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
while (! content->eof && content->current_line < line)
{
svn_pool_clear(iterpool);
SVN_ERR(readline(content, &dummy, iterpool, iterpool));
}
svn_pool_destroy(iterpool);
}
/* After seeking backwards from EOF position clear EOF indicator. */
if (saved_eof && saved_line > content->current_line)
content->eof = FALSE;
return SVN_NO_ERROR;
}
/* Indicate in *MATCHED whether the original text of HUNK matches the patch
* CONTENT at its current line. Lines within FUZZ lines of the start or
* end of HUNK will always match. If IGNORE_WHITESPACE is set, we ignore
* whitespace when doing the matching. When this function returns, neither
* CONTENT->CURRENT_LINE nor the file offset in the target file will
* have changed. If MATCH_MODIFIED is TRUE, match the modified hunk text,
* rather than the original hunk text.
* Do temporary allocations in POOL. */
static svn_error_t *
match_hunk(svn_boolean_t *matched, target_content_t *content,
svn_diff_hunk_t *hunk, svn_linenum_t fuzz,
svn_boolean_t ignore_whitespace,
svn_boolean_t match_modified, apr_pool_t *pool)
{
svn_stringbuf_t *hunk_line;
const char *target_line;
svn_linenum_t lines_read;
svn_linenum_t saved_line;
svn_boolean_t hunk_eof;
svn_boolean_t lines_matched;
apr_pool_t *iterpool;
svn_linenum_t hunk_length;
svn_linenum_t leading_context;
svn_linenum_t trailing_context;
*matched = FALSE;
if (content->eof)
return SVN_NO_ERROR;
saved_line = content->current_line;
lines_read = 0;
lines_matched = FALSE;
leading_context = svn_diff_hunk_get_leading_context(hunk);
trailing_context = svn_diff_hunk_get_trailing_context(hunk);
if (match_modified)
{
svn_diff_hunk_reset_modified_text(hunk);
hunk_length = svn_diff_hunk_get_modified_length(hunk);
}
else
{
svn_diff_hunk_reset_original_text(hunk);
hunk_length = svn_diff_hunk_get_original_length(hunk);
}
iterpool = svn_pool_create(pool);
do
{
const char *hunk_line_translated;
svn_pool_clear(iterpool);
if (match_modified)
SVN_ERR(svn_diff_hunk_readline_modified_text(hunk, &hunk_line,
NULL, &hunk_eof,
iterpool, iterpool));
else
SVN_ERR(svn_diff_hunk_readline_original_text(hunk, &hunk_line,
NULL, &hunk_eof,
iterpool, iterpool));
/* Contract keywords, if any, before matching. */
SVN_ERR(svn_subst_translate_cstring2(hunk_line->data,
&hunk_line_translated,
NULL, FALSE,
content->keywords, FALSE,
iterpool));
SVN_ERR(readline(content, &target_line, iterpool, iterpool));
lines_read++;
/* If the last line doesn't have a newline, we get EOF but still
* have a non-empty line to compare. */
if ((hunk_eof && hunk_line->len == 0) ||
(content->eof && *target_line == 0))
break;
/* Leading/trailing fuzzy lines always match. */
if ((lines_read <= fuzz && leading_context > fuzz) ||
(lines_read > hunk_length - fuzz && trailing_context > fuzz))
lines_matched = TRUE;
else
{
if (ignore_whitespace)
{
char *hunk_line_trimmed;
char *target_line_trimmed;
hunk_line_trimmed = apr_pstrdup(iterpool, hunk_line_translated);
target_line_trimmed = apr_pstrdup(iterpool, target_line);
apr_collapse_spaces(hunk_line_trimmed, hunk_line_trimmed);
apr_collapse_spaces(target_line_trimmed, target_line_trimmed);
lines_matched = ! strcmp(hunk_line_trimmed, target_line_trimmed);
}
else
lines_matched = ! strcmp(hunk_line_translated, target_line);
}
}
while (lines_matched);
*matched = lines_matched && hunk_eof && hunk_line->len == 0;
SVN_ERR(seek_to_line(content, saved_line, iterpool));
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Scan lines of CONTENT for a match of the original text of HUNK,
* up to but not including the specified UPPER_LINE. Use fuzz factor FUZZ.
* If UPPER_LINE is zero scan until EOF occurs when reading from TARGET.
* Return the line at which HUNK was matched in *MATCHED_LINE.
* If the hunk did not match at all, set *MATCHED_LINE to zero.
* If the hunk matched multiple times, and MATCH_FIRST is TRUE,
* return the line number at which the first match occurred in *MATCHED_LINE.
* If the hunk matched multiple times, and MATCH_FIRST is FALSE,
* return the line number at which the last match occurred in *MATCHED_LINE.
* If IGNORE_WHITESPACE is set, ignore whitespace during the matching.
* If MATCH_MODIFIED is TRUE, match the modified hunk text,
* rather than the original hunk text.
* Call cancel CANCEL_FUNC with baton CANCEL_BATON to trigger cancellation.
* Do all allocations in POOL. */
static svn_error_t *
scan_for_match(svn_linenum_t *matched_line,
target_content_t *content,
svn_diff_hunk_t *hunk, svn_boolean_t match_first,
svn_linenum_t upper_line, svn_linenum_t fuzz,
svn_boolean_t ignore_whitespace,
svn_boolean_t match_modified,
svn_cancel_func_t cancel_func, void *cancel_baton,
apr_pool_t *pool)
{
apr_pool_t *iterpool;
*matched_line = 0;
iterpool = svn_pool_create(pool);
while ((content->current_line < upper_line || upper_line == 0) &&
! content->eof)
{
svn_boolean_t matched;
svn_pool_clear(iterpool);
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
SVN_ERR(match_hunk(&matched, content, hunk, fuzz, ignore_whitespace,
match_modified, iterpool));
if (matched)
{
svn_boolean_t taken = FALSE;
int i;
/* Don't allow hunks to match at overlapping locations. */
for (i = 0; i < content->hunks->nelts; i++)
{
const hunk_info_t *hi;
svn_linenum_t length;
hi = APR_ARRAY_IDX(content->hunks, i, const hunk_info_t *);
if (match_modified)
length = svn_diff_hunk_get_modified_length(hi->hunk);
else
length = svn_diff_hunk_get_original_length(hi->hunk);
taken = (! hi->rejected &&
content->current_line >= hi->matched_line &&
content->current_line < (hi->matched_line + length));
if (taken)
break;
}
if (! taken)
{
*matched_line = content->current_line;
if (match_first)
break;
}
}
if (! content->eof)
SVN_ERR(seek_to_line(content, content->current_line + 1,
iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Indicate in *MATCH whether the content described by CONTENT
* matches the modified text of HUNK.
* Use SCRATCH_POOL for temporary allocations. */
static svn_error_t *
match_existing_target(svn_boolean_t *match,
target_content_t *content,
svn_diff_hunk_t *hunk,
apr_pool_t *scratch_pool)
{
svn_boolean_t lines_matched;
apr_pool_t *iterpool;
svn_boolean_t hunk_eof;
svn_linenum_t saved_line;
svn_diff_hunk_reset_modified_text(hunk);
saved_line = content->current_line;
iterpool = svn_pool_create(scratch_pool);
do
{
const char *line;
svn_stringbuf_t *hunk_line;
const char *line_translated;
const char *hunk_line_translated;
svn_pool_clear(iterpool);
SVN_ERR(readline(content, &line, iterpool, iterpool));
SVN_ERR(svn_diff_hunk_readline_modified_text(hunk, &hunk_line,
NULL, &hunk_eof,
iterpool, iterpool));
/* Contract keywords. */
SVN_ERR(svn_subst_translate_cstring2(line, &line_translated,
NULL, FALSE,
content->keywords,
FALSE, iterpool));
SVN_ERR(svn_subst_translate_cstring2(hunk_line->data,
&hunk_line_translated,
NULL, FALSE,
content->keywords,
FALSE, iterpool));
lines_matched = ! strcmp(line_translated, hunk_line_translated);
if (content->eof != hunk_eof)
{
svn_pool_destroy(iterpool);
*match = FALSE;
return SVN_NO_ERROR;
}
}
while (lines_matched && ! content->eof && ! hunk_eof);
svn_pool_destroy(iterpool);
*match = (lines_matched && content->eof == hunk_eof);
SVN_ERR(seek_to_line(content, saved_line, scratch_pool));
return SVN_NO_ERROR;
}
/* Determine the line at which a HUNK applies to CONTENT of the TARGET
* file, and return an appropriate hunk_info object in *HI, allocated from
* RESULT_POOL. Use fuzz factor FUZZ. Set HI->FUZZ to FUZZ. If no correct
* line can be determined, set HI->REJECTED to TRUE.
* IGNORE_WHITESPACE tells whether whitespace should be considered when
* matching. IS_PROP_HUNK indicates whether the hunk patches file content
* or a property.
* When this function returns, neither CONTENT->CURRENT_LINE nor
* the file offset in the target file will have changed.
* Call cancel CANCEL_FUNC with baton CANCEL_BATON to trigger cancellation.
* Do temporary allocations in POOL. */
static svn_error_t *
get_hunk_info(hunk_info_t **hi, patch_target_t *target,
target_content_t *content,
svn_diff_hunk_t *hunk, svn_linenum_t fuzz,
svn_boolean_t ignore_whitespace,
svn_boolean_t is_prop_hunk,
svn_cancel_func_t cancel_func, void *cancel_baton,
apr_pool_t *result_pool, apr_pool_t *scratch_pool)
{
svn_linenum_t matched_line;
svn_linenum_t original_start;
svn_boolean_t already_applied;
original_start = svn_diff_hunk_get_original_start(hunk);
already_applied = FALSE;
/* An original offset of zero means that this hunk wants to create
* a new file. Don't bother matching hunks in that case, since
* the hunk applies at line 1. If the file already exists, the hunk
* is rejected, unless the file is versioned and its content matches
* the file the patch wants to create. */
if (original_start == 0 && fuzz > 0)
{
matched_line = 0; /* reject any fuzz for new files */
}
else if (original_start == 0 && ! is_prop_hunk)
{
if (target->kind_on_disk == svn_node_file)
{
const svn_io_dirent2_t *dirent;
SVN_ERR(svn_io_stat_dirent2(&dirent, target->local_abspath, FALSE,
TRUE, scratch_pool, scratch_pool));
if (dirent->kind == svn_node_file
&& !dirent->special
&& dirent->filesize == 0)
{
matched_line = 1; /* Matched an on-disk empty file */
}
else
{
if (target->db_kind == svn_node_file)
{
svn_boolean_t file_matches;
/* ### I can't reproduce anything but a no-match here.
The content is already at eof, so any hunk fails */
SVN_ERR(match_existing_target(&file_matches, content, hunk,
scratch_pool));
if (file_matches)
{
matched_line = 1;
already_applied = TRUE;
}
else
matched_line = 0; /* reject */
}
else
matched_line = 0; /* reject */
}
}
else
matched_line = 1;
}
/* Same conditions apply as for the file case above.
*
* ### Since the hunk says the prop should be added we just assume so for
* ### now and don't bother with storing the previous lines and such. When
* ### we have the diff operation available we can just check for adds. */
else if (original_start == 0 && is_prop_hunk)
{
if (content->existed)
{
svn_boolean_t prop_matches;
SVN_ERR(match_existing_target(&prop_matches, content, hunk,
scratch_pool));
if (prop_matches)
{
matched_line = 1;
already_applied = TRUE;
}
else
matched_line = 0; /* reject */
}
else
matched_line = 1;
}
else if (original_start > 0 && content->existed)
{
svn_linenum_t saved_line = content->current_line;
/* Scan for a match at the line where the hunk thinks it
* should be going. */
SVN_ERR(seek_to_line(content, original_start, scratch_pool));
if (content->current_line != original_start)
{
/* Seek failed. */
matched_line = 0;
}
else
SVN_ERR(scan_for_match(&matched_line, content, hunk, TRUE,
original_start + 1, fuzz,
ignore_whitespace, FALSE,
cancel_func, cancel_baton,
scratch_pool));
if (matched_line != original_start)
{
/* Check if the hunk is already applied.
* We only check for an exact match here, and don't bother checking
* for already applied patches with offset/fuzz, because such a
* check would be ambiguous. */
if (fuzz == 0)
{
svn_linenum_t modified_start;
modified_start = svn_diff_hunk_get_modified_start(hunk);
if (modified_start == 0)
{
/* Patch wants to delete the file. */
already_applied = target->locally_deleted;
}
else
{
SVN_ERR(seek_to_line(content, modified_start,
scratch_pool));
SVN_ERR(scan_for_match(&matched_line, content,
hunk, TRUE,
modified_start + 1,
fuzz, ignore_whitespace, TRUE,
cancel_func, cancel_baton,
scratch_pool));
already_applied = (matched_line == modified_start);
}
}
else
already_applied = FALSE;
if (! already_applied)
{
/* Scan the whole file again from the start. */
SVN_ERR(seek_to_line(content, 1, scratch_pool));
/* Scan forward towards the hunk's line and look for a line
* where the hunk matches. */
SVN_ERR(scan_for_match(&matched_line, content, hunk, FALSE,
original_start, fuzz,
ignore_whitespace, FALSE,
cancel_func, cancel_baton,
scratch_pool));
/* In tie-break situations, we arbitrarily prefer early matches
* to save us from scanning the rest of the file. */
if (matched_line == 0)
{
/* Scan forward towards the end of the file and look
* for a line where the hunk matches. */
SVN_ERR(scan_for_match(&matched_line, content, hunk,
TRUE, 0, fuzz, ignore_whitespace,
FALSE, cancel_func, cancel_baton,
scratch_pool));
}
}
}
SVN_ERR(seek_to_line(content, saved_line, scratch_pool));
}
else
{
/* The hunk wants to modify a file which doesn't exist. */
matched_line = 0;
}
(*hi) = apr_pcalloc(result_pool, sizeof(hunk_info_t));
(*hi)->hunk = hunk;
(*hi)->matched_line = matched_line;
(*hi)->rejected = (matched_line == 0);
(*hi)->already_applied = already_applied;
(*hi)->fuzz = fuzz;
return SVN_NO_ERROR;
}
/* Copy lines to the patched content until the specified LINE has been
* reached. Indicate in *EOF whether end-of-file was encountered while
* reading from the target.
* If LINE is zero, copy lines until end-of-file has been reached.
* Do all allocations in POOL. */
static svn_error_t *
copy_lines_to_target(target_content_t *content, svn_linenum_t line,
apr_pool_t *pool)
{
apr_pool_t *iterpool;
iterpool = svn_pool_create(pool);
while ((content->current_line < line || line == 0) && ! content->eof)
{
const char *target_line;
apr_size_t len;
svn_pool_clear(iterpool);
SVN_ERR(readline(content, &target_line, iterpool, iterpool));
if (! content->eof)
target_line = apr_pstrcat(iterpool, target_line, content->eol_str,
(char *)NULL);
len = strlen(target_line);
SVN_ERR(content->write(content->write_baton, target_line,
len, iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Write the diff text of HUNK to TARGET's reject file,
* and mark TARGET as having had rejects.
* We don't expand keywords, nor normalise line-endings, in reject files.
* Do temporary allocations in SCRATCH_POOL. */
static svn_error_t *
reject_hunk(patch_target_t *target, target_content_t *content,
svn_diff_hunk_t *hunk, const char *prop_name,
apr_pool_t *pool)
{
const char *hunk_header;
apr_size_t len;
svn_boolean_t eof;
static const char * const text_atat = "@@";
static const char * const prop_atat = "##";
const char *atat;
apr_pool_t *iterpool;
if (prop_name)
{
const char *prop_header;
/* ### Print 'Added', 'Deleted' or 'Modified' instead of 'Property'.
*/
prop_header = apr_psprintf(pool, "Property: %s\n", prop_name);
len = strlen(prop_header);
SVN_ERR(svn_io_file_write_full(target->reject_file, prop_header,
len, &len, pool));
atat = prop_atat;
}
else
{
atat = text_atat;
}
hunk_header = apr_psprintf(pool, "%s -%lu,%lu +%lu,%lu %s%s",
atat,
svn_diff_hunk_get_original_start(hunk),
svn_diff_hunk_get_original_length(hunk),
svn_diff_hunk_get_modified_start(hunk),
svn_diff_hunk_get_modified_length(hunk),
atat,
APR_EOL_STR);
len = strlen(hunk_header);
SVN_ERR(svn_io_file_write_full(target->reject_file, hunk_header, len,
&len, pool));
iterpool = svn_pool_create(pool);
do
{
svn_stringbuf_t *hunk_line;
const char *eol_str;
svn_pool_clear(iterpool);
SVN_ERR(svn_diff_hunk_readline_diff_text(hunk, &hunk_line, &eol_str,
&eof, iterpool, iterpool));
if (! eof)
{
if (hunk_line->len >= 1)
{
len = hunk_line->len;
SVN_ERR(svn_io_file_write_full(target->reject_file,
hunk_line->data, len, &len,
iterpool));
}
if (eol_str)
{
len = strlen(eol_str);
SVN_ERR(svn_io_file_write_full(target->reject_file, eol_str,
len, &len, iterpool));
}
}
}
while (! eof);
svn_pool_destroy(iterpool);
if (prop_name)
target->had_prop_rejects = TRUE;
else
target->had_rejects = TRUE;
return SVN_NO_ERROR;
}
/* Write the modified text of the hunk described by HI to the patched
* CONTENT. TARGET is the patch target.
* If PROP_NAME is not NULL, the hunk is assumed to be targeted for
* a property with the given name.
* Do temporary allocations in POOL. */
static svn_error_t *
apply_hunk(patch_target_t *target, target_content_t *content,
hunk_info_t *hi, const char *prop_name, apr_pool_t *pool)
{
svn_linenum_t lines_read;
svn_boolean_t eof;
apr_pool_t *iterpool;
/* ### Is there a cleaner way to describe if we have an existing target?
*/
if (target->kind_on_disk == svn_node_file || prop_name)
{
svn_linenum_t line;
/* Move forward to the hunk's line, copying data as we go.
* Also copy leading lines of context which matched with fuzz.
* The target has changed on the fuzzy-matched lines,
* so we should retain the target's version of those lines. */
SVN_ERR(copy_lines_to_target(content, hi->matched_line + hi->fuzz,
pool));
/* Skip the target's version of the hunk.
* Don't skip trailing lines which matched with fuzz. */
line = content->current_line +
svn_diff_hunk_get_original_length(hi->hunk) - (2 * hi->fuzz);
SVN_ERR(seek_to_line(content, line, pool));
if (content->current_line != line && ! content->eof)
{
/* Seek failed, reject this hunk. */
hi->rejected = TRUE;
SVN_ERR(reject_hunk(target, content, hi->hunk, prop_name, pool));
return SVN_NO_ERROR;
}
}
/* Write the hunk's version to the patched result.
* Don't write the lines which matched with fuzz. */
lines_read = 0;
svn_diff_hunk_reset_modified_text(hi->hunk);
iterpool = svn_pool_create(pool);
do
{
svn_stringbuf_t *hunk_line;
const char *eol_str;
svn_pool_clear(iterpool);
SVN_ERR(svn_diff_hunk_readline_modified_text(hi->hunk, &hunk_line,
&eol_str, &eof,
iterpool, iterpool));
lines_read++;
if (lines_read > hi->fuzz &&
lines_read <= svn_diff_hunk_get_modified_length(hi->hunk) - hi->fuzz)
{
apr_size_t len;
if (hunk_line->len >= 1)
{
len = hunk_line->len;
SVN_ERR(content->write(content->write_baton,
hunk_line->data, len, iterpool));
}
if (eol_str)
{
/* Use the EOL as it was read from the patch file,
* unless the target's EOL style is set by svn:eol-style */
if (content->eol_style != svn_subst_eol_style_none)
eol_str = content->eol_str;
len = strlen(eol_str);
SVN_ERR(content->write(content->write_baton,
eol_str, len, iterpool));
}
}
}
while (! eof);
svn_pool_destroy(iterpool);
if (prop_name)
target->has_prop_changes = TRUE;
else
target->has_text_changes = TRUE;
return SVN_NO_ERROR;
}
/* Use client context CTX to send a suitable notification for hunk HI,
* using TARGET to determine the path. If the hunk is a property hunk,
* PROP_NAME must be the name of the property, else NULL.
* Use POOL for temporary allocations. */
static svn_error_t *
send_hunk_notification(const hunk_info_t *hi,
const patch_target_t *target,
const char *prop_name,
const svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
svn_wc_notify_t *notify;
svn_wc_notify_action_t action;
if (hi->already_applied)
action = svn_wc_notify_patch_hunk_already_applied;
else if (hi->rejected)
action = svn_wc_notify_patch_rejected_hunk;
else
action = svn_wc_notify_patch_applied_hunk;
notify = svn_wc_create_notify(target->local_abspath
? target->local_abspath
: target->local_relpath,
action, pool);
notify->hunk_original_start =
svn_diff_hunk_get_original_start(hi->hunk);
notify->hunk_original_length =
svn_diff_hunk_get_original_length(hi->hunk);
notify->hunk_modified_start =
svn_diff_hunk_get_modified_start(hi->hunk);
notify->hunk_modified_length =
svn_diff_hunk_get_modified_length(hi->hunk);
notify->hunk_matched_line = hi->matched_line;
notify->hunk_fuzz = hi->fuzz;
notify->prop_name = prop_name;
(*ctx->notify_func2)(ctx->notify_baton2, notify, pool);
return SVN_NO_ERROR;
}
/* Use client context CTX to send a suitable notification for a patch TARGET.
* Use POOL for temporary allocations. */
static svn_error_t *
send_patch_notification(const patch_target_t *target,
const svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
svn_wc_notify_t *notify;
svn_wc_notify_action_t action;
if (! ctx->notify_func2)
return SVN_NO_ERROR;
if (target->skipped)
action = svn_wc_notify_skip;
else if (target->deleted)
action = svn_wc_notify_delete;
else if (target->added || target->replaced)
action = svn_wc_notify_add;
else
action = svn_wc_notify_patch;
notify = svn_wc_create_notify(target->local_abspath ? target->local_abspath
: target->local_relpath,
action, pool);
notify->kind = svn_node_file;
if (action == svn_wc_notify_skip)
{
if (target->db_kind == svn_node_none ||
target->db_kind == svn_node_unknown)
notify->content_state = svn_wc_notify_state_missing;
else if (target->db_kind == svn_node_dir)
notify->content_state = svn_wc_notify_state_obstructed;
else
notify->content_state = svn_wc_notify_state_unknown;
}
else
{
if (target->had_rejects)
notify->content_state = svn_wc_notify_state_conflicted;
else if (target->local_mods)
notify->content_state = svn_wc_notify_state_merged;
else if (target->has_text_changes)
notify->content_state = svn_wc_notify_state_changed;
if (target->had_prop_rejects)
notify->prop_state = svn_wc_notify_state_conflicted;
else if (target->has_prop_changes)
notify->prop_state = svn_wc_notify_state_changed;
}
(*ctx->notify_func2)(ctx->notify_baton2, notify, pool);
if (action == svn_wc_notify_patch)
{
int i;
apr_pool_t *iterpool;
apr_hash_index_t *hash_index;
iterpool = svn_pool_create(pool);
for (i = 0; i < target->content->hunks->nelts; i++)
{
const hunk_info_t *hi;
svn_pool_clear(iterpool);
hi = APR_ARRAY_IDX(target->content->hunks, i, hunk_info_t *);
SVN_ERR(send_hunk_notification(hi, target, NULL /* prop_name */,
ctx, iterpool));
}
for (hash_index = apr_hash_first(pool, target->prop_targets);
hash_index;
hash_index = apr_hash_next(hash_index))
{
prop_patch_target_t *prop_target;
prop_target = svn__apr_hash_index_val(hash_index);
for (i = 0; i < prop_target->content->hunks->nelts; i++)
{
const hunk_info_t *hi;
svn_pool_clear(iterpool);
hi = APR_ARRAY_IDX(prop_target->content->hunks, i,
hunk_info_t *);
/* Don't notify on the hunk level for added or deleted props. */
if (prop_target->operation != svn_diff_op_added &&
prop_target->operation != svn_diff_op_deleted)
SVN_ERR(send_hunk_notification(hi, target, prop_target->name,
ctx, iterpool));
}
}
svn_pool_destroy(iterpool);
}
return SVN_NO_ERROR;
}
+static void
+svn_sort__array(apr_array_header_t *array,
+ int (*comparison_func)(const void *,
+ const void *))
+{
+ qsort(array->elts, array->nelts, array->elt_size, comparison_func);
+}
+
+/* Implements the callback for svn_sort__array. Puts hunks that match
+ before hunks that do not match, puts hunks that match in order
+ based on postion matched, puts hunks that do not match in order
+ based on original position. */
+static int
+sort_matched_hunks(const void *a, const void *b)
+{
+ const hunk_info_t *item1 = *((const hunk_info_t * const *)a);
+ const hunk_info_t *item2 = *((const hunk_info_t * const *)b);
+ svn_boolean_t matched1 = !item1->rejected && !item1->already_applied;
+ svn_boolean_t matched2 = !item2->rejected && !item2->already_applied;
+ svn_linenum_t original1, original2;
+
+ if (matched1 && matched2)
+ {
+ /* Both match so use order matched in file. */
+ if (item1->matched_line > item2->matched_line)
+ return 1;
+ else if (item1->matched_line == item2->matched_line)
+ return 0;
+ else
+ return -1;
+ }
+ else if (matched2)
+ /* Only second matches, put it before first. */
+ return 1;
+ else if (matched1)
+ /* Only first matches, put it before second. */
+ return -1;
+
+ /* Neither matches, sort by original_start. */
+ original1 = svn_diff_hunk_get_original_start(item1->hunk);
+ original2 = svn_diff_hunk_get_original_start(item2->hunk);
+ if (original1 > original2)
+ return 1;
+ else if (original1 == original2)
+ return 0;
+ else
+ return -1;
+}
+
+
/* Apply a PATCH to a working copy at ABS_WC_PATH and put the result
* into temporary files, to be installed in the working copy later.
* Return information about the patch target in *PATCH_TARGET, allocated
* in RESULT_POOL. Use WC_CTX as the working copy context.
* STRIP_COUNT specifies the number of leading path components
* which should be stripped from target paths in the patch.
* REMOVE_TEMPFILES, PATCH_FUNC, and PATCH_BATON as in svn_client_patch().
* IGNORE_WHITESPACE tells whether whitespace should be considered when
* doing the matching.
* Call cancel CANCEL_FUNC with baton CANCEL_BATON to trigger cancellation.
* Do temporary allocations in SCRATCH_POOL. */
static svn_error_t *
apply_one_patch(patch_target_t **patch_target, svn_patch_t *patch,
const char *abs_wc_path, svn_wc_context_t *wc_ctx,
int strip_count,
svn_boolean_t ignore_whitespace,
svn_boolean_t remove_tempfiles,
svn_client_patch_func_t patch_func,
void *patch_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *result_pool, apr_pool_t *scratch_pool)
{
patch_target_t *target;
apr_pool_t *iterpool;
int i;
static const svn_linenum_t MAX_FUZZ = 2;
apr_hash_index_t *hash_index;
SVN_ERR(init_patch_target(&target, patch, abs_wc_path, wc_ctx, strip_count,
remove_tempfiles, result_pool, scratch_pool));
if (target->skipped)
{
*patch_target = target;
return SVN_NO_ERROR;
}
if (patch_func)
{
SVN_ERR(patch_func(patch_baton, &target->filtered,
target->canon_path_from_patchfile,
target->patched_path, target->reject_path,
scratch_pool));
if (target->filtered)
{
*patch_target = target;
return SVN_NO_ERROR;
}
}
iterpool = svn_pool_create(scratch_pool);
/* Match hunks. */
for (i = 0; i < patch->hunks->nelts; i++)
{
svn_diff_hunk_t *hunk;
hunk_info_t *hi;
svn_linenum_t fuzz = 0;
svn_pool_clear(iterpool);
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
hunk = APR_ARRAY_IDX(patch->hunks, i, svn_diff_hunk_t *);
/* Determine the line the hunk should be applied at.
* If no match is found initially, try with fuzz. */
do
{
SVN_ERR(get_hunk_info(&hi, target, target->content, hunk, fuzz,
ignore_whitespace,
FALSE /* is_prop_hunk */,
cancel_func, cancel_baton,
result_pool, iterpool));
fuzz++;
}
while (hi->rejected && fuzz <= MAX_FUZZ && ! hi->already_applied);
APR_ARRAY_PUSH(target->content->hunks, hunk_info_t *) = hi;
}
+
+ /* Hunks are applied in the order determined by the matched line and
+ this may be different from the order of the original lines. */
+ svn_sort__array(target->content->hunks, sort_matched_hunks);
/* Apply or reject hunks. */
for (i = 0; i < target->content->hunks->nelts; i++)
{
hunk_info_t *hi;
svn_pool_clear(iterpool);
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
hi = APR_ARRAY_IDX(target->content->hunks, i, hunk_info_t *);
if (hi->already_applied)
continue;
else if (hi->rejected)
SVN_ERR(reject_hunk(target, target->content, hi->hunk,
NULL /* prop_name */,
iterpool));
else
SVN_ERR(apply_hunk(target, target->content, hi,
NULL /* prop_name */, iterpool));
}
if (target->kind_on_disk == svn_node_file)
{
/* Copy any remaining lines to target. */
SVN_ERR(copy_lines_to_target(target->content, 0, scratch_pool));
if (! target->content->eof)
{
/* We could not copy the entire target file to the temporary file,
* and would truncate the target if we copied the temporary file
* on top of it. Skip this target. */
target->skipped = TRUE;
}
}
/* Match property hunks. */
for (hash_index = apr_hash_first(scratch_pool, patch->prop_patches);
hash_index;
hash_index = apr_hash_next(hash_index))
{
svn_prop_patch_t *prop_patch;
const char *prop_name;
prop_patch_target_t *prop_target;
prop_name = svn__apr_hash_index_key(hash_index);
prop_patch = svn__apr_hash_index_val(hash_index);
if (! strcmp(prop_name, SVN_PROP_SPECIAL))
target->is_special = TRUE;
/* We'll store matched hunks in prop_content. */
prop_target = svn_hash_gets(target->prop_targets, prop_name);
for (i = 0; i < prop_patch->hunks->nelts; i++)
{
svn_diff_hunk_t *hunk;
hunk_info_t *hi;
svn_linenum_t fuzz = 0;
svn_pool_clear(iterpool);
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
hunk = APR_ARRAY_IDX(prop_patch->hunks, i, svn_diff_hunk_t *);
/* Determine the line the hunk should be applied at.
* If no match is found initially, try with fuzz. */
do
{
SVN_ERR(get_hunk_info(&hi, target, prop_target->content,
hunk, fuzz,
ignore_whitespace,
TRUE /* is_prop_hunk */,
cancel_func, cancel_baton,
result_pool, iterpool));
fuzz++;
}
while (hi->rejected && fuzz <= MAX_FUZZ && ! hi->already_applied);
APR_ARRAY_PUSH(prop_target->content->hunks, hunk_info_t *) = hi;
}
}
/* Apply or reject property hunks. */
for (hash_index = apr_hash_first(scratch_pool, target->prop_targets);
hash_index;
hash_index = apr_hash_next(hash_index))
{
prop_patch_target_t *prop_target;
prop_target = svn__apr_hash_index_val(hash_index);
for (i = 0; i < prop_target->content->hunks->nelts; i++)
{
hunk_info_t *hi;
svn_pool_clear(iterpool);
hi = APR_ARRAY_IDX(prop_target->content->hunks, i,
hunk_info_t *);
if (hi->already_applied)
continue;
else if (hi->rejected)
SVN_ERR(reject_hunk(target, prop_target->content, hi->hunk,
prop_target->name,
iterpool));
else
SVN_ERR(apply_hunk(target, prop_target->content, hi,
prop_target->name,
iterpool));
}
if (prop_target->content->existed)
{
/* Copy any remaining lines to target. */
SVN_ERR(copy_lines_to_target(prop_target->content, 0,
scratch_pool));
if (! prop_target->content->eof)
{
/* We could not copy the entire target property to the
* temporary file, and would truncate the target if we
* copied the temporary file on top of it. Skip this target. */
target->skipped = TRUE;
}
}
}
svn_pool_destroy(iterpool);
if (!target->is_symlink)
{
/* Now close files we don't need any longer to get their contents
* flushed to disk.
* But we're not closing the reject file -- it still needed and
* will be closed later in write_out_rejected_hunks(). */
if (target->kind_on_disk == svn_node_file)
SVN_ERR(svn_io_file_close(target->file, scratch_pool));
SVN_ERR(svn_io_file_close(target->patched_file, scratch_pool));
}
if (! target->skipped)
{
apr_finfo_t working_file;
apr_finfo_t patched_file;
/* Get sizes of the patched temporary file and the working file.
* We'll need those to figure out whether we should delete the
* patched file. */
SVN_ERR(svn_io_stat(&patched_file, target->patched_path,
APR_FINFO_SIZE | APR_FINFO_LINK, scratch_pool));
if (target->kind_on_disk == svn_node_file)
SVN_ERR(svn_io_stat(&working_file, target->local_abspath,
APR_FINFO_SIZE | APR_FINFO_LINK, scratch_pool));
else
working_file.size = 0;
if (patched_file.size == 0 && working_file.size > 0)
{
/* If a unidiff removes all lines from a file, that usually
* means deletion, so we can confidently schedule the target
* for deletion. In the rare case where the unidiff was really
* meant to replace a file with an empty one, this may not
* be desirable. But the deletion can easily be reverted and
* creating an empty file manually is not exactly hard either. */
target->deleted = (target->db_kind == svn_node_file);
}
else if (patched_file.size == 0 && working_file.size == 0)
{
/* The target was empty or non-existent to begin with
* and no content was changed by patching.
* Report this as skipped if it didn't exist, unless in the special
* case of adding an empty file which has properties set on it or
* adding an empty file with a 'git diff' */
if (target->kind_on_disk == svn_node_none
&& ! target->has_prop_changes
&& ! target->added)
target->skipped = TRUE;
}
else if (patched_file.size > 0 && working_file.size == 0)
{
/* The patch has created a file. */
if (target->locally_deleted)
target->replaced = TRUE;
else if (target->db_kind == svn_node_none)
target->added = TRUE;
}
}
*patch_target = target;
return SVN_NO_ERROR;
}
/* Try to create missing parent directories for TARGET in the working copy
* rooted at ABS_WC_PATH, and add the parents to version control.
* If the parents cannot be created, mark the target as skipped.
* Use client context CTX. If DRY_RUN is true, do not create missing
* parents but issue notifications only.
* Use SCRATCH_POOL for temporary allocations. */
static svn_error_t *
create_missing_parents(patch_target_t *target,
const char *abs_wc_path,
svn_client_ctx_t *ctx,
svn_boolean_t dry_run,
apr_pool_t *scratch_pool)
{
const char *local_abspath;
apr_array_header_t *components;
int present_components;
int i;
apr_pool_t *iterpool;
/* Check if we can safely create the target's parent. */
local_abspath = abs_wc_path;
components = svn_path_decompose(target->local_relpath, scratch_pool);
present_components = 0;
iterpool = svn_pool_create(scratch_pool);
for (i = 0; i < components->nelts - 1; i++)
{
const char *component;
svn_node_kind_t wc_kind, disk_kind;
svn_pool_clear(iterpool);
component = APR_ARRAY_IDX(components, i, const char *);
local_abspath = svn_dirent_join(local_abspath, component, scratch_pool);
SVN_ERR(svn_wc_read_kind2(&wc_kind, ctx->wc_ctx, local_abspath,
FALSE, TRUE, iterpool));
SVN_ERR(svn_io_check_path(local_abspath, &disk_kind, iterpool));
if (disk_kind == svn_node_file || wc_kind == svn_node_file)
{
/* on-disk files and missing files are obstructions */
target->skipped = TRUE;
break;
}
else if (disk_kind == svn_node_dir)
{
if (wc_kind == svn_node_dir)
present_components++;
else
{
target->skipped = TRUE;
break;
}
}
else if (wc_kind != svn_node_none)
{
/* Node is missing */
target->skipped = TRUE;
break;
}
else
{
/* It's not a file, it's not a dir...
Let's add a dir */
break;
}
}
if (! target->skipped)
{
local_abspath = abs_wc_path;
for (i = 0; i < present_components; i++)
{
const char *component;
component = APR_ARRAY_IDX(components, i, const char *);
local_abspath = svn_dirent_join(local_abspath,
component, scratch_pool);
}
if (!dry_run && present_components < components->nelts - 1)
SVN_ERR(svn_io_make_dir_recursively(
svn_dirent_join(
abs_wc_path,
svn_relpath_dirname(target->local_relpath,
scratch_pool),
scratch_pool),
scratch_pool));
for (i = present_components; i < components->nelts - 1; i++)
{
const char *component;
svn_pool_clear(iterpool);
component = APR_ARRAY_IDX(components, i, const char *);
local_abspath = svn_dirent_join(local_abspath, component,
scratch_pool);
if (dry_run)
{
if (ctx->notify_func2)
{
/* Just do notification. */
svn_wc_notify_t *notify;
notify = svn_wc_create_notify(local_abspath,
svn_wc_notify_add,
iterpool);
notify->kind = svn_node_dir;
ctx->notify_func2(ctx->notify_baton2, notify,
iterpool);
}
}
else
{
/* Create the missing component and add it
* to version control. Allow cancellation since we
* have not modified the working copy yet for this
* target. */
if (ctx->cancel_func)
SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
SVN_ERR(svn_wc_add_from_disk2(ctx->wc_ctx, local_abspath,
NULL /*props*/,
ctx->notify_func2, ctx->notify_baton2,
iterpool));
}
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Install a patched TARGET into the working copy at ABS_WC_PATH.
* Use client context CTX to retrieve WC_CTX, and possibly doing
* notifications. If DRY_RUN is TRUE, don't modify the working copy.
* Do temporary allocations in POOL. */
static svn_error_t *
install_patched_target(patch_target_t *target, const char *abs_wc_path,
svn_client_ctx_t *ctx, svn_boolean_t dry_run,
apr_pool_t *pool)
{
if (target->deleted)
{
if (! dry_run)
{
/* Schedule the target for deletion. Suppress
* notification, we'll do it manually in a minute
* because we also need to notify during dry-run.
* Also suppress cancellation, because we'd rather
* notify about what we did before aborting. */
SVN_ERR(svn_wc_delete4(ctx->wc_ctx, target->local_abspath,
FALSE /* keep_local */, FALSE,
NULL, NULL, NULL, NULL, pool));
}
}
else
{
svn_node_kind_t parent_db_kind;
if (target->added || target->replaced)
{
const char *parent_abspath;
parent_abspath = svn_dirent_dirname(target->local_abspath,
pool);
/* If the target's parent directory does not yet exist
* we need to create it before we can copy the patched
* result in place. */
SVN_ERR(svn_wc_read_kind2(&parent_db_kind, ctx->wc_ctx,
parent_abspath, FALSE, FALSE, pool));
/* We can't add targets under nodes scheduled for delete, so add
a new directory if needed. */
if (parent_db_kind == svn_node_dir
|| parent_db_kind == svn_node_file)
{
if (parent_db_kind != svn_node_dir)
target->skipped = TRUE;
else
{
svn_node_kind_t disk_kind;
SVN_ERR(svn_io_check_path(parent_abspath, &disk_kind, pool));
if (disk_kind != svn_node_dir)
target->skipped = TRUE;
}
}
else
SVN_ERR(create_missing_parents(target, abs_wc_path, ctx,
dry_run, pool));
}
else
{
svn_node_kind_t wc_kind;
/* The target should exist */
SVN_ERR(svn_wc_read_kind2(&wc_kind, ctx->wc_ctx,
target->local_abspath,
FALSE, FALSE, pool));
if (target->kind_on_disk == svn_node_none
|| wc_kind != target->kind_on_disk)
{
target->skipped = TRUE;
}
}
if (! dry_run && ! target->skipped)
{
if (target->is_special)
{
svn_stream_t *stream;
svn_stream_t *patched_stream;
SVN_ERR(svn_stream_open_readonly(&patched_stream,
target->patched_path,
pool, pool));
SVN_ERR(svn_subst_create_specialfile(&stream,
target->local_abspath,
pool, pool));
SVN_ERR(svn_stream_copy3(patched_stream, stream,
ctx->cancel_func, ctx->cancel_baton,
pool));
}
else
{
svn_boolean_t repair_eol;
/* Copy the patched file on top of the target file.
* Always expand keywords in the patched file, but repair EOL
* only if svn:eol-style dictates a particular style. */
repair_eol = (target->content->eol_style ==
svn_subst_eol_style_fixed ||
target->content->eol_style ==
svn_subst_eol_style_native);
SVN_ERR(svn_subst_copy_and_translate4(
target->patched_path, target->local_abspath,
target->content->eol_str, repair_eol,
target->content->keywords,
TRUE /* expand */, FALSE /* special */,
ctx->cancel_func, ctx->cancel_baton, pool));
}
if (target->added || target->replaced)
{
/* The target file didn't exist previously,
* so add it to version control.
* Suppress notification, we'll do that later (and also
* during dry-run). Don't allow cancellation because
* we'd rather notify about what we did before aborting. */
SVN_ERR(svn_wc_add_from_disk2(ctx->wc_ctx, target->local_abspath,
NULL /*props*/,
NULL, NULL, pool));
}
/* Restore the target's executable bit if necessary. */
SVN_ERR(svn_io_set_file_executable(target->local_abspath,
target->executable,
FALSE, pool));
}
}
return SVN_NO_ERROR;
}
/* Write out rejected hunks, if any, to TARGET->REJECT_PATH. If DRY_RUN is
* TRUE, don't modify the working copy.
* Do temporary allocations in POOL.
*/
static svn_error_t *
write_out_rejected_hunks(patch_target_t *target,
svn_boolean_t dry_run,
apr_pool_t *pool)
{
SVN_ERR(svn_io_file_close(target->reject_file, pool));
if (! dry_run && (target->had_rejects || target->had_prop_rejects))
{
/* Write out rejected hunks, if any. */
SVN_ERR(svn_io_copy_file(target->reject_path,
apr_psprintf(pool, "%s.svnpatch.rej",
target->local_abspath),
FALSE, pool));
/* ### TODO mark file as conflicted. */
}
return SVN_NO_ERROR;
}
/* Install the patched properties for TARGET. Use client context CTX to
* retrieve WC_CTX. If DRY_RUN is TRUE, don't modify the working copy.
* Do temporary allocations in SCRATCH_POOL. */
static svn_error_t *
install_patched_prop_targets(patch_target_t *target,
svn_client_ctx_t *ctx, svn_boolean_t dry_run,
apr_pool_t *scratch_pool)
{
apr_hash_index_t *hi;
apr_pool_t *iterpool;
iterpool = svn_pool_create(scratch_pool);
for (hi = apr_hash_first(scratch_pool, target->prop_targets);
hi;
hi = apr_hash_next(hi))
{
prop_patch_target_t *prop_target = svn__apr_hash_index_val(hi);
const svn_string_t *prop_val;
svn_error_t *err;
svn_pool_clear(iterpool);
if (ctx->cancel_func)
SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
/* For a deleted prop we only set the value to NULL. */
if (prop_target->operation == svn_diff_op_deleted)
{
if (! dry_run)
SVN_ERR(svn_wc_prop_set4(ctx->wc_ctx, target->local_abspath,
prop_target->name, NULL, svn_depth_empty,
TRUE /* skip_checks */,
NULL /* changelist_filter */,
NULL, NULL /* cancellation */,
NULL, NULL /* notification */,
iterpool));
continue;
}
/* If the patch target doesn't exist yet, the patch wants to add an
* empty file with properties set on it. So create an empty file and
* add it to version control. But if the patch was in the 'git format'
* then the file has already been added.
*
* ### How can we tell whether the patch really wanted to create
* ### an empty directory? */
if (! target->has_text_changes
&& target->kind_on_disk == svn_node_none
&& ! target->added)
{
if (! dry_run)
{
SVN_ERR(svn_io_file_create(target->local_abspath, "",
scratch_pool));
SVN_ERR(svn_wc_add_from_disk2(ctx->wc_ctx, target->local_abspath,
NULL /*props*/,
/* suppress notification */
NULL, NULL,
iterpool));
}
target->added = TRUE;
}
/* Attempt to set the property, and reject all hunks if this
fails. If the property had a non-empty value, but now has
an empty one, we'll just delete the property altogether. */
if (prop_target->value && prop_target->value->len
&& prop_target->patched_value && !prop_target->patched_value->len)
prop_val = NULL;
else
prop_val = svn_stringbuf__morph_into_string(prop_target->patched_value);
if (dry_run)
{
const svn_string_t *canon_propval;
err = svn_wc_canonicalize_svn_prop(&canon_propval,
prop_target->name,
prop_val, target->local_abspath,
target->db_kind,
TRUE, /* ### Skipping checks */
NULL, NULL,
iterpool);
}
else
{
err = svn_wc_prop_set4(ctx->wc_ctx, target->local_abspath,
prop_target->name, prop_val, svn_depth_empty,
TRUE /* skip_checks */,
NULL /* changelist_filter */,
NULL, NULL /* cancellation */,
NULL, NULL /* notification */,
iterpool);
}
if (err)
{
/* ### The errors which svn_wc_canonicalize_svn_prop() will
* ### return aren't documented. */
if (err->apr_err == SVN_ERR_ILLEGAL_TARGET ||
err->apr_err == SVN_ERR_NODE_UNEXPECTED_KIND ||
err->apr_err == SVN_ERR_IO_UNKNOWN_EOL ||
err->apr_err == SVN_ERR_BAD_MIME_TYPE ||
err->apr_err == SVN_ERR_CLIENT_INVALID_EXTERNALS_DESCRIPTION)
{
int i;
svn_error_clear(err);
for (i = 0; i < prop_target->content->hunks->nelts; i++)
{
hunk_info_t *hunk_info;
hunk_info = APR_ARRAY_IDX(prop_target->content->hunks,
i, hunk_info_t *);
hunk_info->rejected = TRUE;
SVN_ERR(reject_hunk(target, prop_target->content,
hunk_info->hunk, prop_target->name,
iterpool));
}
}
else
return svn_error_trace(err);
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Baton for can_delete_callback */
struct can_delete_baton_t
{
svn_boolean_t must_keep;
const apr_array_header_t *targets_info;
const char *local_abspath;
};
/* Implements svn_wc_status_func4_t. */
static svn_error_t *
can_delete_callback(void *baton,
const char *abspath,
const svn_wc_status3_t *status,
apr_pool_t *pool)
{
struct can_delete_baton_t *cb = baton;
int i;
switch(status->node_status)
{
case svn_wc_status_none:
case svn_wc_status_deleted:
return SVN_NO_ERROR;
default:
if (! strcmp(cb->local_abspath, abspath))
return SVN_NO_ERROR; /* Only interested in descendants */
for (i = 0; i < cb->targets_info->nelts; i++)
{
const patch_target_info_t *target_info =
APR_ARRAY_IDX(cb->targets_info, i, const patch_target_info_t *);
if (! strcmp(target_info->local_abspath, abspath))
{
if (target_info->deleted)
return SVN_NO_ERROR;
break; /* Cease invocation; must keep */
}
}
cb->must_keep = TRUE;
return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
}
}
static svn_error_t *
check_ancestor_delete(const char *deleted_target,
apr_array_header_t *targets_info,
const char *apply_root,
svn_boolean_t dry_run,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
struct can_delete_baton_t cb;
svn_error_t *err;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
const char *dir_abspath = svn_dirent_dirname(deleted_target, scratch_pool);
while (svn_dirent_is_child(apply_root, dir_abspath, iterpool))
{
svn_pool_clear(iterpool);
cb.local_abspath = dir_abspath;
cb.must_keep = FALSE;
cb.targets_info = targets_info;
err = svn_wc_walk_status(ctx->wc_ctx, dir_abspath, svn_depth_infinity,
TRUE, FALSE, FALSE, NULL,
can_delete_callback, &cb,
ctx->cancel_func, ctx->cancel_baton,
iterpool);
if (err)
{
if (err->apr_err != SVN_ERR_CEASE_INVOCATION)
return svn_error_trace(err);
svn_error_clear(err);
}
if (cb.must_keep)
{
break;
}
if (! dry_run)
{
SVN_ERR(svn_wc_delete4(ctx->wc_ctx, dir_abspath, FALSE, FALSE,
ctx->cancel_func, ctx->cancel_baton,
NULL, NULL,
scratch_pool));
}
{
patch_target_info_t *pti = apr_pcalloc(result_pool, sizeof(*pti));
pti->local_abspath = apr_pstrdup(result_pool, dir_abspath);
pti->deleted = TRUE;
APR_ARRAY_PUSH(targets_info, patch_target_info_t *) = pti;
}
if (ctx->notify_func2)
{
svn_wc_notify_t *notify;
notify = svn_wc_create_notify(dir_abspath, svn_wc_notify_delete,
iterpool);
notify->kind = svn_node_dir;
ctx->notify_func2(ctx->notify_baton2, notify, iterpool);
}
/* And check if we must also delete the parent */
dir_abspath = svn_dirent_dirname(dir_abspath, scratch_pool);
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* This function is the main entry point into the patch code. */
static svn_error_t *
apply_patches(/* The path to the patch file. */
const char *patch_abspath,
/* The abspath to the working copy the patch should be applied to. */
const char *abs_wc_path,
/* Indicates whether we're doing a dry run. */
svn_boolean_t dry_run,
/* Number of leading components to strip from patch target paths. */
int strip_count,
/* Whether to apply the patch in reverse. */
svn_boolean_t reverse,
/* Whether to ignore whitespace when matching context lines. */
svn_boolean_t ignore_whitespace,
/* As in svn_client_patch(). */
svn_boolean_t remove_tempfiles,
/* As in svn_client_patch(). */
svn_client_patch_func_t patch_func,
void *patch_baton,
/* The client context. */
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
svn_patch_t *patch;
apr_pool_t *iterpool;
svn_patch_file_t *patch_file;
apr_array_header_t *targets_info;
/* Try to open the patch file. */
SVN_ERR(svn_diff_open_patch_file(&patch_file, patch_abspath, scratch_pool));
/* Apply patches. */
targets_info = apr_array_make(scratch_pool, 0,
sizeof(patch_target_info_t *));
iterpool = svn_pool_create(scratch_pool);
do
{
svn_pool_clear(iterpool);
if (ctx->cancel_func)
SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file,
reverse, ignore_whitespace,
iterpool, iterpool));
if (patch)
{
patch_target_t *target;
SVN_ERR(apply_one_patch(&target, patch, abs_wc_path,
ctx->wc_ctx, strip_count,
ignore_whitespace, remove_tempfiles,
patch_func, patch_baton,
ctx->cancel_func, ctx->cancel_baton,
iterpool, iterpool));
if (! target->filtered)
{
/* Save info we'll still need when we're done patching. */
patch_target_info_t *target_info =
apr_pcalloc(scratch_pool, sizeof(patch_target_info_t));
target_info->local_abspath = apr_pstrdup(scratch_pool,
target->local_abspath);
target_info->deleted = target->deleted;
if (! target->skipped)
{
APR_ARRAY_PUSH(targets_info,
patch_target_info_t *) = target_info;
if (target->has_text_changes
|| target->added
|| target->deleted)
SVN_ERR(install_patched_target(target, abs_wc_path,
ctx, dry_run, iterpool));
if (target->has_prop_changes && (!target->deleted))
SVN_ERR(install_patched_prop_targets(target, ctx,
dry_run, iterpool));
SVN_ERR(write_out_rejected_hunks(target, dry_run, iterpool));
}
SVN_ERR(send_patch_notification(target, ctx, iterpool));
if (target->deleted && !target->skipped)
{
SVN_ERR(check_ancestor_delete(target_info->local_abspath,
targets_info, abs_wc_path,
dry_run, ctx,
scratch_pool, iterpool));
}
}
}
}
while (patch);
SVN_ERR(svn_diff_close_patch_file(patch_file, iterpool));
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_client_patch(const char *patch_abspath,
const char *wc_dir_abspath,
svn_boolean_t dry_run,
int strip_count,
svn_boolean_t reverse,
svn_boolean_t ignore_whitespace,
svn_boolean_t remove_tempfiles,
svn_client_patch_func_t patch_func,
void *patch_baton,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
svn_node_kind_t kind;
if (strip_count < 0)
return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
_("strip count must be positive"));
if (svn_path_is_url(wc_dir_abspath))
return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
_("'%s' is not a local path"),
svn_dirent_local_style(wc_dir_abspath,
scratch_pool));
SVN_ERR(svn_io_check_path(patch_abspath, &kind, scratch_pool));
if (kind == svn_node_none)
return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
_("'%s' does not exist"),
svn_dirent_local_style(patch_abspath,
scratch_pool));
if (kind != svn_node_file)
return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
_("'%s' is not a file"),
svn_dirent_local_style(patch_abspath,
scratch_pool));
SVN_ERR(svn_io_check_path(wc_dir_abspath, &kind, scratch_pool));
if (kind == svn_node_none)
return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
_("'%s' does not exist"),
svn_dirent_local_style(wc_dir_abspath,
scratch_pool));
if (kind != svn_node_dir)
return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
_("'%s' is not a directory"),
svn_dirent_local_style(wc_dir_abspath,
scratch_pool));
SVN_WC__CALL_WITH_WRITE_LOCK(
apply_patches(patch_abspath, wc_dir_abspath, dry_run, strip_count,
reverse, ignore_whitespace, remove_tempfiles,
patch_func, patch_baton, ctx, scratch_pool),
ctx->wc_ctx, wc_dir_abspath, FALSE /* lock_anchor */, scratch_pool);
return SVN_NO_ERROR;
}
Index: vendor/subversion/dist/subversion/libsvn_client/upgrade.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_client/upgrade.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_client/upgrade.c (revision 286501)
@@ -1,327 +1,405 @@
/*
* upgrade.c: wrapper around wc upgrade functionality.
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
/* ==================================================================== */
/*** Includes. ***/
#include "svn_time.h"
#include "svn_wc.h"
#include "svn_client.h"
#include "svn_config.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_pools.h"
#include "client.h"
#include "svn_props.h"
#include "svn_private_config.h"
#include "private/svn_wc_private.h"
/*** Code. ***/
/* callback baton for fetch_repos_info */
struct repos_info_baton
{
apr_pool_t *state_pool;
svn_client_ctx_t *ctx;
const char *last_repos;
const char *last_uuid;
};
/* svn_wc_upgrade_get_repos_info_t implementation for calling
svn_wc_upgrade() from svn_client_upgrade() */
static svn_error_t *
fetch_repos_info(const char **repos_root,
const char **repos_uuid,
void *baton,
const char *url,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
struct repos_info_baton *ri = baton;
/* The same info is likely to retrieved multiple times (e.g. externals) */
if (ri->last_repos && svn_uri__is_ancestor(ri->last_repos, url))
{
*repos_root = apr_pstrdup(result_pool, ri->last_repos);
*repos_uuid = apr_pstrdup(result_pool, ri->last_uuid);
return SVN_NO_ERROR;
}
SVN_ERR(svn_client_get_repos_root(repos_root, repos_uuid, url, ri->ctx,
result_pool, scratch_pool));
/* Store data for further calls */
ri->last_repos = apr_pstrdup(ri->state_pool, *repos_root);
ri->last_uuid = apr_pstrdup(ri->state_pool, *repos_uuid);
return SVN_NO_ERROR;
}
+/* Forward definition. Upgrades svn:externals properties in the working copy
+ LOCAL_ABSPATH to the WC-NG storage.
+ */
+static svn_error_t *
+upgrade_externals_from_properties(svn_client_ctx_t *ctx,
+ const char *local_abspath,
+ apr_pool_t *scratch_pool);
+
svn_error_t *
svn_client_upgrade(const char *path,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
const char *local_abspath;
apr_hash_t *externals;
- apr_hash_index_t *hi;
- apr_pool_t *iterpool;
- apr_pool_t *iterpool2;
- svn_opt_revision_t rev = {svn_opt_revision_unspecified, {0}};
struct repos_info_baton info_baton;
info_baton.state_pool = scratch_pool;
info_baton.ctx = ctx;
info_baton.last_repos = NULL;
info_baton.last_uuid = NULL;
if (svn_path_is_url(path))
return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
_("'%s' is not a local path"), path);
SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
SVN_ERR(svn_wc_upgrade(ctx->wc_ctx, local_abspath,
fetch_repos_info, &info_baton,
ctx->cancel_func, ctx->cancel_baton,
ctx->notify_func2, ctx->notify_baton2,
scratch_pool));
+ SVN_ERR(svn_wc__externals_defined_below(&externals,
+ ctx->wc_ctx, local_abspath,
+ scratch_pool, scratch_pool));
+
+ if (apr_hash_count(externals) > 0)
+ {
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+ apr_hash_index_t *hi;
+
+ /* We are upgrading from >= 1.7. No need to upgrade from
+ svn:externals properties. And by that avoiding the removal
+ of recorded externals information (issue #4519)
+
+ Only directory externals need an explicit upgrade */
+ for (hi = apr_hash_first(scratch_pool, externals);
+ hi;
+ hi = apr_hash_next(hi))
+ {
+ const char *ext_abspath;
+ svn_node_kind_t kind;
+
+ svn_pool_clear(iterpool);
+
+ ext_abspath = svn__apr_hash_index_key(hi);
+
+ SVN_ERR(svn_wc__read_external_info(&kind, NULL, NULL, NULL, NULL,
+ ctx->wc_ctx, local_abspath,
+ ext_abspath, FALSE,
+ iterpool, iterpool));
+
+ if (kind == svn_node_dir)
+ {
+ svn_error_t *err = svn_client_upgrade(ext_abspath, ctx, iterpool);
+
+ if (err)
+ {
+ svn_wc_notify_t *notify =
+ svn_wc_create_notify(ext_abspath,
+ svn_wc_notify_failed_external,
+ iterpool);
+ notify->err = err;
+ ctx->notify_func2(ctx->notify_baton2,
+ notify, iterpool);
+ svn_error_clear(err);
+ /* Next external node, please... */
+ }
+ }
+ }
+
+ svn_pool_destroy(iterpool);
+ }
+ else
+ {
+ /* Upgrading from <= 1.6, or no svn:properties defined.
+ (There is no way to detect the difference from libsvn_client :( ) */
+
+ SVN_ERR(upgrade_externals_from_properties(ctx, local_abspath,
+ scratch_pool));
+ }
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+upgrade_externals_from_properties(svn_client_ctx_t *ctx,
+ const char *local_abspath,
+ apr_pool_t *scratch_pool)
+{
+ apr_hash_index_t *hi;
+ apr_pool_t *iterpool;
+ apr_pool_t *iterpool2;
+ apr_hash_t *externals;
+ svn_opt_revision_t rev = {svn_opt_revision_unspecified, {0}};
+ struct repos_info_baton info_baton;
+
/* Now it's time to upgrade the externals too. We do it after the wc
upgrade to avoid that errors in the externals causes the wc upgrade to
fail. Thanks to caching the performance penalty of walking the wc a
second time shouldn't be too severe */
SVN_ERR(svn_client_propget5(&externals, NULL, SVN_PROP_EXTERNALS,
local_abspath, &rev, &rev, NULL,
svn_depth_infinity, NULL, ctx,
scratch_pool, scratch_pool));
iterpool = svn_pool_create(scratch_pool);
iterpool2 = svn_pool_create(scratch_pool);
for (hi = apr_hash_first(scratch_pool, externals); hi;
hi = apr_hash_next(hi))
{
int i;
const char *externals_parent_abspath;
const char *externals_parent_url;
const char *externals_parent_repos_root_url;
const char *externals_parent_repos_relpath;
const char *externals_parent = svn__apr_hash_index_key(hi);
svn_string_t *external_desc = svn__apr_hash_index_val(hi);
apr_array_header_t *externals_p;
svn_error_t *err;
svn_pool_clear(iterpool);
externals_p = apr_array_make(iterpool, 1,
sizeof(svn_wc_external_item2_t*));
/* In this loop, an error causes the respective externals definition, or
* the external (inner loop), to be skipped, so that upgrade carries on
* with the other externals. */
err = svn_dirent_get_absolute(&externals_parent_abspath,
externals_parent, iterpool);
if (!err)
err = svn_wc__node_get_repos_info(NULL,
&externals_parent_repos_relpath,
&externals_parent_repos_root_url,
NULL,
ctx->wc_ctx,
externals_parent_abspath,
iterpool, iterpool);
if (!err)
externals_parent_url = svn_path_url_add_component2(
externals_parent_repos_root_url,
externals_parent_repos_relpath,
iterpool);
if (!err)
err = svn_wc_parse_externals_description3(
- &externals_p, svn_dirent_dirname(path, iterpool),
+ &externals_p, svn_dirent_dirname(local_abspath, iterpool),
external_desc->data, FALSE, iterpool);
if (err)
{
svn_wc_notify_t *notify =
svn_wc_create_notify(externals_parent,
svn_wc_notify_failed_external,
scratch_pool);
notify->err = err;
ctx->notify_func2(ctx->notify_baton2,
notify, scratch_pool);
svn_error_clear(err);
/* Next externals definition, please... */
continue;
}
for (i = 0; i < externals_p->nelts; i++)
{
svn_wc_external_item2_t *item;
const char *resolved_url;
const char *external_abspath;
const char *repos_relpath;
const char *repos_root_url;
const char *repos_uuid;
svn_node_kind_t external_kind;
svn_revnum_t peg_revision;
svn_revnum_t revision;
item = APR_ARRAY_IDX(externals_p, i, svn_wc_external_item2_t*);
svn_pool_clear(iterpool2);
external_abspath = svn_dirent_join(externals_parent_abspath,
item->target_dir,
iterpool2);
err = svn_wc__resolve_relative_external_url(
&resolved_url,
item,
externals_parent_repos_root_url,
externals_parent_url,
scratch_pool, scratch_pool);
if (err)
goto handle_error;
/* This is a hack. We only need to call svn_wc_upgrade() on external
* dirs, as file externals are upgraded along with their defining
* WC. Reading the kind will throw an exception on an external dir,
* saying that the wc must be upgraded. If it's a file, the lookup
* is done in an adm_dir belonging to the defining wc (which has
* already been upgraded) and no error is returned. If it doesn't
* exist (external that isn't checked out yet), we'll just get
* svn_node_none. */
err = svn_wc_read_kind2(&external_kind, ctx->wc_ctx,
external_abspath, TRUE, FALSE, iterpool2);
if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
{
svn_error_clear(err);
err = svn_client_upgrade(external_abspath, ctx, iterpool2);
if (err)
goto handle_error;
}
else if (err)
goto handle_error;
/* The upgrade of any dir should be done now, get the now reliable
* kind. */
err = svn_wc_read_kind2(&external_kind, ctx->wc_ctx, external_abspath,
TRUE, FALSE, iterpool2);
if (err)
goto handle_error;
/* Update the EXTERNALS table according to the root URL,
* relpath and uuid known in the upgraded external WC. */
/* We should probably have a function that provides all three
* of root URL, repos relpath and uuid at once, but here goes... */
/* First get the relpath, as that returns SVN_ERR_WC_PATH_NOT_FOUND
* when the node is not present in the file system.
* svn_wc__node_get_repos_info() would try to derive the URL. */
err = svn_wc__node_get_repos_info(NULL,
&repos_relpath,
&repos_root_url,
&repos_uuid,
ctx->wc_ctx,
external_abspath,
iterpool2, iterpool2);
if (err)
goto handle_error;
/* If we haven't got any information from the checked out external,
* or if the URL information mismatches the external's definition,
* ask fetch_repos_info() to find out the repos root. */
if (0 != strcmp(resolved_url,
svn_path_url_add_component2(repos_root_url,
repos_relpath,
scratch_pool)))
{
err = fetch_repos_info(&repos_root_url,
&repos_uuid,
&info_baton,
resolved_url,
scratch_pool, scratch_pool);
if (err)
goto handle_error;
repos_relpath = svn_uri_skip_ancestor(repos_root_url,
resolved_url,
iterpool2);
/* There's just the URL, no idea what kind the external is.
* That's fine, as the external isn't even checked out yet.
* The kind will be set during the next 'update'. */
external_kind = svn_node_unknown;
}
if (err)
goto handle_error;
peg_revision = (item->peg_revision.kind == svn_opt_revision_number
? item->peg_revision.value.number
: SVN_INVALID_REVNUM);
revision = (item->revision.kind == svn_opt_revision_number
? item->revision.value.number
: SVN_INVALID_REVNUM);
err = svn_wc__upgrade_add_external_info(ctx->wc_ctx,
external_abspath,
external_kind,
externals_parent,
repos_relpath,
repos_root_url,
repos_uuid,
peg_revision,
revision,
iterpool2);
handle_error:
if (err)
{
svn_wc_notify_t *notify =
svn_wc_create_notify(external_abspath,
svn_wc_notify_failed_external,
scratch_pool);
notify->err = err;
ctx->notify_func2(ctx->notify_baton2,
notify, scratch_pool);
svn_error_clear(err);
/* Next external node, please... */
}
}
}
svn_pool_destroy(iterpool);
svn_pool_destroy(iterpool2);
return SVN_NO_ERROR;
}
Index: vendor/subversion/dist/subversion/libsvn_delta/svndiff.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_delta/svndiff.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_delta/svndiff.c (revision 286501)
@@ -1,1107 +1,1115 @@
/*
* svndiff.c -- Encoding and decoding svndiff-format deltas.
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include <assert.h>
#include <string.h>
#include "svn_delta.h"
#include "svn_io.h"
#include "delta.h"
#include "svn_pools.h"
#include "svn_private_config.h"
#include <zlib.h>
#include "private/svn_error_private.h"
#include "private/svn_delta_private.h"
/* The zlib compressBound function was not exported until 1.2.0. */
#if ZLIB_VERNUM >= 0x1200
#define svnCompressBound(LEN) compressBound(LEN)
#else
#define svnCompressBound(LEN) ((LEN) + ((LEN) >> 12) + ((LEN) >> 14) + 11)
#endif
/* For svndiff1, address/instruction/new data under this size will not
be compressed using zlib as a secondary compressor. */
#define MIN_COMPRESS_SIZE 512
/* ----- Text delta to svndiff ----- */
/* We make one of these and get it passed back to us in calls to the
window handler. We only use it to record the write function and
baton passed to svn_txdelta_to_svndiff3(). */
struct encoder_baton {
svn_stream_t *output;
svn_boolean_t header_done;
int version;
int compression_level;
apr_pool_t *pool;
};
/* This is at least as big as the largest size of an integer that
encode_int can generate; it is sufficient for creating buffers for
it to write into. This assumes that integers are at most 64 bits,
and so 10 bytes (with 7 bits of information each) are sufficient to
represent them. */
#define MAX_ENCODED_INT_LEN 10
/* This is at least as big as the largest size for a single instruction. */
#define MAX_INSTRUCTION_LEN (2*MAX_ENCODED_INT_LEN+1)
/* This is at least as big as the largest possible instructions
section: in theory, the instructions could be SVN_DELTA_WINDOW_SIZE
1-byte copy-from-source instructions (though this is very unlikely). */
#define MAX_INSTRUCTION_SECTION_LEN (SVN_DELTA_WINDOW_SIZE*MAX_INSTRUCTION_LEN)
/* Encode VAL into the buffer P using the variable-length svndiff
integer format. Return the incremented value of P after the
encoded bytes have been written. P must point to a buffer of size
at least MAX_ENCODED_INT_LEN.
This encoding uses the high bit of each byte as a continuation bit
and the other seven bits as data bits. High-order data bits are
encoded first, followed by lower-order bits, so the value can be
reconstructed by concatenating the data bits from left to right and
interpreting the result as a binary number. Examples (brackets
denote byte boundaries, spaces are for clarity only):
1 encodes as [0 0000001]
33 encodes as [0 0100001]
129 encodes as [1 0000001] [0 0000001]
2000 encodes as [1 0001111] [0 1010000]
*/
static unsigned char *
encode_int(unsigned char *p, svn_filesize_t val)
{
int n;
svn_filesize_t v;
unsigned char cont;
SVN_ERR_ASSERT_NO_RETURN(val >= 0);
/* Figure out how many bytes we'll need. */
v = val >> 7;
n = 1;
while (v > 0)
{
v = v >> 7;
n++;
}
SVN_ERR_ASSERT_NO_RETURN(n <= MAX_ENCODED_INT_LEN);
/* Encode the remaining bytes; n is always the number of bytes
coming after the one we're encoding. */
while (--n >= 0)
{
cont = ((n > 0) ? 0x1 : 0x0) << 7;
*p++ = (unsigned char)(((val >> (n * 7)) & 0x7f) | cont);
}
return p;
}
/* Append an encoded integer to a string. */
static void
append_encoded_int(svn_stringbuf_t *header, svn_filesize_t val)
{
unsigned char buf[MAX_ENCODED_INT_LEN], *p;
p = encode_int(buf, val);
svn_stringbuf_appendbytes(header, (const char *)buf, p - buf);
}
/* If IN is a string that is >= MIN_COMPRESS_SIZE and the COMPRESSION_LEVEL
is not SVN_DELTA_COMPRESSION_LEVEL_NONE, zlib compress it and places the
result in OUT, with an integer prepended specifying the original size.
If IN is < MIN_COMPRESS_SIZE, or if the compressed version of IN was no
smaller than the original IN, OUT will be a copy of IN with the size
prepended as an integer. */
static svn_error_t *
zlib_encode(const char *data,
apr_size_t len,
svn_stringbuf_t *out,
int compression_level)
{
unsigned long endlen;
apr_size_t intlen;
svn_stringbuf_setempty(out);
append_encoded_int(out, len);
intlen = out->len;
/* Compression initialization overhead is considered to large for
short buffers. Also, if we don't actually want to compress data,
ZLIB will produce an output no shorter than the input. Hence,
the DATA would directly appended to OUT, so we can do that directly
without calling ZLIB before. */
if ( (len < MIN_COMPRESS_SIZE)
|| (compression_level == SVN_DELTA_COMPRESSION_LEVEL_NONE))
{
svn_stringbuf_appendbytes(out, data, len);
}
else
{
int zerr;
svn_stringbuf_ensure(out, svnCompressBound(len) + intlen);
endlen = out->blocksize;
zerr = compress2((unsigned char *)out->data + intlen, &endlen,
(const unsigned char *)data, len,
compression_level);
if (zerr != Z_OK)
return svn_error_trace(svn_error__wrap_zlib(
zerr, "compress2",
_("Compression of svndiff data failed")));
/* Compression didn't help :(, just append the original text */
if (endlen >= len)
{
svn_stringbuf_appendbytes(out, data, len);
return SVN_NO_ERROR;
}
out->len = endlen + intlen;
out->data[out->len] = 0;
}
return SVN_NO_ERROR;
}
static svn_error_t *
send_simple_insertion_window(svn_txdelta_window_t *window,
struct encoder_baton *eb)
{
unsigned char headers[4 + 5 * MAX_ENCODED_INT_LEN + MAX_INSTRUCTION_LEN];
unsigned char ibuf[MAX_INSTRUCTION_LEN];
unsigned char *header_current;
apr_size_t header_len;
apr_size_t ip_len, i;
apr_size_t len = window->new_data->len;
/* there is only one target copy op. It must span the whole window */
assert(window->ops[0].action_code == svn_txdelta_new);
assert(window->ops[0].length == window->tview_len);
assert(window->ops[0].offset == 0);
/* write stream header if necessary */
if (!eb->header_done)
{
eb->header_done = TRUE;
headers[0] = 'S';
headers[1] = 'V';
headers[2] = 'N';
headers[3] = (unsigned char)eb->version;
header_current = headers + 4;
}
else
{
header_current = headers;
}
/* Encode the action code and length. */
if (window->tview_len >> 6 == 0)
{
ibuf[0] = (unsigned char)(window->tview_len + (0x2 << 6));
ip_len = 1;
}
else
{
ibuf[0] = (0x2 << 6);
ip_len = encode_int(ibuf + 1, window->tview_len) - ibuf;
}
/* encode the window header. Please note that the source window may
* have content despite not being used for deltification. */
header_current = encode_int(header_current, window->sview_offset);
header_current = encode_int(header_current, window->sview_len);
header_current = encode_int(header_current, window->tview_len);
header_current[0] = (unsigned char)ip_len; /* 1 instruction */
header_current = encode_int(&header_current[1], len);
/* append instructions (1 to a handful of bytes) */
for (i = 0; i < ip_len; ++i)
header_current[i] = ibuf[i];
header_len = header_current - headers + ip_len;
/* Write out the window. */
SVN_ERR(svn_stream_write(eb->output, (const char *)headers, &header_len));
if (len)
SVN_ERR(svn_stream_write(eb->output, window->new_data->data, &len));
return SVN_NO_ERROR;
}
static svn_error_t *
window_handler(svn_txdelta_window_t *window, void *baton)
{
struct encoder_baton *eb = baton;
apr_pool_t *pool;
svn_stringbuf_t *instructions;
svn_stringbuf_t *i1;
svn_stringbuf_t *header;
const svn_string_t *newdata;
unsigned char ibuf[MAX_INSTRUCTION_LEN], *ip;
const svn_txdelta_op_t *op;
apr_size_t len;
/* use specialized code if there is no source */
if (window && !window->src_ops && window->num_ops == 1 && !eb->version)
return svn_error_trace(send_simple_insertion_window(window, eb));
/* Make sure we write the header. */
if (!eb->header_done)
{
char svnver[4] = {'S','V','N','\0'};
len = 4;
svnver[3] = (char)eb->version;
SVN_ERR(svn_stream_write(eb->output, svnver, &len));
eb->header_done = TRUE;
}
if (window == NULL)
{
svn_stream_t *output = eb->output;
/* We're done; clean up.
We clean our pool first. Given that the output stream was passed
TO us, we'll assume it has a longer lifetime, and that it will not
be affected by our pool destruction.
The contrary point of view (close the stream first): that could
tell our user that everything related to the output stream is done,
and a cleanup of the user pool should occur. However, that user
pool could include the subpool we created for our work (eb->pool),
which would then make our call to svn_pool_destroy() puke.
*/
svn_pool_destroy(eb->pool);
return svn_stream_close(output);
}
/* create the necessary data buffers */
pool = svn_pool_create(eb->pool);
instructions = svn_stringbuf_create_empty(pool);
i1 = svn_stringbuf_create_empty(pool);
header = svn_stringbuf_create_empty(pool);
/* Encode the instructions. */
for (op = window->ops; op < window->ops + window->num_ops; op++)
{
/* Encode the action code and length. */
ip = ibuf;
switch (op->action_code)
{
case svn_txdelta_source: *ip = 0; break;
case svn_txdelta_target: *ip = (0x1 << 6); break;
case svn_txdelta_new: *ip = (0x2 << 6); break;
}
if (op->length >> 6 == 0)
*ip++ |= (unsigned char)op->length;
else
ip = encode_int(ip + 1, op->length);
if (op->action_code != svn_txdelta_new)
ip = encode_int(ip, op->offset);
svn_stringbuf_appendbytes(instructions, (const char *)ibuf, ip - ibuf);
}
/* Encode the header. */
append_encoded_int(header, window->sview_offset);
append_encoded_int(header, window->sview_len);
append_encoded_int(header, window->tview_len);
if (eb->version == 1)
{
SVN_ERR(zlib_encode(instructions->data, instructions->len,
i1, eb->compression_level));
instructions = i1;
}
append_encoded_int(header, instructions->len);
if (eb->version == 1)
{
svn_stringbuf_t *temp = svn_stringbuf_create_empty(pool);
svn_string_t *tempstr = svn_string_create_empty(pool);
SVN_ERR(zlib_encode(window->new_data->data, window->new_data->len,
temp, eb->compression_level));
tempstr->data = temp->data;
tempstr->len = temp->len;
newdata = tempstr;
}
else
newdata = window->new_data;
append_encoded_int(header, newdata->len);
/* Write out the window. */
len = header->len;
SVN_ERR(svn_stream_write(eb->output, header->data, &len));
if (instructions->len > 0)
{
len = instructions->len;
SVN_ERR(svn_stream_write(eb->output, instructions->data, &len));
}
if (newdata->len > 0)
{
len = newdata->len;
SVN_ERR(svn_stream_write(eb->output, newdata->data, &len));
}
svn_pool_destroy(pool);
return SVN_NO_ERROR;
}
void
svn_txdelta_to_svndiff3(svn_txdelta_window_handler_t *handler,
void **handler_baton,
svn_stream_t *output,
int svndiff_version,
int compression_level,
apr_pool_t *pool)
{
apr_pool_t *subpool = svn_pool_create(pool);
struct encoder_baton *eb;
eb = apr_palloc(subpool, sizeof(*eb));
eb->output = output;
eb->header_done = FALSE;
eb->pool = subpool;
eb->version = svndiff_version;
eb->compression_level = compression_level;
*handler = window_handler;
*handler_baton = eb;
}
void
svn_txdelta_to_svndiff2(svn_txdelta_window_handler_t *handler,
void **handler_baton,
svn_stream_t *output,
int svndiff_version,
apr_pool_t *pool)
{
svn_txdelta_to_svndiff3(handler, handler_baton, output, svndiff_version,
SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool);
}
void
svn_txdelta_to_svndiff(svn_stream_t *output,
apr_pool_t *pool,
svn_txdelta_window_handler_t *handler,
void **handler_baton)
{
svn_txdelta_to_svndiff3(handler, handler_baton, output, 0,
SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool);
}
/* ----- svndiff to text delta ----- */
/* An svndiff parser object. */
struct decode_baton
{
/* Once the svndiff parser has enough data buffered to create a
"window", it passes this window to the caller's consumer routine. */
svn_txdelta_window_handler_t consumer_func;
void *consumer_baton;
/* Pool to create subpools from; each developing window will be a
subpool. */
apr_pool_t *pool;
/* The current subpool which contains our current window-buffer. */
apr_pool_t *subpool;
/* The actual svndiff data buffer, living within subpool. */
svn_stringbuf_t *buffer;
/* The offset and size of the last source view, so that we can check
to make sure the next one isn't sliding backwards. */
svn_filesize_t last_sview_offset;
apr_size_t last_sview_len;
/* We have to discard four bytes at the beginning for the header.
This field keeps track of how many of those bytes we have read. */
apr_size_t header_bytes;
/* Do we want an error to occur when we close the stream that
indicates we didn't send the whole svndiff data? If you plan to
not transmit the whole svndiff data stream, you will want this to
be FALSE. */
svn_boolean_t error_on_early_close;
/* svndiff version in use by delta. */
unsigned char version;
};
/* Decode an svndiff-encoded integer into *VAL and return a pointer to
the byte after the integer. The bytes to be decoded live in the
range [P..END-1]. If these bytes do not contain a whole encoded
integer, return NULL; in this case *VAL is undefined.
See the comment for encode_int() earlier in this file for more detail on
the encoding format. */
static const unsigned char *
decode_file_offset(svn_filesize_t *val,
const unsigned char *p,
const unsigned char *end)
{
svn_filesize_t temp = 0;
if (p + MAX_ENCODED_INT_LEN < end)
end = p + MAX_ENCODED_INT_LEN;
/* Decode bytes until we're done. */
while (p < end)
{
/* Don't use svn_filesize_t here, because this might be 64 bits
* on 32 bit targets. Optimizing compilers may or may not be
* able to reduce that to the effective code below. */
unsigned int c = *p++;
temp = (temp << 7) | (c & 0x7f);
if (c < 0x80)
{
*val = temp;
return p;
}
}
return NULL;
}
/* Same as above, only decode into a size variable. */
static const unsigned char *
decode_size(apr_size_t *val,
const unsigned char *p,
const unsigned char *end)
{
apr_size_t temp = 0;
if (p + MAX_ENCODED_INT_LEN < end)
end = p + MAX_ENCODED_INT_LEN;
/* Decode bytes until we're done. */
while (p < end)
{
apr_size_t c = *p++;
temp = (temp << 7) | (c & 0x7f);
if (c < 0x80)
{
*val = temp;
return p;
}
}
return NULL;
}
/* Decode the possibly-zlib compressed string of length INLEN that is in
IN, into OUT. We expect an integer is prepended to IN that specifies
the original size, and that if encoded size == original size, that the
remaining data is not compressed.
In that case, we will simply return pointer into IN as data pointer for
OUT, COPYLESS_ALLOWED has been set. The, the caller is expected not to
modify the contents of OUT.
An error is returned if the decoded length exceeds the given LIMIT.
*/
static svn_error_t *
zlib_decode(const unsigned char *in, apr_size_t inLen, svn_stringbuf_t *out,
apr_size_t limit)
{
apr_size_t len;
const unsigned char *oldplace = in;
/* First thing in the string is the original length. */
in = decode_size(&len, in, in + inLen);
if (in == NULL)
return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA, NULL,
_("Decompression of svndiff data failed: no size"));
if (len > limit)
return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA, NULL,
_("Decompression of svndiff data failed: "
"size too large"));
/* We need to subtract the size of the encoded original length off the
* still remaining input length. */
inLen -= (in - oldplace);
if (inLen == len)
{
svn_stringbuf_ensure(out, len);
memcpy(out->data, in, len);
out->data[len] = 0;
out->len = len;
return SVN_NO_ERROR;
}
else
{
unsigned long zlen = len;
int zerr;
svn_stringbuf_ensure(out, len);
zerr = uncompress((unsigned char *)out->data, &zlen, in, inLen);
if (zerr != Z_OK)
return svn_error_trace(svn_error__wrap_zlib(
zerr, "uncompress",
_("Decompression of svndiff data failed")));
/* Zlib should not produce something that has a different size than the
original length we stored. */
if (zlen != len)
return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA,
NULL,
_("Size of uncompressed data "
"does not match stored original length"));
out->data[zlen] = 0;
out->len = zlen;
}
return SVN_NO_ERROR;
}
/* Decode an instruction into OP, returning a pointer to the text
after the instruction. Note that if the action code is
svn_txdelta_new, the offset field of *OP will not be set. */
static const unsigned char *
decode_instruction(svn_txdelta_op_t *op,
const unsigned char *p,
const unsigned char *end)
{
apr_size_t c;
apr_size_t action;
if (p == end)
return NULL;
/* We need this more than once */
c = *p++;
/* Decode the instruction selector. */
action = (c >> 6) & 0x3;
if (action >= 0x3)
return NULL;
/* This relies on enum svn_delta_action values to match and never to be
redefined. */
op->action_code = (enum svn_delta_action)(action);
/* Decode the length and offset. */
op->length = c & 0x3f;
if (op->length == 0)
{
p = decode_size(&op->length, p, end);
if (p == NULL)
return NULL;
}
if (action != svn_txdelta_new)
{
p = decode_size(&op->offset, p, end);
if (p == NULL)
return NULL;
}
return p;
}
/* Count the instructions in the range [P..END-1] and make sure they
are valid for the given window lengths. Return an error if the
instructions are invalid; otherwise set *NINST to the number of
instructions. */
static svn_error_t *
count_and_verify_instructions(int *ninst,
const unsigned char *p,
const unsigned char *end,
apr_size_t sview_len,
apr_size_t tview_len,
apr_size_t new_len)
{
int n = 0;
svn_txdelta_op_t op;
apr_size_t tpos = 0, npos = 0;
while (p < end)
{
p = decode_instruction(&op, p, end);
/* Detect any malformed operations from the instruction stream. */
if (p == NULL)
return svn_error_createf
(SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
_("Invalid diff stream: insn %d cannot be decoded"), n);
else if (op.length == 0)
return svn_error_createf
(SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
_("Invalid diff stream: insn %d has length zero"), n);
else if (op.length > tview_len - tpos)
return svn_error_createf
(SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
_("Invalid diff stream: insn %d overflows the target view"), n);
switch (op.action_code)
{
case svn_txdelta_source:
if (op.length > sview_len - op.offset ||
op.offset > sview_len)
return svn_error_createf
(SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
_("Invalid diff stream: "
"[src] insn %d overflows the source view"), n);
break;
case svn_txdelta_target:
if (op.offset >= tpos)
return svn_error_createf
(SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
_("Invalid diff stream: "
"[tgt] insn %d starts beyond the target view position"), n);
break;
case svn_txdelta_new:
if (op.length > new_len - npos)
return svn_error_createf
(SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
_("Invalid diff stream: "
"[new] insn %d overflows the new data section"), n);
npos += op.length;
break;
}
tpos += op.length;
n++;
}
if (tpos != tview_len)
return svn_error_create(SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
_("Delta does not fill the target window"));
if (npos != new_len)
return svn_error_create(SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
_("Delta does not contain enough new data"));
*ninst = n;
return SVN_NO_ERROR;
}
/* Given the five integer fields of a window header and a pointer to
the remainder of the window contents, fill in a delta window
structure *WINDOW. New allocations will be performed in POOL;
the new_data field of *WINDOW will refer directly to memory pointed
to by DATA. */
static svn_error_t *
decode_window(svn_txdelta_window_t *window, svn_filesize_t sview_offset,
apr_size_t sview_len, apr_size_t tview_len, apr_size_t inslen,
apr_size_t newlen, const unsigned char *data, apr_pool_t *pool,
unsigned int version)
{
const unsigned char *insend;
int ninst;
apr_size_t npos;
svn_txdelta_op_t *ops, *op;
svn_string_t *new_data = apr_palloc(pool, sizeof(*new_data));
window->sview_offset = sview_offset;
window->sview_len = sview_len;
window->tview_len = tview_len;
insend = data + inslen;
if (version == 1)
{
svn_stringbuf_t *instout = svn_stringbuf_create_empty(pool);
svn_stringbuf_t *ndout = svn_stringbuf_create_empty(pool);
SVN_ERR(zlib_decode(insend, newlen, ndout,
SVN_DELTA_WINDOW_SIZE));
SVN_ERR(zlib_decode(data, insend - data, instout,
MAX_INSTRUCTION_SECTION_LEN));
newlen = ndout->len;
data = (unsigned char *)instout->data;
insend = (unsigned char *)instout->data + instout->len;
new_data->data = (const char *) ndout->data;
new_data->len = newlen;
}
else
{
/* Copy the data because an svn_string_t must have the invariant
data[len]=='\0'. */
char *buf = apr_palloc(pool, newlen + 1);
memcpy(buf, insend, newlen);
buf[newlen] = '\0';
new_data->data = buf;
new_data->len = newlen;
}
/* Count the instructions and make sure they are all valid. */
SVN_ERR(count_and_verify_instructions(&ninst, data, insend,
sview_len, tview_len, newlen));
/* Allocate a buffer for the instructions and decode them. */
ops = apr_palloc(pool, ninst * sizeof(*ops));
npos = 0;
window->src_ops = 0;
for (op = ops; op < ops + ninst; op++)
{
data = decode_instruction(op, data, insend);
if (op->action_code == svn_txdelta_source)
++window->src_ops;
else if (op->action_code == svn_txdelta_new)
{
op->offset = npos;
npos += op->length;
}
}
SVN_ERR_ASSERT(data == insend);
window->ops = ops;
window->num_ops = ninst;
window->new_data = new_data;
return SVN_NO_ERROR;
}
static svn_error_t *
write_handler(void *baton,
const char *buffer,
apr_size_t *len)
{
struct decode_baton *db = (struct decode_baton *) baton;
const unsigned char *p, *end;
svn_filesize_t sview_offset;
apr_size_t sview_len, tview_len, inslen, newlen, remaining;
apr_size_t buflen = *len;
/* Chew up four bytes at the beginning for the header. */
if (db->header_bytes < 4)
{
apr_size_t nheader = 4 - db->header_bytes;
if (nheader > buflen)
nheader = buflen;
if (memcmp(buffer, "SVN\0" + db->header_bytes, nheader) == 0)
db->version = 0;
else if (memcmp(buffer, "SVN\1" + db->header_bytes, nheader) == 0)
db->version = 1;
else
return svn_error_create(SVN_ERR_SVNDIFF_INVALID_HEADER, NULL,
_("Svndiff has invalid header"));
buflen -= nheader;
buffer += nheader;
db->header_bytes += nheader;
}
/* Concatenate the old with the new. */
svn_stringbuf_appendbytes(db->buffer, buffer, buflen);
/* We have a buffer of svndiff data that might be good for:
a) an integral number of windows' worth of data - this is a
trivial case. Make windows from our data and ship them off.
b) a non-integral number of windows' worth of data - we shall
consume the integral portion of the window data, and then
somewhere in the following loop the decoding of the svndiff
data will run out of stuff to decode, and will simply return
SVN_NO_ERROR, anxiously awaiting more data.
*/
while (1)
{
apr_pool_t *newpool;
svn_txdelta_window_t window;
/* Read the header, if we have enough bytes for that. */
p = (const unsigned char *) db->buffer->data;
end = (const unsigned char *) db->buffer->data + db->buffer->len;
p = decode_file_offset(&sview_offset, p, end);
if (p == NULL)
- return SVN_NO_ERROR;
+ break;
p = decode_size(&sview_len, p, end);
if (p == NULL)
- return SVN_NO_ERROR;
+ break;
p = decode_size(&tview_len, p, end);
if (p == NULL)
- return SVN_NO_ERROR;
+ break;
p = decode_size(&inslen, p, end);
if (p == NULL)
- return SVN_NO_ERROR;
+ break;
p = decode_size(&newlen, p, end);
if (p == NULL)
- return SVN_NO_ERROR;
+ break;
if (tview_len > SVN_DELTA_WINDOW_SIZE ||
sview_len > SVN_DELTA_WINDOW_SIZE ||
/* for svndiff1, newlen includes the original length */
newlen > SVN_DELTA_WINDOW_SIZE + MAX_ENCODED_INT_LEN ||
inslen > MAX_INSTRUCTION_SECTION_LEN)
return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL,
_("Svndiff contains a too-large window"));
/* Check for integer overflow. */
if (sview_offset < 0 || inslen + newlen < inslen
|| sview_len + tview_len < sview_len
|| (apr_size_t)sview_offset + sview_len < (apr_size_t)sview_offset)
return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL,
_("Svndiff contains corrupt window header"));
/* Check for source windows which slide backwards. */
if (sview_len > 0
&& (sview_offset < db->last_sview_offset
|| (sview_offset + sview_len
< db->last_sview_offset + db->last_sview_len)))
return svn_error_create
(SVN_ERR_SVNDIFF_BACKWARD_VIEW, NULL,
_("Svndiff has backwards-sliding source views"));
/* Wait for more data if we don't have enough bytes for the
whole window. */
if ((apr_size_t) (end - p) < inslen + newlen)
return SVN_NO_ERROR;
/* Decode the window and send it off. */
SVN_ERR(decode_window(&window, sview_offset, sview_len, tview_len,
inslen, newlen, p, db->subpool,
db->version));
SVN_ERR(db->consumer_func(&window, db->consumer_baton));
/* Make a new subpool and buffer, saving aside the remaining
data in the old buffer. */
newpool = svn_pool_create(db->pool);
p += inslen + newlen;
remaining = db->buffer->data + db->buffer->len - (const char *) p;
db->buffer =
svn_stringbuf_ncreate((const char *) p, remaining, newpool);
/* Remember the offset and length of the source view for next time. */
db->last_sview_offset = sview_offset;
db->last_sview_len = sview_len;
/* We've copied stuff out of the old pool. Toss that pool and use
our new pool.
### might be nice to avoid the copy and just use svn_pool_clear
### to get rid of whatever the "other stuff" is. future project...
*/
svn_pool_destroy(db->subpool);
db->subpool = newpool;
}
- /* NOTREACHED */
+ /* At this point we processed all integral windows and DB->BUFFER is empty
+ or contains partially read window header.
+ Check that unprocessed data is not larger that theoretical maximum
+ window header size. */
+ if (db->buffer->len > 5 * MAX_ENCODED_INT_LEN)
+ return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL,
+ _("Svndiff contains a too-large window header"));
+
+ return SVN_NO_ERROR;
}
/* Minimal svn_stream_t write handler, doing nothing */
static svn_error_t *
noop_write_handler(void *baton,
const char *buffer,
apr_size_t *len)
{
return SVN_NO_ERROR;
}
static svn_error_t *
close_handler(void *baton)
{
struct decode_baton *db = (struct decode_baton *) baton;
svn_error_t *err;
/* Make sure that we're at a plausible end of stream, returning an
error if we are expected to do so. */
if ((db->error_on_early_close)
&& (db->header_bytes < 4 || db->buffer->len != 0))
return svn_error_create(SVN_ERR_SVNDIFF_UNEXPECTED_END, NULL,
_("Unexpected end of svndiff input"));
/* Tell the window consumer that we're done, and clean up. */
err = db->consumer_func(NULL, db->consumer_baton);
svn_pool_destroy(db->pool);
return err;
}
svn_stream_t *
svn_txdelta_parse_svndiff(svn_txdelta_window_handler_t handler,
void *handler_baton,
svn_boolean_t error_on_early_close,
apr_pool_t *pool)
{
apr_pool_t *subpool = svn_pool_create(pool);
struct decode_baton *db = apr_palloc(pool, sizeof(*db));
svn_stream_t *stream;
db->consumer_func = handler;
db->consumer_baton = handler_baton;
db->pool = subpool;
db->subpool = svn_pool_create(subpool);
db->buffer = svn_stringbuf_create_empty(db->subpool);
db->last_sview_offset = 0;
db->last_sview_len = 0;
db->header_bytes = 0;
db->error_on_early_close = error_on_early_close;
stream = svn_stream_create(db, pool);
if (handler != svn_delta_noop_window_handler)
{
svn_stream_set_write(stream, write_handler);
svn_stream_set_close(stream, close_handler);
}
else
{
/* And else we just ignore everything as efficiently as we can.
by only hooking a no-op handler */
svn_stream_set_write(stream, noop_write_handler);
}
return stream;
}
/* Routines for reading one svndiff window at a time. */
/* Read one byte from STREAM into *BYTE. */
static svn_error_t *
read_one_byte(unsigned char *byte, svn_stream_t *stream)
{
char c;
apr_size_t len = 1;
SVN_ERR(svn_stream_read(stream, &c, &len));
if (len == 0)
return svn_error_create(SVN_ERR_SVNDIFF_UNEXPECTED_END, NULL,
_("Unexpected end of svndiff input"));
*byte = (unsigned char) c;
return SVN_NO_ERROR;
}
/* Read and decode one integer from STREAM into *SIZE. */
static svn_error_t *
read_one_size(apr_size_t *size, svn_stream_t *stream)
{
unsigned char c;
*size = 0;
while (1)
{
SVN_ERR(read_one_byte(&c, stream));
*size = (*size << 7) | (c & 0x7f);
if (!(c & 0x80))
break;
}
return SVN_NO_ERROR;
}
/* Read a window header from STREAM and check it for integer overflow. */
static svn_error_t *
read_window_header(svn_stream_t *stream, svn_filesize_t *sview_offset,
apr_size_t *sview_len, apr_size_t *tview_len,
apr_size_t *inslen, apr_size_t *newlen)
{
unsigned char c;
/* Read the source view offset by hand, since it's not an apr_size_t. */
*sview_offset = 0;
while (1)
{
SVN_ERR(read_one_byte(&c, stream));
*sview_offset = (*sview_offset << 7) | (c & 0x7f);
if (!(c & 0x80))
break;
}
/* Read the four size fields. */
SVN_ERR(read_one_size(sview_len, stream));
SVN_ERR(read_one_size(tview_len, stream));
SVN_ERR(read_one_size(inslen, stream));
SVN_ERR(read_one_size(newlen, stream));
if (*tview_len > SVN_DELTA_WINDOW_SIZE ||
*sview_len > SVN_DELTA_WINDOW_SIZE ||
/* for svndiff1, newlen includes the original length */
*newlen > SVN_DELTA_WINDOW_SIZE + MAX_ENCODED_INT_LEN ||
*inslen > MAX_INSTRUCTION_SECTION_LEN)
return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL,
_("Svndiff contains a too-large window"));
/* Check for integer overflow. */
if (*sview_offset < 0 || *inslen + *newlen < *inslen
|| *sview_len + *tview_len < *sview_len
|| (apr_size_t)*sview_offset + *sview_len < (apr_size_t)*sview_offset)
return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL,
_("Svndiff contains corrupt window header"));
return SVN_NO_ERROR;
}
svn_error_t *
svn_txdelta_read_svndiff_window(svn_txdelta_window_t **window,
svn_stream_t *stream,
int svndiff_version,
apr_pool_t *pool)
{
svn_filesize_t sview_offset;
apr_size_t sview_len, tview_len, inslen, newlen, len;
unsigned char *buf;
SVN_ERR(read_window_header(stream, &sview_offset, &sview_len, &tview_len,
&inslen, &newlen));
len = inslen + newlen;
buf = apr_palloc(pool, len);
SVN_ERR(svn_stream_read(stream, (char*)buf, &len));
if (len < inslen + newlen)
return svn_error_create(SVN_ERR_SVNDIFF_UNEXPECTED_END, NULL,
_("Unexpected end of svndiff input"));
*window = apr_palloc(pool, sizeof(**window));
return decode_window(*window, sview_offset, sview_len, tview_len, inslen,
newlen, buf, pool, svndiff_version);
}
svn_error_t *
svn_txdelta_skip_svndiff_window(apr_file_t *file,
int svndiff_version,
apr_pool_t *pool)
{
svn_stream_t *stream = svn_stream_from_aprfile2(file, TRUE, pool);
svn_filesize_t sview_offset;
apr_size_t sview_len, tview_len, inslen, newlen;
apr_off_t offset;
SVN_ERR(read_window_header(stream, &sview_offset, &sview_len, &tview_len,
&inslen, &newlen));
offset = inslen + newlen;
return svn_io_file_seek(file, APR_CUR, &offset, pool);
}
svn_error_t *
svn__compress(svn_string_t *in,
svn_stringbuf_t *out,
int compression_level)
{
return zlib_encode(in->data, in->len, out, compression_level);
}
svn_error_t *
svn__decompress(svn_string_t *in,
svn_stringbuf_t *out,
apr_size_t limit)
{
return zlib_decode((const unsigned char*)in->data, in->len, out, limit);
}
Index: vendor/subversion/dist/subversion/libsvn_diff/parse-diff.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_diff/parse-diff.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_diff/parse-diff.c (revision 286501)
@@ -1,1373 +1,1374 @@
/*
* parse-diff.c: functions for parsing diff files
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include "svn_hash.h"
#include "svn_types.h"
#include "svn_error.h"
#include "svn_io.h"
#include "svn_pools.h"
#include "svn_props.h"
#include "svn_string.h"
#include "svn_utf.h"
#include "svn_dirent_uri.h"
#include "svn_diff.h"
#include "private/svn_eol_private.h"
#include "private/svn_dep_compat.h"
/* Helper macro for readability */
#define starts_with(str, start) \
(strncmp((str), (start), strlen(start)) == 0)
/* Like strlen() but for string literals. */
#define STRLEN_LITERAL(str) (sizeof(str) - 1)
/* This struct describes a range within a file, as well as the
* current cursor position within the range. All numbers are in bytes. */
struct svn_diff__hunk_range {
apr_off_t start;
apr_off_t end;
apr_off_t current;
};
struct svn_diff_hunk_t {
/* The patch this hunk belongs to. */
svn_patch_t *patch;
/* APR file handle to the patch file this hunk came from. */
apr_file_t *apr_file;
/* Ranges used to keep track of this hunk's texts positions within
* the patch file. */
struct svn_diff__hunk_range diff_text_range;
struct svn_diff__hunk_range original_text_range;
struct svn_diff__hunk_range modified_text_range;
/* Hunk ranges as they appeared in the patch file.
* All numbers are lines, not bytes. */
svn_linenum_t original_start;
svn_linenum_t original_length;
svn_linenum_t modified_start;
svn_linenum_t modified_length;
/* Number of lines of leading and trailing hunk context. */
svn_linenum_t leading_context;
svn_linenum_t trailing_context;
};
void
svn_diff_hunk_reset_diff_text(svn_diff_hunk_t *hunk)
{
hunk->diff_text_range.current = hunk->diff_text_range.start;
}
void
svn_diff_hunk_reset_original_text(svn_diff_hunk_t *hunk)
{
if (hunk->patch->reverse)
hunk->modified_text_range.current = hunk->modified_text_range.start;
else
hunk->original_text_range.current = hunk->original_text_range.start;
}
void
svn_diff_hunk_reset_modified_text(svn_diff_hunk_t *hunk)
{
if (hunk->patch->reverse)
hunk->original_text_range.current = hunk->original_text_range.start;
else
hunk->modified_text_range.current = hunk->modified_text_range.start;
}
svn_linenum_t
svn_diff_hunk_get_original_start(const svn_diff_hunk_t *hunk)
{
return hunk->patch->reverse ? hunk->modified_start : hunk->original_start;
}
svn_linenum_t
svn_diff_hunk_get_original_length(const svn_diff_hunk_t *hunk)
{
return hunk->patch->reverse ? hunk->modified_length : hunk->original_length;
}
svn_linenum_t
svn_diff_hunk_get_modified_start(const svn_diff_hunk_t *hunk)
{
return hunk->patch->reverse ? hunk->original_start : hunk->modified_start;
}
svn_linenum_t
svn_diff_hunk_get_modified_length(const svn_diff_hunk_t *hunk)
{
return hunk->patch->reverse ? hunk->original_length : hunk->modified_length;
}
svn_linenum_t
svn_diff_hunk_get_leading_context(const svn_diff_hunk_t *hunk)
{
return hunk->leading_context;
}
svn_linenum_t
svn_diff_hunk_get_trailing_context(const svn_diff_hunk_t *hunk)
{
return hunk->trailing_context;
}
/* Try to parse a positive number from a decimal number encoded
* in the string NUMBER. Return parsed number in OFFSET, and return
* TRUE if parsing was successful. */
static svn_boolean_t
parse_offset(svn_linenum_t *offset, const char *number)
{
svn_error_t *err;
apr_uint64_t val;
err = svn_cstring_strtoui64(&val, number, 0, SVN_LINENUM_MAX_VALUE, 10);
if (err)
{
svn_error_clear(err);
return FALSE;
}
*offset = (svn_linenum_t)val;
return TRUE;
}
/* Try to parse a hunk range specification from the string RANGE.
* Return parsed information in *START and *LENGTH, and return TRUE
* if the range parsed correctly. Note: This function may modify the
* input value RANGE. */
static svn_boolean_t
parse_range(svn_linenum_t *start, svn_linenum_t *length, char *range)
{
char *comma;
if (*range == 0)
return FALSE;
comma = strstr(range, ",");
if (comma)
{
if (strlen(comma + 1) > 0)
{
/* Try to parse the length. */
if (! parse_offset(length, comma + 1))
return FALSE;
/* Snip off the end of the string,
* so we can comfortably parse the line
* number the hunk starts at. */
*comma = '\0';
}
else
/* A comma but no length? */
return FALSE;
}
else
{
*length = 1;
}
/* Try to parse the line number the hunk starts at. */
return parse_offset(start, range);
}
/* Try to parse a hunk header in string HEADER, putting parsed information
* into HUNK. Return TRUE if the header parsed correctly. ATAT is the
* character string used to delimit the hunk header.
* Do all allocations in POOL. */
static svn_boolean_t
parse_hunk_header(const char *header, svn_diff_hunk_t *hunk,
const char *atat, apr_pool_t *pool)
{
const char *p;
const char *start;
svn_stringbuf_t *range;
p = header + strlen(atat);
if (*p != ' ')
/* No. */
return FALSE;
p++;
if (*p != '-')
/* Nah... */
return FALSE;
/* OK, this may be worth allocating some memory for... */
range = svn_stringbuf_create_ensure(31, pool);
start = ++p;
while (*p && *p != ' ')
{
p++;
}
if (*p != ' ')
/* No no no... */
return FALSE;
svn_stringbuf_appendbytes(range, start, p - start);
/* Try to parse the first range. */
if (! parse_range(&hunk->original_start, &hunk->original_length, range->data))
return FALSE;
/* Clear the stringbuf so we can reuse it for the second range. */
svn_stringbuf_setempty(range);
p++;
if (*p != '+')
/* Eeek! */
return FALSE;
/* OK, this may be worth copying... */
start = ++p;
while (*p && *p != ' ')
{
p++;
}
if (*p != ' ')
/* No no no... */
return FALSE;
svn_stringbuf_appendbytes(range, start, p - start);
/* Check for trailing @@ */
p++;
if (! starts_with(p, atat))
return FALSE;
/* There may be stuff like C-function names after the trailing @@,
* but we ignore that. */
/* Try to parse the second range. */
if (! parse_range(&hunk->modified_start, &hunk->modified_length, range->data))
return FALSE;
/* Hunk header is good. */
return TRUE;
}
/* Read a line of original or modified hunk text from the specified
* RANGE within FILE. FILE is expected to contain unidiff text.
* Leading unidiff symbols ('+', '-', and ' ') are removed from the line,
* Any lines commencing with the VERBOTEN character are discarded.
* VERBOTEN should be '+' or '-', depending on which form of hunk text
* is being read.
*
* All other parameters are as in svn_diff_hunk_readline_original_text()
* and svn_diff_hunk_readline_modified_text().
*/
static svn_error_t *
hunk_readline_original_or_modified(apr_file_t *file,
struct svn_diff__hunk_range *range,
svn_stringbuf_t **stringbuf,
const char **eol,
svn_boolean_t *eof,
char verboten,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_size_t max_len;
svn_boolean_t filtered;
apr_off_t pos;
svn_stringbuf_t *str;
if (range->current >= range->end)
{
/* We're past the range. Indicate that no bytes can be read. */
*eof = TRUE;
if (eol)
*eol = NULL;
*stringbuf = svn_stringbuf_create_empty(result_pool);
return SVN_NO_ERROR;
}
pos = 0;
SVN_ERR(svn_io_file_seek(file, APR_CUR, &pos, scratch_pool));
SVN_ERR(svn_io_file_seek(file, APR_SET, &range->current, scratch_pool));
do
{
max_len = range->end - range->current;
SVN_ERR(svn_io_file_readline(file, &str, eol, eof, max_len,
result_pool, scratch_pool));
range->current = 0;
SVN_ERR(svn_io_file_seek(file, APR_CUR, &range->current, scratch_pool));
filtered = (str->data[0] == verboten || str->data[0] == '\\');
}
while (filtered && ! *eof);
if (filtered)
{
/* EOF, return an empty string. */
*stringbuf = svn_stringbuf_create_ensure(0, result_pool);
}
else if (str->data[0] == '+' || str->data[0] == '-' || str->data[0] == ' ')
{
/* Shave off leading unidiff symbols. */
*stringbuf = svn_stringbuf_create(str->data + 1, result_pool);
}
else
{
/* Return the line as-is. */
*stringbuf = svn_stringbuf_dup(str, result_pool);
}
SVN_ERR(svn_io_file_seek(file, APR_SET, &pos, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_diff_hunk_readline_original_text(svn_diff_hunk_t *hunk,
svn_stringbuf_t **stringbuf,
const char **eol,
svn_boolean_t *eof,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
return svn_error_trace(
hunk_readline_original_or_modified(hunk->apr_file,
hunk->patch->reverse ?
&hunk->modified_text_range :
&hunk->original_text_range,
stringbuf, eol, eof,
hunk->patch->reverse ? '-' : '+',
result_pool, scratch_pool));
}
svn_error_t *
svn_diff_hunk_readline_modified_text(svn_diff_hunk_t *hunk,
svn_stringbuf_t **stringbuf,
const char **eol,
svn_boolean_t *eof,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
return svn_error_trace(
hunk_readline_original_or_modified(hunk->apr_file,
hunk->patch->reverse ?
&hunk->original_text_range :
&hunk->modified_text_range,
stringbuf, eol, eof,
hunk->patch->reverse ? '+' : '-',
result_pool, scratch_pool));
}
svn_error_t *
svn_diff_hunk_readline_diff_text(svn_diff_hunk_t *hunk,
svn_stringbuf_t **stringbuf,
const char **eol,
svn_boolean_t *eof,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_diff_hunk_t dummy;
svn_stringbuf_t *line;
apr_size_t max_len;
apr_off_t pos;
if (hunk->diff_text_range.current >= hunk->diff_text_range.end)
{
/* We're past the range. Indicate that no bytes can be read. */
*eof = TRUE;
if (eol)
*eol = NULL;
*stringbuf = svn_stringbuf_create_empty(result_pool);
return SVN_NO_ERROR;
}
pos = 0;
SVN_ERR(svn_io_file_seek(hunk->apr_file, APR_CUR, &pos, scratch_pool));
SVN_ERR(svn_io_file_seek(hunk->apr_file, APR_SET,
&hunk->diff_text_range.current, scratch_pool));
max_len = hunk->diff_text_range.end - hunk->diff_text_range.current;
SVN_ERR(svn_io_file_readline(hunk->apr_file, &line, eol, eof, max_len,
result_pool,
scratch_pool));
hunk->diff_text_range.current = 0;
SVN_ERR(svn_io_file_seek(hunk->apr_file, APR_CUR,
&hunk->diff_text_range.current, scratch_pool));
SVN_ERR(svn_io_file_seek(hunk->apr_file, APR_SET, &pos, scratch_pool));
if (hunk->patch->reverse)
{
if (parse_hunk_header(line->data, &dummy, "@@", scratch_pool))
{
/* Line is a hunk header, reverse it. */
line = svn_stringbuf_createf(result_pool,
"@@ -%lu,%lu +%lu,%lu @@",
hunk->modified_start,
hunk->modified_length,
hunk->original_start,
hunk->original_length);
}
else if (parse_hunk_header(line->data, &dummy, "##", scratch_pool))
{
/* Line is a hunk header, reverse it. */
line = svn_stringbuf_createf(result_pool,
"## -%lu,%lu +%lu,%lu ##",
hunk->modified_start,
hunk->modified_length,
hunk->original_start,
hunk->original_length);
}
else
{
if (line->data[0] == '+')
line->data[0] = '-';
else if (line->data[0] == '-')
line->data[0] = '+';
}
}
*stringbuf = line;
return SVN_NO_ERROR;
}
/* Parse *PROP_NAME from HEADER as the part after the INDICATOR line.
* Allocate *PROP_NAME in RESULT_POOL.
* Set *PROP_NAME to NULL if no valid property name was found. */
static svn_error_t *
parse_prop_name(const char **prop_name, const char *header,
const char *indicator, apr_pool_t *result_pool)
{
SVN_ERR(svn_utf_cstring_to_utf8(prop_name,
header + strlen(indicator),
result_pool));
if (**prop_name == '\0')
*prop_name = NULL;
else if (! svn_prop_name_is_valid(*prop_name))
{
svn_stringbuf_t *buf = svn_stringbuf_create(*prop_name, result_pool);
svn_stringbuf_strip_whitespace(buf);
*prop_name = (svn_prop_name_is_valid(buf->data) ? buf->data : NULL);
}
return SVN_NO_ERROR;
}
/* Return the next *HUNK from a PATCH in APR_FILE.
* If no hunk can be found, set *HUNK to NULL.
* Set IS_PROPERTY to TRUE if we have a property hunk. If the returned HUNK
* is the first belonging to a certain property, then PROP_NAME and
* PROP_OPERATION will be set too. If we have a text hunk, PROP_NAME will be
* NULL. If IGNORE_WHITESPACE is TRUE, lines without leading spaces will be
* treated as context lines. Allocate results in RESULT_POOL.
* Use SCRATCH_POOL for all other allocations. */
static svn_error_t *
parse_next_hunk(svn_diff_hunk_t **hunk,
svn_boolean_t *is_property,
const char **prop_name,
svn_diff_operation_kind_t *prop_operation,
svn_patch_t *patch,
apr_file_t *apr_file,
svn_boolean_t ignore_whitespace,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
static const char * const minus = "--- ";
static const char * const text_atat = "@@";
static const char * const prop_atat = "##";
svn_stringbuf_t *line;
svn_boolean_t eof, in_hunk, hunk_seen;
apr_off_t pos, last_line;
apr_off_t start, end;
apr_off_t original_end;
apr_off_t modified_end;
svn_linenum_t original_lines;
svn_linenum_t modified_lines;
svn_linenum_t leading_context;
svn_linenum_t trailing_context;
svn_boolean_t changed_line_seen;
enum {
noise_line,
original_line,
modified_line,
context_line
} last_line_type;
apr_pool_t *iterpool;
*prop_operation = svn_diff_op_unchanged;
/* We only set this if we have a property hunk header. */
*prop_name = NULL;
*is_property = FALSE;
if (apr_file_eof(apr_file) == APR_EOF)
{
/* No more hunks here. */
*hunk = NULL;
return SVN_NO_ERROR;
}
in_hunk = FALSE;
hunk_seen = FALSE;
leading_context = 0;
trailing_context = 0;
changed_line_seen = FALSE;
original_end = 0;
modified_end = 0;
*hunk = apr_pcalloc(result_pool, sizeof(**hunk));
/* Get current seek position -- APR has no ftell() :( */
pos = 0;
SVN_ERR(svn_io_file_seek(apr_file, APR_CUR, &pos, scratch_pool));
/* Start out assuming noise. */
last_line_type = noise_line;
iterpool = svn_pool_create(scratch_pool);
do
{
svn_pool_clear(iterpool);
/* Remember the current line's offset, and read the line. */
last_line = pos;
SVN_ERR(svn_io_file_readline(apr_file, &line, NULL, &eof, APR_SIZE_MAX,
iterpool, iterpool));
/* Update line offset for next iteration. */
pos = 0;
SVN_ERR(svn_io_file_seek(apr_file, APR_CUR, &pos, iterpool));
/* Lines starting with a backslash indicate a missing EOL:
* "\ No newline at end of file" or "end of property". */
if (line->data[0] == '\\')
{
if (in_hunk)
{
char eolbuf[2];
apr_size_t len;
apr_off_t off;
apr_off_t hunk_text_end;
/* Comment terminates the hunk text and says the hunk text
* has no trailing EOL. Snip off trailing EOL which is part
* of the patch file but not part of the hunk text. */
off = last_line - 2;
SVN_ERR(svn_io_file_seek(apr_file, APR_SET, &off, iterpool));
len = sizeof(eolbuf);
SVN_ERR(svn_io_file_read_full2(apr_file, eolbuf, len, &len,
&eof, iterpool));
if (eolbuf[0] == '\r' && eolbuf[1] == '\n')
hunk_text_end = last_line - 2;
else if (eolbuf[1] == '\n' || eolbuf[1] == '\r')
hunk_text_end = last_line - 1;
else
hunk_text_end = last_line;
if (last_line_type == original_line && original_end == 0)
original_end = hunk_text_end;
else if (last_line_type == modified_line && modified_end == 0)
modified_end = hunk_text_end;
else if (last_line_type == context_line)
{
if (original_end == 0)
original_end = hunk_text_end;
if (modified_end == 0)
modified_end = hunk_text_end;
}
SVN_ERR(svn_io_file_seek(apr_file, APR_SET, &pos, iterpool));
}
continue;
}
if (in_hunk)
{
char c;
static const char add = '+';
static const char del = '-';
if (! hunk_seen)
{
/* We're reading the first line of the hunk, so the start
* of the line just read is the hunk text's byte offset. */
start = last_line;
}
c = line->data[0];
if (original_lines > 0 && modified_lines > 0 &&
((c == ' ')
/* Tolerate chopped leading spaces on empty lines. */
|| (! eof && line->len == 0)
/* Maybe tolerate chopped leading spaces on non-empty lines. */
|| (ignore_whitespace && c != del && c != add)))
{
/* It's a "context" line in the hunk. */
hunk_seen = TRUE;
original_lines--;
modified_lines--;
if (changed_line_seen)
trailing_context++;
else
leading_context++;
last_line_type = context_line;
}
else if (original_lines > 0 && c == del)
{
/* It's a "deleted" line in the hunk. */
hunk_seen = TRUE;
changed_line_seen = TRUE;
/* A hunk may have context in the middle. We only want
trailing lines of context. */
if (trailing_context > 0)
trailing_context = 0;
original_lines--;
last_line_type = original_line;
}
else if (modified_lines > 0 && c == add)
{
/* It's an "added" line in the hunk. */
hunk_seen = TRUE;
changed_line_seen = TRUE;
/* A hunk may have context in the middle. We only want
trailing lines of context. */
if (trailing_context > 0)
trailing_context = 0;
modified_lines--;
last_line_type = modified_line;
}
else
{
if (eof)
{
/* The hunk ends at EOF. */
end = pos;
}
else
{
/* The start of the current line marks the first byte
* after the hunk text. */
end = last_line;
}
if (original_end == 0)
original_end = end;
if (modified_end == 0)
modified_end = end;
break; /* Hunk was empty or has been read. */
}
}
else
{
if (starts_with(line->data, text_atat))
{
/* Looks like we have a hunk header, try to rip it apart. */
in_hunk = parse_hunk_header(line->data, *hunk, text_atat,
iterpool);
if (in_hunk)
{
original_lines = (*hunk)->original_length;
modified_lines = (*hunk)->modified_length;
*is_property = FALSE;
}
}
else if (starts_with(line->data, prop_atat))
{
/* Looks like we have a property hunk header, try to rip it
* apart. */
in_hunk = parse_hunk_header(line->data, *hunk, prop_atat,
iterpool);
if (in_hunk)
{
original_lines = (*hunk)->original_length;
modified_lines = (*hunk)->modified_length;
*is_property = TRUE;
}
}
else if (starts_with(line->data, "Added: "))
{
SVN_ERR(parse_prop_name(prop_name, line->data, "Added: ",
result_pool));
if (*prop_name)
*prop_operation = svn_diff_op_added;
}
else if (starts_with(line->data, "Deleted: "))
{
SVN_ERR(parse_prop_name(prop_name, line->data, "Deleted: ",
result_pool));
if (*prop_name)
*prop_operation = svn_diff_op_deleted;
}
else if (starts_with(line->data, "Modified: "))
{
SVN_ERR(parse_prop_name(prop_name, line->data, "Modified: ",
result_pool));
if (*prop_name)
*prop_operation = svn_diff_op_modified;
}
else if (starts_with(line->data, minus)
|| starts_with(line->data, "diff --git "))
/* This could be a header of another patch. Bail out. */
break;
}
}
/* Check for the line length since a file may not have a newline at the
* end and we depend upon the last line to be an empty one. */
while (! eof || line->len > 0);
svn_pool_destroy(iterpool);
if (! eof)
/* Rewind to the start of the line just read, so subsequent calls
* to this function or svn_diff_parse_next_patch() don't end
* up skipping the line -- it may contain a patch or hunk header. */
SVN_ERR(svn_io_file_seek(apr_file, APR_SET, &last_line, scratch_pool));
if (hunk_seen && start < end)
{
(*hunk)->patch = patch;
(*hunk)->apr_file = apr_file;
(*hunk)->leading_context = leading_context;
(*hunk)->trailing_context = trailing_context;
(*hunk)->diff_text_range.start = start;
(*hunk)->diff_text_range.current = start;
(*hunk)->diff_text_range.end = end;
(*hunk)->original_text_range.start = start;
(*hunk)->original_text_range.current = start;
(*hunk)->original_text_range.end = original_end;
(*hunk)->modified_text_range.start = start;
(*hunk)->modified_text_range.current = start;
(*hunk)->modified_text_range.end = modified_end;
}
else
/* Something went wrong, just discard the result. */
*hunk = NULL;
return SVN_NO_ERROR;
}
/* Compare function for sorting hunks after parsing.
* We sort hunks by their original line offset. */
static int
compare_hunks(const void *a, const void *b)
{
const svn_diff_hunk_t *ha = *((const svn_diff_hunk_t *const *)a);
const svn_diff_hunk_t *hb = *((const svn_diff_hunk_t *const *)b);
if (ha->original_start < hb->original_start)
return -1;
if (ha->original_start > hb->original_start)
return 1;
return 0;
}
/* Possible states of the diff header parser. */
enum parse_state
{
state_start, /* initial */
state_git_diff_seen, /* diff --git */
state_git_tree_seen, /* a tree operation, rather then content change */
state_git_minus_seen, /* --- /dev/null; or --- a/ */
state_git_plus_seen, /* +++ /dev/null; or +++ a/ */
state_move_from_seen, /* rename from foo.c */
state_copy_from_seen, /* copy from foo.c */
state_minus_seen, /* --- foo.c */
state_unidiff_found, /* valid start of a regular unidiff header */
state_git_header_found /* valid start of a --git diff header */
};
/* Data type describing a valid state transition of the parser. */
struct transition
{
const char *expected_input;
enum parse_state required_state;
/* A callback called upon each parser state transition. */
svn_error_t *(*fn)(enum parse_state *new_state, char *input,
svn_patch_t *patch, apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
};
/* UTF-8 encode and canonicalize the content of LINE as FILE_NAME. */
static svn_error_t *
grab_filename(const char **file_name, const char *line, apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *utf8_path;
const char *canon_path;
/* Grab the filename and encode it in UTF-8. */
/* TODO: Allow specifying the patch file's encoding.
* For now, we assume its encoding is native. */
/* ### This can fail if the filename cannot be represented in the current
* ### locale's encoding. */
SVN_ERR(svn_utf_cstring_to_utf8(&utf8_path,
line,
scratch_pool));
/* Canonicalize the path name. */
canon_path = svn_dirent_canonicalize(utf8_path, scratch_pool);
*file_name = apr_pstrdup(result_pool, canon_path);
return SVN_NO_ERROR;
}
/* Parse the '--- ' line of a regular unidiff. */
static svn_error_t *
diff_minus(enum parse_state *new_state, char *line, svn_patch_t *patch,
apr_pool_t *result_pool, apr_pool_t *scratch_pool)
{
/* If we can find a tab, it separates the filename from
* the rest of the line which we can discard. */
char *tab = strchr(line, '\t');
if (tab)
*tab = '\0';
SVN_ERR(grab_filename(&patch->old_filename, line + STRLEN_LITERAL("--- "),
result_pool, scratch_pool));
*new_state = state_minus_seen;
return SVN_NO_ERROR;
}
/* Parse the '+++ ' line of a regular unidiff. */
static svn_error_t *
diff_plus(enum parse_state *new_state, char *line, svn_patch_t *patch,
apr_pool_t *result_pool, apr_pool_t *scratch_pool)
{
/* If we can find a tab, it separates the filename from
* the rest of the line which we can discard. */
char *tab = strchr(line, '\t');
if (tab)
*tab = '\0';
SVN_ERR(grab_filename(&patch->new_filename, line + STRLEN_LITERAL("+++ "),
result_pool, scratch_pool));
*new_state = state_unidiff_found;
return SVN_NO_ERROR;
}
/* Parse the first line of a git extended unidiff. */
static svn_error_t *
git_start(enum parse_state *new_state, char *line, svn_patch_t *patch,
apr_pool_t *result_pool, apr_pool_t *scratch_pool)
{
const char *old_path_start;
char *old_path_end;
const char *new_path_start;
const char *new_path_end;
char *new_path_marker;
const char *old_path_marker;
/* ### Add handling of escaped paths
* http://www.kernel.org/pub/software/scm/git/docs/git-diff.html:
*
* TAB, LF, double quote and backslash characters in pathnames are
* represented as \t, \n, \" and \\, respectively. If there is need for
* such substitution then the whole pathname is put in double quotes.
*/
/* Our line should look like this: 'diff --git a/path b/path'.
*
* If we find any deviations from that format, we return with state reset
* to start.
*/
old_path_marker = strstr(line, " a/");
if (! old_path_marker)
{
*new_state = state_start;
return SVN_NO_ERROR;
}
if (! *(old_path_marker + 3))
{
*new_state = state_start;
return SVN_NO_ERROR;
}
new_path_marker = strstr(old_path_marker, " b/");
if (! new_path_marker)
{
*new_state = state_start;
return SVN_NO_ERROR;
}
if (! *(new_path_marker + 3))
{
*new_state = state_start;
return SVN_NO_ERROR;
}
/* By now, we know that we have a line on the form '--git diff a/.+ b/.+'
* We only need the filenames when we have deleted or added empty
* files. In those cases the old_path and new_path is identical on the
* 'diff --git' line. For all other cases we fetch the filenames from
* other header lines. */
old_path_start = line + STRLEN_LITERAL("diff --git a/");
new_path_end = line + strlen(line);
new_path_start = old_path_start;
while (TRUE)
{
ptrdiff_t len_old;
ptrdiff_t len_new;
new_path_marker = strstr(new_path_start, " b/");
/* No new path marker, bail out. */
if (! new_path_marker)
break;
old_path_end = new_path_marker;
new_path_start = new_path_marker + STRLEN_LITERAL(" b/");
/* No path after the marker. */
if (! *new_path_start)
break;
len_old = old_path_end - old_path_start;
len_new = new_path_end - new_path_start;
/* Are the paths before and after the " b/" marker the same? */
if (len_old == len_new
&& ! strncmp(old_path_start, new_path_start, len_old))
{
*old_path_end = '\0';
SVN_ERR(grab_filename(&patch->old_filename, old_path_start,
result_pool, scratch_pool));
SVN_ERR(grab_filename(&patch->new_filename, new_path_start,
result_pool, scratch_pool));
break;
}
}
/* We assume that the path is only modified until we've found a 'tree'
* header */
patch->operation = svn_diff_op_modified;
*new_state = state_git_diff_seen;
return SVN_NO_ERROR;
}
/* Parse the '--- ' line of a git extended unidiff. */
static svn_error_t *
git_minus(enum parse_state *new_state, char *line, svn_patch_t *patch,
apr_pool_t *result_pool, apr_pool_t *scratch_pool)
{
/* If we can find a tab, it separates the filename from
* the rest of the line which we can discard. */
char *tab = strchr(line, '\t');
if (tab)
*tab = '\0';
if (starts_with(line, "--- /dev/null"))
SVN_ERR(grab_filename(&patch->old_filename, "/dev/null",
result_pool, scratch_pool));
else
SVN_ERR(grab_filename(&patch->old_filename, line + STRLEN_LITERAL("--- a/"),
result_pool, scratch_pool));
*new_state = state_git_minus_seen;
return SVN_NO_ERROR;
}
/* Parse the '+++ ' line of a git extended unidiff. */
static svn_error_t *
git_plus(enum parse_state *new_state, char *line, svn_patch_t *patch,
apr_pool_t *result_pool, apr_pool_t *scratch_pool)
{
/* If we can find a tab, it separates the filename from
* the rest of the line which we can discard. */
char *tab = strchr(line, '\t');
if (tab)
*tab = '\0';
if (starts_with(line, "+++ /dev/null"))
SVN_ERR(grab_filename(&patch->new_filename, "/dev/null",
result_pool, scratch_pool));
else
SVN_ERR(grab_filename(&patch->new_filename, line + STRLEN_LITERAL("+++ b/"),
result_pool, scratch_pool));
*new_state = state_git_header_found;
return SVN_NO_ERROR;
}
/* Parse the 'rename from ' line of a git extended unidiff. */
static svn_error_t *
git_move_from(enum parse_state *new_state, char *line, svn_patch_t *patch,
apr_pool_t *result_pool, apr_pool_t *scratch_pool)
{
SVN_ERR(grab_filename(&patch->old_filename,
line + STRLEN_LITERAL("rename from "),
result_pool, scratch_pool));
*new_state = state_move_from_seen;
return SVN_NO_ERROR;
}
/* Parse the 'rename to ' line of a git extended unidiff. */
static svn_error_t *
git_move_to(enum parse_state *new_state, char *line, svn_patch_t *patch,
apr_pool_t *result_pool, apr_pool_t *scratch_pool)
{
SVN_ERR(grab_filename(&patch->new_filename,
line + STRLEN_LITERAL("rename to "),
result_pool, scratch_pool));
patch->operation = svn_diff_op_moved;
*new_state = state_git_tree_seen;
return SVN_NO_ERROR;
}
/* Parse the 'copy from ' line of a git extended unidiff. */
static svn_error_t *
git_copy_from(enum parse_state *new_state, char *line, svn_patch_t *patch,
apr_pool_t *result_pool, apr_pool_t *scratch_pool)
{
SVN_ERR(grab_filename(&patch->old_filename,
line + STRLEN_LITERAL("copy from "),
result_pool, scratch_pool));
*new_state = state_copy_from_seen;
return SVN_NO_ERROR;
}
/* Parse the 'copy to ' line of a git extended unidiff. */
static svn_error_t *
git_copy_to(enum parse_state *new_state, char *line, svn_patch_t *patch,
apr_pool_t *result_pool, apr_pool_t *scratch_pool)
{
SVN_ERR(grab_filename(&patch->new_filename, line + STRLEN_LITERAL("copy to "),
result_pool, scratch_pool));
patch->operation = svn_diff_op_copied;
*new_state = state_git_tree_seen;
return SVN_NO_ERROR;
}
/* Parse the 'new file ' line of a git extended unidiff. */
static svn_error_t *
git_new_file(enum parse_state *new_state, char *line, svn_patch_t *patch,
apr_pool_t *result_pool, apr_pool_t *scratch_pool)
{
patch->operation = svn_diff_op_added;
/* Filename already retrieved from diff --git header. */
*new_state = state_git_tree_seen;
return SVN_NO_ERROR;
}
/* Parse the 'deleted file ' line of a git extended unidiff. */
static svn_error_t *
git_deleted_file(enum parse_state *new_state, char *line, svn_patch_t *patch,
apr_pool_t *result_pool, apr_pool_t *scratch_pool)
{
patch->operation = svn_diff_op_deleted;
/* Filename already retrieved from diff --git header. */
*new_state = state_git_tree_seen;
return SVN_NO_ERROR;
}
/* Add a HUNK associated with the property PROP_NAME to PATCH. */
static svn_error_t *
add_property_hunk(svn_patch_t *patch, const char *prop_name,
svn_diff_hunk_t *hunk, svn_diff_operation_kind_t operation,
apr_pool_t *result_pool)
{
svn_prop_patch_t *prop_patch;
prop_patch = svn_hash_gets(patch->prop_patches, prop_name);
if (! prop_patch)
{
prop_patch = apr_palloc(result_pool, sizeof(svn_prop_patch_t));
prop_patch->name = prop_name;
prop_patch->operation = operation;
prop_patch->hunks = apr_array_make(result_pool, 1,
sizeof(svn_diff_hunk_t *));
svn_hash_sets(patch->prop_patches, prop_name, prop_patch);
}
APR_ARRAY_PUSH(prop_patch->hunks, svn_diff_hunk_t *) = hunk;
return SVN_NO_ERROR;
}
struct svn_patch_file_t
{
/* The APR file handle to the patch file. */
apr_file_t *apr_file;
/* The file offset at which the next patch is expected. */
apr_off_t next_patch_offset;
};
svn_error_t *
svn_diff_open_patch_file(svn_patch_file_t **patch_file,
const char *local_abspath,
apr_pool_t *result_pool)
{
svn_patch_file_t *p;
p = apr_palloc(result_pool, sizeof(*p));
SVN_ERR(svn_io_file_open(&p->apr_file, local_abspath,
APR_READ | APR_BUFFERED, APR_OS_DEFAULT,
result_pool));
p->next_patch_offset = 0;
*patch_file = p;
return SVN_NO_ERROR;
}
/* Parse hunks from APR_FILE and store them in PATCH->HUNKS.
* Parsing stops if no valid next hunk can be found.
* If IGNORE_WHITESPACE is TRUE, lines without
* leading spaces will be treated as context lines.
* Allocate results in RESULT_POOL.
* Use SCRATCH_POOL for temporary allocations. */
static svn_error_t *
parse_hunks(svn_patch_t *patch, apr_file_t *apr_file,
svn_boolean_t ignore_whitespace,
apr_pool_t *result_pool, apr_pool_t *scratch_pool)
{
svn_diff_hunk_t *hunk;
svn_boolean_t is_property;
const char *last_prop_name;
const char *prop_name;
svn_diff_operation_kind_t prop_operation;
apr_pool_t *iterpool;
last_prop_name = NULL;
patch->hunks = apr_array_make(result_pool, 10, sizeof(svn_diff_hunk_t *));
patch->prop_patches = apr_hash_make(result_pool);
iterpool = svn_pool_create(scratch_pool);
do
{
svn_pool_clear(iterpool);
SVN_ERR(parse_next_hunk(&hunk, &is_property, &prop_name, &prop_operation,
patch, apr_file, ignore_whitespace, result_pool,
iterpool));
if (hunk && is_property)
{
if (! prop_name)
prop_name = last_prop_name;
else
last_prop_name = prop_name;
SVN_ERR(add_property_hunk(patch, prop_name, hunk, prop_operation,
result_pool));
}
else if (hunk)
{
APR_ARRAY_PUSH(patch->hunks, svn_diff_hunk_t *) = hunk;
last_prop_name = NULL;
}
}
while (hunk);
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* State machine for the diff header parser.
* Expected Input Required state Function to call */
static struct transition transitions[] =
{
{"--- ", state_start, diff_minus},
{"+++ ", state_minus_seen, diff_plus},
{"diff --git", state_start, git_start},
{"--- a/", state_git_diff_seen, git_minus},
{"--- a/", state_git_tree_seen, git_minus},
{"--- /dev/null", state_git_tree_seen, git_minus},
{"+++ b/", state_git_minus_seen, git_plus},
{"+++ /dev/null", state_git_minus_seen, git_plus},
{"rename from ", state_git_diff_seen, git_move_from},
{"rename to ", state_move_from_seen, git_move_to},
{"copy from ", state_git_diff_seen, git_copy_from},
{"copy to ", state_copy_from_seen, git_copy_to},
{"new file ", state_git_diff_seen, git_new_file},
{"deleted file ", state_git_diff_seen, git_deleted_file},
};
svn_error_t *
svn_diff_parse_next_patch(svn_patch_t **patch,
svn_patch_file_t *patch_file,
svn_boolean_t reverse,
svn_boolean_t ignore_whitespace,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_off_t pos, last_line;
svn_boolean_t eof;
svn_boolean_t line_after_tree_header_read = FALSE;
apr_pool_t *iterpool;
enum parse_state state = state_start;
if (apr_file_eof(patch_file->apr_file) == APR_EOF)
{
/* No more patches here. */
*patch = NULL;
return SVN_NO_ERROR;
}
*patch = apr_pcalloc(result_pool, sizeof(**patch));
pos = patch_file->next_patch_offset;
SVN_ERR(svn_io_file_seek(patch_file->apr_file, APR_SET, &pos, scratch_pool));
iterpool = svn_pool_create(scratch_pool);
do
{
svn_stringbuf_t *line;
svn_boolean_t valid_header_line = FALSE;
int i;
svn_pool_clear(iterpool);
/* Remember the current line's offset, and read the line. */
last_line = pos;
SVN_ERR(svn_io_file_readline(patch_file->apr_file, &line, NULL, &eof,
APR_SIZE_MAX, iterpool, iterpool));
if (! eof)
{
/* Update line offset for next iteration. */
pos = 0;
SVN_ERR(svn_io_file_seek(patch_file->apr_file, APR_CUR, &pos,
iterpool));
}
/* Run the state machine. */
for (i = 0; i < (sizeof(transitions) / sizeof(transitions[0])); i++)
{
if (starts_with(line->data, transitions[i].expected_input)
&& state == transitions[i].required_state)
{
SVN_ERR(transitions[i].fn(&state, line->data, *patch,
result_pool, iterpool));
valid_header_line = TRUE;
break;
}
}
if (state == state_unidiff_found || state == state_git_header_found)
{
/* We have a valid diff header, yay! */
break;
}
else if (state == state_git_tree_seen && line_after_tree_header_read)
{
/* git patches can contain an index line after the file mode line */
if (!starts_with(line->data, "index "))
{
/* We have a valid diff header for a patch with only tree changes.
* Rewind to the start of the line just read, so subsequent calls
* to this function don't end up skipping the line -- it may
* contain a patch. */
SVN_ERR(svn_io_file_seek(patch_file->apr_file, APR_SET, &last_line,
scratch_pool));
break;
}
}
else if (state == state_git_tree_seen)
{
line_after_tree_header_read = TRUE;
}
else if (! valid_header_line && state != state_start
+ && state != state_git_diff_seen
&& !starts_with(line->data, "index "))
{
/* We've encountered an invalid diff header.
*
* Rewind to the start of the line just read - it may be a new
* header that begins there. */
SVN_ERR(svn_io_file_seek(patch_file->apr_file, APR_SET, &last_line,
scratch_pool));
state = state_start;
}
}
while (! eof);
(*patch)->reverse = reverse;
if (reverse)
{
const char *temp;
temp = (*patch)->old_filename;
(*patch)->old_filename = (*patch)->new_filename;
(*patch)->new_filename = temp;
}
if ((*patch)->old_filename == NULL || (*patch)->new_filename == NULL)
{
/* Something went wrong, just discard the result. */
*patch = NULL;
}
else
SVN_ERR(parse_hunks(*patch, patch_file->apr_file, ignore_whitespace,
result_pool, iterpool));
svn_pool_destroy(iterpool);
patch_file->next_patch_offset = 0;
SVN_ERR(svn_io_file_seek(patch_file->apr_file, APR_CUR,
&patch_file->next_patch_offset, scratch_pool));
if (*patch)
{
/* Usually, hunks appear in the patch sorted by their original line
* offset. But just in case they weren't parsed in this order for
* some reason, we sort them so that our caller can assume that hunks
* are sorted as if parsed from a usual patch. */
qsort((*patch)->hunks->elts, (*patch)->hunks->nelts,
(*patch)->hunks->elt_size, compare_hunks);
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_diff_close_patch_file(svn_patch_file_t *patch_file,
apr_pool_t *scratch_pool)
{
return svn_error_trace(svn_io_file_close(patch_file->apr_file,
scratch_pool));
}
Index: vendor/subversion/dist/subversion/libsvn_diff/util.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_diff/util.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_diff/util.c (revision 286501)
@@ -1,591 +1,606 @@
/*
* util.c : routines for doing diffs
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include <apr.h>
#include <apr_general.h>
#include "svn_hash.h"
#include "svn_pools.h"
#include "svn_dirent_uri.h"
#include "svn_props.h"
#include "svn_mergeinfo.h"
#include "svn_error.h"
#include "svn_diff.h"
#include "svn_types.h"
#include "svn_ctype.h"
+#include "svn_sorts.h"
#include "svn_utf.h"
#include "svn_version.h"
#include "private/svn_diff_private.h"
#include "diff.h"
#include "svn_private_config.h"
svn_boolean_t
svn_diff_contains_conflicts(svn_diff_t *diff)
{
while (diff != NULL)
{
if (diff->type == svn_diff__type_conflict)
{
return TRUE;
}
diff = diff->next;
}
return FALSE;
}
svn_boolean_t
svn_diff_contains_diffs(svn_diff_t *diff)
{
while (diff != NULL)
{
if (diff->type != svn_diff__type_common)
{
return TRUE;
}
diff = diff->next;
}
return FALSE;
}
svn_error_t *
svn_diff_output(svn_diff_t *diff,
void *output_baton,
const svn_diff_output_fns_t *vtable)
{
svn_error_t *(*output_fn)(void *,
apr_off_t, apr_off_t,
apr_off_t, apr_off_t,
apr_off_t, apr_off_t);
while (diff != NULL)
{
switch (diff->type)
{
case svn_diff__type_common:
output_fn = vtable->output_common;
break;
case svn_diff__type_diff_common:
output_fn = vtable->output_diff_common;
break;
case svn_diff__type_diff_modified:
output_fn = vtable->output_diff_modified;
break;
case svn_diff__type_diff_latest:
output_fn = vtable->output_diff_latest;
break;
case svn_diff__type_conflict:
output_fn = NULL;
if (vtable->output_conflict != NULL)
{
SVN_ERR(vtable->output_conflict(output_baton,
diff->original_start, diff->original_length,
diff->modified_start, diff->modified_length,
diff->latest_start, diff->latest_length,
diff->resolved_diff));
}
break;
default:
output_fn = NULL;
break;
}
if (output_fn != NULL)
{
SVN_ERR(output_fn(output_baton,
diff->original_start, diff->original_length,
diff->modified_start, diff->modified_length,
diff->latest_start, diff->latest_length));
}
diff = diff->next;
}
return SVN_NO_ERROR;
}
void
svn_diff__normalize_buffer(char **tgt,
apr_off_t *lengthp,
svn_diff__normalize_state_t *statep,
const char *buf,
const svn_diff_file_options_t *opts)
{
/* Variables for looping through BUF */
const char *curp, *endp;
/* Variable to record normalizing state */
svn_diff__normalize_state_t state = *statep;
/* Variables to track what needs copying into the target buffer */
const char *start = buf;
apr_size_t include_len = 0;
svn_boolean_t last_skipped = FALSE; /* makes sure we set 'start' */
/* Variable to record the state of the target buffer */
char *tgt_newend = *tgt;
/* If this is a noop, then just get out of here. */
if (! opts->ignore_space && ! opts->ignore_eol_style)
{
*tgt = (char *)buf;
return;
}
/* It only took me forever to get this routine right,
so here my thoughts go:
Below, we loop through the data, doing 2 things:
- Normalizing
- Copying other data
The routine tries its hardest *not* to copy data, but instead
returning a pointer into already normalized existing data.
To this end, a block 'other data' shouldn't be copied when found,
but only as soon as it can't be returned in-place.
On a character level, there are 3 possible operations:
- Skip the character (don't include in the normalized data)
- Include the character (do include in the normalizad data)
- Include as another character
This is essentially the same as skipping the current character
and inserting a given character in the output data.
The macros below (SKIP, INCLUDE and INCLUDE_AS) are defined to
handle the character based operations. The macros themselves
collect character level data into blocks.
At all times designate the START, INCLUDED_LEN and CURP pointers
an included and and skipped block like this:
[ start, start + included_len ) [ start + included_len, curp )
INCLUDED EXCLUDED
When the routine flips from skipping to including, the last
included block has to be flushed to the output buffer.
*/
/* Going from including to skipping; only schedules the current
included section for flushing.
Also, simply chop off the character if it's the first in the buffer,
so we can possibly just return the remainder of the buffer */
#define SKIP \
do { \
if (start == curp) \
++start; \
last_skipped = TRUE; \
} while (0)
#define INCLUDE \
do { \
if (last_skipped) \
COPY_INCLUDED_SECTION; \
++include_len; \
last_skipped = FALSE; \
} while (0)
#define COPY_INCLUDED_SECTION \
do { \
if (include_len > 0) \
{ \
memmove(tgt_newend, start, include_len); \
tgt_newend += include_len; \
include_len = 0; \
} \
start = curp; \
} while (0)
/* Include the current character as character X.
If the current character already *is* X, add it to the
currently included region, increasing chances for consecutive
fully normalized blocks. */
#define INCLUDE_AS(x) \
do { \
if (*curp == (x)) \
INCLUDE; \
else \
{ \
INSERT((x)); \
SKIP; \
} \
} while (0)
/* Insert character X in the output buffer */
#define INSERT(x) \
do { \
COPY_INCLUDED_SECTION; \
*tgt_newend++ = (x); \
} while (0)
for (curp = buf, endp = buf + *lengthp; curp != endp; ++curp)
{
switch (*curp)
{
case '\r':
if (opts->ignore_eol_style)
INCLUDE_AS('\n');
else
INCLUDE;
state = svn_diff__normalize_state_cr;
break;
case '\n':
if (state == svn_diff__normalize_state_cr
&& opts->ignore_eol_style)
SKIP;
else
INCLUDE;
state = svn_diff__normalize_state_normal;
break;
default:
if (svn_ctype_isspace(*curp)
&& opts->ignore_space != svn_diff_file_ignore_space_none)
{
/* Whitespace but not '\r' or '\n' */
if (state != svn_diff__normalize_state_whitespace
&& opts->ignore_space
== svn_diff_file_ignore_space_change)
/*### If we can postpone insertion of the space
until the next non-whitespace character,
we have a potential of reducing the number of copies:
If this space is followed by more spaces,
this will cause a block-copy.
If the next non-space block is considered normalized
*and* preceded by a space, we can take advantage of that. */
/* Note, the above optimization applies to 90% of the source
lines in our own code, since it (generally) doesn't use
more than one space per blank section, except for the
beginning of a line. */
INCLUDE_AS(' ');
else
SKIP;
state = svn_diff__normalize_state_whitespace;
}
else
{
/* Non-whitespace character, or whitespace character in
svn_diff_file_ignore_space_none mode. */
INCLUDE;
state = svn_diff__normalize_state_normal;
}
}
}
/* If we're not in whitespace, flush the last chunk of data.
* Note that this will work correctly when this is the last chunk of the
* file:
* * If there is an eol, it will either have been output when we entered
* the state_cr, or it will be output now.
* * If there is no eol and we're not in whitespace, then we just output
* everything below.
* * If there's no eol and we are in whitespace, we want to ignore
* whitespace unconditionally. */
if (*tgt == tgt_newend)
{
/* we haven't copied any data in to *tgt and our chunk consists
only of one block of (already normalized) data.
Just return the block. */
*tgt = (char *)start;
*lengthp = include_len;
}
else
{
COPY_INCLUDED_SECTION;
*lengthp = tgt_newend - *tgt;
}
*statep = state;
#undef SKIP
#undef INCLUDE
#undef INCLUDE_AS
#undef INSERT
#undef COPY_INCLUDED_SECTION
}
svn_error_t *
svn_diff__unified_append_no_newline_msg(svn_stringbuf_t *stringbuf,
const char *header_encoding,
apr_pool_t *scratch_pool)
{
const char *out_str;
SVN_ERR(svn_utf_cstring_from_utf8_ex2(
&out_str,
APR_EOL_STR
SVN_DIFF__NO_NEWLINE_AT_END_OF_FILE APR_EOL_STR,
header_encoding, scratch_pool));
svn_stringbuf_appendcstr(stringbuf, out_str);
return SVN_NO_ERROR;
}
svn_error_t *
svn_diff__unified_write_hunk_header(svn_stream_t *output_stream,
const char *header_encoding,
const char *hunk_delimiter,
apr_off_t old_start,
apr_off_t old_length,
apr_off_t new_start,
apr_off_t new_length,
const char *hunk_extra_context,
apr_pool_t *scratch_pool)
{
SVN_ERR(svn_stream_printf_from_utf8(output_stream, header_encoding,
scratch_pool,
"%s -%" APR_OFF_T_FMT,
hunk_delimiter, old_start));
/* If the hunk length is 1, suppress the number of lines in the hunk
* (it is 1 implicitly) */
if (old_length != 1)
{
SVN_ERR(svn_stream_printf_from_utf8(output_stream, header_encoding,
scratch_pool,
",%" APR_OFF_T_FMT, old_length));
}
SVN_ERR(svn_stream_printf_from_utf8(output_stream, header_encoding,
scratch_pool,
" +%" APR_OFF_T_FMT, new_start));
if (new_length != 1)
{
SVN_ERR(svn_stream_printf_from_utf8(output_stream, header_encoding,
scratch_pool,
",%" APR_OFF_T_FMT, new_length));
}
if (hunk_extra_context == NULL)
hunk_extra_context = "";
SVN_ERR(svn_stream_printf_from_utf8(output_stream, header_encoding,
scratch_pool,
" %s%s%s" APR_EOL_STR,
hunk_delimiter,
hunk_extra_context[0] ? " " : "",
hunk_extra_context));
return SVN_NO_ERROR;
}
svn_error_t *
svn_diff__unidiff_write_header(svn_stream_t *output_stream,
const char *header_encoding,
const char *old_header,
const char *new_header,
apr_pool_t *scratch_pool)
{
SVN_ERR(svn_stream_printf_from_utf8(output_stream, header_encoding,
scratch_pool,
"--- %s" APR_EOL_STR
"+++ %s" APR_EOL_STR,
old_header,
new_header));
return SVN_NO_ERROR;
}
/* A helper function for display_prop_diffs. Output the differences between
the mergeinfo stored in ORIG_MERGEINFO_VAL and NEW_MERGEINFO_VAL in a
human-readable form to OUTSTREAM, using ENCODING. Use POOL for temporary
allocations. */
static svn_error_t *
display_mergeinfo_diff(const char *old_mergeinfo_val,
const char *new_mergeinfo_val,
const char *encoding,
svn_stream_t *outstream,
apr_pool_t *pool)
{
apr_hash_t *old_mergeinfo_hash, *new_mergeinfo_hash, *added, *deleted;
apr_pool_t *iterpool = svn_pool_create(pool);
apr_hash_index_t *hi;
if (old_mergeinfo_val)
SVN_ERR(svn_mergeinfo_parse(&old_mergeinfo_hash, old_mergeinfo_val, pool));
else
old_mergeinfo_hash = NULL;
if (new_mergeinfo_val)
SVN_ERR(svn_mergeinfo_parse(&new_mergeinfo_hash, new_mergeinfo_val, pool));
else
new_mergeinfo_hash = NULL;
SVN_ERR(svn_mergeinfo_diff2(&deleted, &added, old_mergeinfo_hash,
new_mergeinfo_hash,
TRUE, pool, pool));
for (hi = apr_hash_first(pool, deleted);
hi; hi = apr_hash_next(hi))
{
const char *from_path = svn__apr_hash_index_key(hi);
svn_rangelist_t *merge_revarray = svn__apr_hash_index_val(hi);
svn_string_t *merge_revstr;
svn_pool_clear(iterpool);
SVN_ERR(svn_rangelist_to_string(&merge_revstr, merge_revarray,
iterpool));
SVN_ERR(svn_stream_printf_from_utf8(outstream, encoding, iterpool,
_(" Reverse-merged %s:r%s%s"),
from_path, merge_revstr->data,
APR_EOL_STR));
}
for (hi = apr_hash_first(pool, added);
hi; hi = apr_hash_next(hi))
{
const char *from_path = svn__apr_hash_index_key(hi);
svn_rangelist_t *merge_revarray = svn__apr_hash_index_val(hi);
svn_string_t *merge_revstr;
svn_pool_clear(iterpool);
SVN_ERR(svn_rangelist_to_string(&merge_revstr, merge_revarray,
iterpool));
SVN_ERR(svn_stream_printf_from_utf8(outstream, encoding, iterpool,
_(" Merged %s:r%s%s"),
from_path, merge_revstr->data,
APR_EOL_STR));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
+/* qsort callback handling svn_prop_t by name */
+static int
+propchange_sort(const void *k1, const void *k2)
+{
+ const svn_prop_t *propchange1 = k1;
+ const svn_prop_t *propchange2 = k2;
+
+ return strcmp(propchange1->name, propchange2->name);
+}
+
svn_error_t *
svn_diff__display_prop_diffs(svn_stream_t *outstream,
const char *encoding,
const apr_array_header_t *propchanges,
apr_hash_t *original_props,
svn_boolean_t pretty_print_mergeinfo,
- apr_pool_t *pool)
+ apr_pool_t *scratch_pool)
{
+ apr_pool_t *pool = scratch_pool;
apr_pool_t *iterpool = svn_pool_create(pool);
+ apr_array_header_t *changes = apr_array_copy(scratch_pool, propchanges);
int i;
- for (i = 0; i < propchanges->nelts; i++)
+ qsort(changes->elts, changes->nelts, changes->elt_size, propchange_sort);
+
+ for (i = 0; i < changes->nelts; i++)
{
const char *action;
const svn_string_t *original_value;
const svn_prop_t *propchange
- = &APR_ARRAY_IDX(propchanges, i, svn_prop_t);
+ = &APR_ARRAY_IDX(changes, i, svn_prop_t);
if (original_props)
original_value = svn_hash_gets(original_props, propchange->name);
else
original_value = NULL;
/* If the property doesn't exist on either side, or if it exists
with the same value, skip it. This can happen if the client is
hitting an old mod_dav_svn server that doesn't understand the
"send-all" REPORT style. */
if ((! (original_value || propchange->value))
|| (original_value && propchange->value
&& svn_string_compare(original_value, propchange->value)))
continue;
svn_pool_clear(iterpool);
if (! original_value)
action = "Added";
else if (! propchange->value)
action = "Deleted";
else
action = "Modified";
SVN_ERR(svn_stream_printf_from_utf8(outstream, encoding, iterpool,
"%s: %s%s", action,
propchange->name, APR_EOL_STR));
if (pretty_print_mergeinfo
&& strcmp(propchange->name, SVN_PROP_MERGEINFO) == 0)
{
const char *orig = original_value ? original_value->data : NULL;
const char *val = propchange->value ? propchange->value->data : NULL;
svn_error_t *err = display_mergeinfo_diff(orig, val, encoding,
outstream, iterpool);
/* Issue #3896: If we can't pretty-print mergeinfo differences
because invalid mergeinfo is present, then don't let the diff
fail, just print the diff as any other property. */
if (err && err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
{
svn_error_clear(err);
}
else
{
SVN_ERR(err);
continue;
}
}
{
svn_diff_t *diff;
svn_diff_file_options_t options = { 0 };
const svn_string_t *orig
= original_value ? original_value
: svn_string_create_empty(iterpool);
const svn_string_t *val
= propchange->value ? propchange->value
: svn_string_create_empty(iterpool);
SVN_ERR(svn_diff_mem_string_diff(&diff, orig, val, &options,
iterpool));
/* UNIX patch will try to apply a diff even if the diff header
* is missing. It tries to be helpful by asking the user for a
* target filename when it can't determine the target filename
* from the diff header. But there usually are no files which
* UNIX patch could apply the property diff to, so we use "##"
* instead of "@@" as the default hunk delimiter for property diffs.
* We also supress the diff header. */
SVN_ERR(svn_diff_mem_string_output_unified2(
outstream, diff, FALSE /* no header */, "##", NULL, NULL,
encoding, orig, val, iterpool));
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Return the library version number. */
const svn_version_t *
svn_diff_version(void)
{
SVN_VERSION_BODY;
}
Index: vendor/subversion/dist/subversion/libsvn_fs_fs/caching.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_fs_fs/caching.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_fs_fs/caching.c (revision 286501)
@@ -1,692 +1,678 @@
/* caching.c : in-memory caching
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include "fs.h"
#include "fs_fs.h"
#include "id.h"
#include "dag.h"
#include "tree.h"
#include "temp_serializer.h"
#include "../libsvn_fs/fs-loader.h"
#include "svn_config.h"
#include "svn_cache_config.h"
#include "svn_private_config.h"
#include "svn_hash.h"
#include "svn_pools.h"
#include "private/svn_debug.h"
#include "private/svn_subr_private.h"
/* Take the ORIGINAL string and replace all occurrences of ":" without
* limiting the key space. Allocate the result in POOL.
*/
static const char *
normalize_key_part(const char *original,
apr_pool_t *pool)
{
apr_size_t i;
apr_size_t len = strlen(original);
svn_stringbuf_t *normalized = svn_stringbuf_create_ensure(len, pool);
for (i = 0; i < len; ++i)
{
char c = original[i];
switch (c)
{
case ':': svn_stringbuf_appendbytes(normalized, "%_", 2);
break;
case '%': svn_stringbuf_appendbytes(normalized, "%%", 2);
break;
default : svn_stringbuf_appendbyte(normalized, c);
}
}
return normalized->data;
}
/* Return a memcache in *MEMCACHE_P for FS if it's configured to use
memcached, or NULL otherwise. Also, sets *FAIL_STOP to a boolean
indicating whether cache errors should be returned to the caller or
just passed to the FS warning handler.
*CACHE_TXDELTAS, *CACHE_FULLTEXTS and *CACHE_REVPROPS flags will be set
according to FS->CONFIG. *CACHE_NAMESPACE receives the cache prefix
to use.
Use FS->pool for allocating the memcache and CACHE_NAMESPACE, and POOL
for temporary allocations. */
static svn_error_t *
read_config(svn_memcache_t **memcache_p,
svn_boolean_t *fail_stop,
const char **cache_namespace,
svn_boolean_t *cache_txdeltas,
svn_boolean_t *cache_fulltexts,
svn_boolean_t *cache_revprops,
svn_fs_t *fs,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
SVN_ERR(svn_cache__make_memcache_from_config(memcache_p, ffd->config,
- fs->pool));
+ fs->pool));
/* No cache namespace by default. I.e. all FS instances share the
* cached data. If you specify different namespaces, the data will
* share / compete for the same cache memory but keys will not match
* across namespaces and, thus, cached data will not be shared between
* namespaces.
*
* Since the namespace will be concatenated with other elements to form
* the complete key prefix, we must make sure that the resulting string
* is unique and cannot be created by any other combination of elements.
*/
*cache_namespace
= normalize_key_part(svn_hash__get_cstring(fs->config,
SVN_FS_CONFIG_FSFS_CACHE_NS,
""),
pool);
/* don't cache text deltas by default.
* Once we reconstructed the fulltexts from the deltas,
* these deltas are rarely re-used. Therefore, only tools
* like svnadmin will activate this to speed up operations
* dump and verify.
*/
*cache_txdeltas
= svn_hash__get_bool(fs->config,
SVN_FS_CONFIG_FSFS_CACHE_DELTAS,
FALSE);
/* by default, cache fulltexts.
* Most SVN tools care about reconstructed file content.
* Thus, this is a reasonable default.
* SVN admin tools may set that to FALSE because fulltexts
* won't be re-used rendering the cache less effective
* by squeezing wanted data out.
*/
*cache_fulltexts
= svn_hash__get_bool(fs->config,
SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS,
TRUE);
- /* don't cache revprops by default.
- * Revprop caching significantly speeds up operations like
- * svn ls -v. However, it requires synchronization that may
- * not be available or efficient in the current server setup.
- *
- * If the caller chose option "2", enable revprop caching if
- * the required API support is there to make it efficient.
+ /* For now, always disable revprop caching.
*/
- if (strcmp(svn_hash__get_cstring(fs->config,
- SVN_FS_CONFIG_FSFS_CACHE_REVPROPS,
- ""), "2"))
- *cache_revprops
- = svn_hash__get_bool(fs->config,
- SVN_FS_CONFIG_FSFS_CACHE_REVPROPS,
- FALSE);
- else
- *cache_revprops = svn_named_atomic__is_efficient();
+ *cache_revprops = FALSE;
return svn_config_get_bool(ffd->config, fail_stop,
CONFIG_SECTION_CACHES, CONFIG_OPTION_FAIL_STOP,
FALSE);
}
/* Implements svn_cache__error_handler_t
* This variant clears the error after logging it.
*/
static svn_error_t *
warn_and_continue_on_cache_errors(svn_error_t *err,
void *baton,
apr_pool_t *pool)
{
svn_fs_t *fs = baton;
(fs->warning)(fs->warning_baton, err);
svn_error_clear(err);
return SVN_NO_ERROR;
}
/* Implements svn_cache__error_handler_t
* This variant logs the error and passes it on to the callers.
*/
static svn_error_t *
warn_and_fail_on_cache_errors(svn_error_t *err,
void *baton,
apr_pool_t *pool)
{
svn_fs_t *fs = baton;
(fs->warning)(fs->warning_baton, err);
return err;
}
#ifdef SVN_DEBUG_CACHE_DUMP_STATS
/* Baton to be used for the dump_cache_statistics() pool cleanup function, */
struct dump_cache_baton_t
{
/* the pool about to be cleaned up. Will be used for temp. allocations. */
apr_pool_t *pool;
/* the cache to dump the statistics for */
svn_cache__t *cache;
};
/* APR pool cleanup handler that will printf the statistics of the
cache referenced by the baton in BATON_VOID. */
static apr_status_t
dump_cache_statistics(void *baton_void)
{
struct dump_cache_baton_t *baton = baton_void;
apr_status_t result = APR_SUCCESS;
svn_cache__info_t info;
svn_string_t *text_stats;
apr_array_header_t *lines;
int i;
svn_error_t *err = svn_cache__get_info(baton->cache,
&info,
TRUE,
baton->pool);
if (! err)
{
text_stats = svn_cache__format_info(&info, baton->pool);
lines = svn_cstring_split(text_stats->data, "\n", FALSE, baton->pool);
for (i = 0; i < lines->nelts; ++i)
{
const char *line = APR_ARRAY_IDX(lines, i, const char *);
#ifdef SVN_DEBUG
SVN_DBG(("%s\n", line));
#endif
}
}
/* process error returns */
if (err)
{
result = err->apr_err;
svn_error_clear(err);
}
return result;
}
#endif /* SVN_DEBUG_CACHE_DUMP_STATS */
/* This function sets / registers the required callbacks for a given
* not transaction-specific CACHE object in FS, if CACHE is not NULL.
*
* All these svn_cache__t instances shall be handled uniformly. Unless
* ERROR_HANDLER is NULL, register it for the given CACHE in FS.
*/
static svn_error_t *
init_callbacks(svn_cache__t *cache,
svn_fs_t *fs,
svn_cache__error_handler_t error_handler,
apr_pool_t *pool)
{
if (cache != NULL)
{
#ifdef SVN_DEBUG_CACHE_DUMP_STATS
/* schedule printing the access statistics upon pool cleanup,
* i.e. end of FSFS session.
*/
struct dump_cache_baton_t *baton;
baton = apr_palloc(pool, sizeof(*baton));
baton->pool = pool;
baton->cache = cache;
apr_pool_cleanup_register(pool,
baton,
dump_cache_statistics,
apr_pool_cleanup_null);
#endif
if (error_handler)
SVN_ERR(svn_cache__set_error_handler(cache,
error_handler,
fs,
pool));
}
return SVN_NO_ERROR;
}
/* Sets *CACHE_P to cache instance based on provided options.
* Creates memcache if MEMCACHE is not NULL. Creates membuffer cache if
* MEMBUFFER is not NULL. Fallbacks to inprocess cache if MEMCACHE and
* MEMBUFFER are NULL and pages is non-zero. Sets *CACHE_P to NULL
* otherwise.
*
* Unless NO_HANDLER is true, register an error handler that reports errors
* as warnings to the FS warning callback.
*
* Cache is allocated in POOL.
* */
static svn_error_t *
create_cache(svn_cache__t **cache_p,
svn_memcache_t *memcache,
svn_membuffer_t *membuffer,
apr_int64_t pages,
apr_int64_t items_per_page,
svn_cache__serialize_func_t serializer,
svn_cache__deserialize_func_t deserializer,
apr_ssize_t klen,
const char *prefix,
svn_fs_t *fs,
svn_boolean_t no_handler,
apr_pool_t *pool)
{
svn_cache__error_handler_t error_handler = no_handler
? NULL
: warn_and_fail_on_cache_errors;
if (memcache)
{
SVN_ERR(svn_cache__create_memcache(cache_p, memcache,
serializer, deserializer, klen,
prefix, pool));
error_handler = no_handler
? NULL
: warn_and_continue_on_cache_errors;
}
else if (membuffer)
{
SVN_ERR(svn_cache__create_membuffer_cache(
cache_p, membuffer, serializer, deserializer,
klen, prefix, FALSE, pool));
}
else if (pages)
{
SVN_ERR(svn_cache__create_inprocess(
cache_p, serializer, deserializer, klen, pages,
items_per_page, FALSE, prefix, pool));
}
else
{
*cache_p = NULL;
}
SVN_ERR(init_callbacks(*cache_p, fs, error_handler, pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__initialize_caches(svn_fs_t *fs,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
const char *prefix = apr_pstrcat(pool,
"fsfs:", fs->uuid,
"/", normalize_key_part(fs->path, pool),
":",
(char *)NULL);
svn_memcache_t *memcache;
svn_membuffer_t *membuffer;
svn_boolean_t no_handler;
svn_boolean_t cache_txdeltas;
svn_boolean_t cache_fulltexts;
svn_boolean_t cache_revprops;
const char *cache_namespace;
/* Evaluating the cache configuration. */
SVN_ERR(read_config(&memcache,
&no_handler,
&cache_namespace,
&cache_txdeltas,
&cache_fulltexts,
&cache_revprops,
fs,
pool));
prefix = apr_pstrcat(pool, "ns:", cache_namespace, ":", prefix, NULL);
membuffer = svn_cache__get_global_membuffer_cache();
/* Make the cache for revision roots. For the vast majority of
* commands, this is only going to contain a few entries (svnadmin
* dump/verify is an exception here), so to reduce overhead let's
* try to keep it to just one page. I estimate each entry has about
* 72 bytes of overhead (svn_revnum_t key, svn_fs_id_t +
* id_private_t + 3 strings for value, and the cache_entry); the
* default pool size is 8192, so about a hundred should fit
* comfortably. */
SVN_ERR(create_cache(&(ffd->rev_root_id_cache),
NULL,
membuffer,
1, 100,
svn_fs_fs__serialize_id,
svn_fs_fs__deserialize_id,
sizeof(svn_revnum_t),
apr_pstrcat(pool, prefix, "RRI", (char *)NULL),
fs,
no_handler,
fs->pool));
/* Rough estimate: revision DAG nodes have size around 320 bytes, so
* let's put 16 on a page. */
SVN_ERR(create_cache(&(ffd->rev_node_cache),
NULL,
membuffer,
1024, 16,
svn_fs_fs__dag_serialize,
svn_fs_fs__dag_deserialize,
APR_HASH_KEY_STRING,
apr_pstrcat(pool, prefix, "DAG", (char *)NULL),
fs,
no_handler,
fs->pool));
/* 1st level DAG node cache */
ffd->dag_node_cache = svn_fs_fs__create_dag_cache(pool);
/* Very rough estimate: 1K per directory. */
SVN_ERR(create_cache(&(ffd->dir_cache),
NULL,
membuffer,
1024, 8,
svn_fs_fs__serialize_dir_entries,
svn_fs_fs__deserialize_dir_entries,
APR_HASH_KEY_STRING,
apr_pstrcat(pool, prefix, "DIR", (char *)NULL),
fs,
no_handler,
fs->pool));
/* Only 16 bytes per entry (a revision number + the corresponding offset).
Since we want ~8k pages, that means 512 entries per page. */
SVN_ERR(create_cache(&(ffd->packed_offset_cache),
NULL,
membuffer,
32, 1,
svn_fs_fs__serialize_manifest,
svn_fs_fs__deserialize_manifest,
sizeof(svn_revnum_t),
apr_pstrcat(pool, prefix, "PACK-MANIFEST",
(char *)NULL),
fs,
no_handler,
fs->pool));
/* initialize node revision cache, if caching has been enabled */
SVN_ERR(create_cache(&(ffd->node_revision_cache),
NULL,
membuffer,
0, 0, /* Do not use inprocess cache */
svn_fs_fs__serialize_node_revision,
svn_fs_fs__deserialize_node_revision,
sizeof(pair_cache_key_t),
apr_pstrcat(pool, prefix, "NODEREVS", (char *)NULL),
fs,
no_handler,
fs->pool));
/* initialize node change list cache, if caching has been enabled */
SVN_ERR(create_cache(&(ffd->changes_cache),
NULL,
membuffer,
0, 0, /* Do not use inprocess cache */
svn_fs_fs__serialize_changes,
svn_fs_fs__deserialize_changes,
sizeof(svn_revnum_t),
apr_pstrcat(pool, prefix, "CHANGES", (char *)NULL),
fs,
no_handler,
fs->pool));
/* if enabled, cache fulltext and other derived information */
if (cache_fulltexts)
{
SVN_ERR(create_cache(&(ffd->fulltext_cache),
memcache,
membuffer,
0, 0, /* Do not use inprocess cache */
/* Values are svn_stringbuf_t */
NULL, NULL,
sizeof(pair_cache_key_t),
apr_pstrcat(pool, prefix, "TEXT", (char *)NULL),
fs,
no_handler,
fs->pool));
SVN_ERR(create_cache(&(ffd->properties_cache),
NULL,
membuffer,
0, 0, /* Do not use inprocess cache */
svn_fs_fs__serialize_properties,
svn_fs_fs__deserialize_properties,
sizeof(pair_cache_key_t),
apr_pstrcat(pool, prefix, "PROP",
(char *)NULL),
fs,
no_handler,
fs->pool));
SVN_ERR(create_cache(&(ffd->mergeinfo_cache),
NULL,
membuffer,
0, 0, /* Do not use inprocess cache */
svn_fs_fs__serialize_mergeinfo,
svn_fs_fs__deserialize_mergeinfo,
APR_HASH_KEY_STRING,
apr_pstrcat(pool, prefix, "MERGEINFO",
(char *)NULL),
fs,
no_handler,
fs->pool));
SVN_ERR(create_cache(&(ffd->mergeinfo_existence_cache),
NULL,
membuffer,
0, 0, /* Do not use inprocess cache */
/* Values are svn_stringbuf_t */
NULL, NULL,
APR_HASH_KEY_STRING,
apr_pstrcat(pool, prefix, "HAS_MERGEINFO",
(char *)NULL),
fs,
no_handler,
fs->pool));
}
else
{
ffd->fulltext_cache = NULL;
ffd->properties_cache = NULL;
ffd->mergeinfo_cache = NULL;
ffd->mergeinfo_existence_cache = NULL;
}
/* initialize revprop cache, if full-text caching has been enabled */
if (cache_revprops)
{
SVN_ERR(create_cache(&(ffd->revprop_cache),
NULL,
membuffer,
0, 0, /* Do not use inprocess cache */
svn_fs_fs__serialize_properties,
svn_fs_fs__deserialize_properties,
sizeof(pair_cache_key_t),
apr_pstrcat(pool, prefix, "REVPROP",
(char *)NULL),
fs,
no_handler,
fs->pool));
}
else
{
ffd->revprop_cache = NULL;
}
/* if enabled, cache text deltas and their combinations */
if (cache_txdeltas)
{
SVN_ERR(create_cache(&(ffd->txdelta_window_cache),
NULL,
membuffer,
0, 0, /* Do not use inprocess cache */
svn_fs_fs__serialize_txdelta_window,
svn_fs_fs__deserialize_txdelta_window,
APR_HASH_KEY_STRING,
apr_pstrcat(pool, prefix, "TXDELTA_WINDOW",
(char *)NULL),
fs,
no_handler,
fs->pool));
SVN_ERR(create_cache(&(ffd->combined_window_cache),
NULL,
membuffer,
0, 0, /* Do not use inprocess cache */
/* Values are svn_stringbuf_t */
NULL, NULL,
APR_HASH_KEY_STRING,
apr_pstrcat(pool, prefix, "COMBINED_WINDOW",
(char *)NULL),
fs,
no_handler,
fs->pool));
}
else
{
ffd->txdelta_window_cache = NULL;
ffd->combined_window_cache = NULL;
}
return SVN_NO_ERROR;
}
/* Baton to be used for the remove_txn_cache() pool cleanup function, */
struct txn_cleanup_baton_t
{
/* the cache to reset */
svn_cache__t *txn_cache;
/* the position where to reset it */
svn_cache__t **to_reset;
};
/* APR pool cleanup handler that will reset the cache pointer given in
BATON_VOID. */
static apr_status_t
remove_txn_cache(void *baton_void)
{
struct txn_cleanup_baton_t *baton = baton_void;
/* be careful not to hurt performance by resetting newer txn's caches. */
if (*baton->to_reset == baton->txn_cache)
{
/* This is equivalent to calling svn_fs_fs__reset_txn_caches(). */
*baton->to_reset = NULL;
}
return APR_SUCCESS;
}
/* This function sets / registers the required callbacks for a given
* transaction-specific *CACHE object, if CACHE is not NULL and a no-op
* otherwise. In particular, it will ensure that *CACHE gets reset to NULL
* upon POOL destruction latest.
*/
static void
init_txn_callbacks(svn_cache__t **cache,
apr_pool_t *pool)
{
if (*cache != NULL)
{
struct txn_cleanup_baton_t *baton;
baton = apr_palloc(pool, sizeof(*baton));
baton->txn_cache = *cache;
baton->to_reset = cache;
apr_pool_cleanup_register(pool,
baton,
remove_txn_cache,
apr_pool_cleanup_null);
}
}
svn_error_t *
svn_fs_fs__initialize_txn_caches(svn_fs_t *fs,
const char *txn_id,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
/* Transaction content needs to be carefully prefixed to virtually
eliminate any chance for conflicts. The (repo, txn_id) pair
should be unique but if a transaction fails, it might be possible
to start a new transaction later that receives the same id.
Therefore, throw in a uuid as well - just to be sure. */
const char *prefix = apr_pstrcat(pool,
"fsfs:", fs->uuid,
"/", fs->path,
":", txn_id,
":", svn_uuid_generate(pool), ":",
(char *)NULL);
/* We don't support caching for concurrent transactions in the SAME
* FSFS session. Maybe, you forgot to clean POOL. */
if (ffd->txn_dir_cache != NULL || ffd->concurrent_transactions)
{
ffd->txn_dir_cache = NULL;
ffd->concurrent_transactions = TRUE;
return SVN_NO_ERROR;
}
/* create a txn-local directory cache */
SVN_ERR(create_cache(&ffd->txn_dir_cache,
NULL,
svn_cache__get_global_membuffer_cache(),
1024, 8,
svn_fs_fs__serialize_dir_entries,
svn_fs_fs__deserialize_dir_entries,
APR_HASH_KEY_STRING,
apr_pstrcat(pool, prefix, "TXNDIR",
(char *)NULL),
fs,
TRUE,
pool));
/* reset the transaction-specific cache if the pool gets cleaned up. */
init_txn_callbacks(&(ffd->txn_dir_cache), pool);
return SVN_NO_ERROR;
}
void
svn_fs_fs__reset_txn_caches(svn_fs_t *fs)
{
/* we can always just reset the caches. This may degrade performance but
* can never cause in incorrect behavior. */
fs_fs_data_t *ffd = fs->fsap_data;
ffd->txn_dir_cache = NULL;
}
Index: vendor/subversion/dist/subversion/libsvn_fs_fs/fs_fs.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_fs_fs/fs_fs.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_fs_fs/fs_fs.c (revision 286501)
@@ -1,11631 +1,11664 @@
/* fs_fs.c --- filesystem operations specific to fs_fs
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <errno.h>
#include <apr_general.h>
#include <apr_pools.h>
#include <apr_file_io.h>
#include <apr_uuid.h>
#include <apr_lib.h>
#include <apr_md5.h>
#include <apr_sha1.h>
#include <apr_strings.h>
#include <apr_thread_mutex.h>
#include "svn_pools.h"
#include "svn_fs.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_hash.h"
#include "svn_props.h"
#include "svn_sorts.h"
#include "svn_string.h"
#include "svn_time.h"
#include "svn_mergeinfo.h"
#include "svn_config.h"
#include "svn_ctype.h"
#include "svn_version.h"
#include "fs.h"
#include "tree.h"
#include "lock.h"
#include "key-gen.h"
#include "fs_fs.h"
#include "id.h"
#include "rep-cache.h"
#include "temp_serializer.h"
#include "private/svn_string_private.h"
#include "private/svn_fs_util.h"
#include "private/svn_subr_private.h"
#include "private/svn_delta_private.h"
#include "../libsvn_fs/fs-loader.h"
#include "svn_private_config.h"
#include "temp_serializer.h"
/* An arbitrary maximum path length, so clients can't run us out of memory
* by giving us arbitrarily large paths. */
#define FSFS_MAX_PATH_LEN 4096
/* The default maximum number of files per directory to store in the
rev and revprops directory. The number below is somewhat arbitrary,
and can be overridden by defining the macro while compiling; the
figure of 1000 is reasonable for VFAT filesystems, which are by far
the worst performers in this area. */
#ifndef SVN_FS_FS_DEFAULT_MAX_FILES_PER_DIR
#define SVN_FS_FS_DEFAULT_MAX_FILES_PER_DIR 1000
#endif
/* Begin deltification after a node history exceeded this this limit.
Useful values are 4 to 64 with 16 being a good compromise between
computational overhead and repository size savings.
Should be a power of 2.
Values < 2 will result in standard skip-delta behavior. */
#define SVN_FS_FS_MAX_LINEAR_DELTIFICATION 16
/* Finding a deltification base takes operations proportional to the
number of changes being skipped. To prevent exploding runtime
during commits, limit the deltification range to this value.
Should be a power of 2 minus one.
Values < 1 disable deltification. */
#define SVN_FS_FS_MAX_DELTIFICATION_WALK 1023
/* Give writing processes 10 seconds to replace an existing revprop
file with a new one. After that time, we assume that the writing
process got aborted and that we have re-read revprops. */
#define REVPROP_CHANGE_TIMEOUT (10 * 1000000)
/* The following are names of atomics that will be used to communicate
* revprop updates across all processes on this machine. */
#define ATOMIC_REVPROP_GENERATION "rev-prop-generation"
#define ATOMIC_REVPROP_TIMEOUT "rev-prop-timeout"
#define ATOMIC_REVPROP_NAMESPACE "rev-prop-atomics"
/* Following are defines that specify the textual elements of the
native filesystem directories and revision files. */
/* Headers used to describe node-revision in the revision file. */
#define HEADER_ID "id"
#define HEADER_TYPE "type"
#define HEADER_COUNT "count"
#define HEADER_PROPS "props"
#define HEADER_TEXT "text"
#define HEADER_CPATH "cpath"
#define HEADER_PRED "pred"
#define HEADER_COPYFROM "copyfrom"
#define HEADER_COPYROOT "copyroot"
#define HEADER_FRESHTXNRT "is-fresh-txn-root"
#define HEADER_MINFO_HERE "minfo-here"
#define HEADER_MINFO_CNT "minfo-cnt"
/* Kinds that a change can be. */
#define ACTION_MODIFY "modify"
#define ACTION_ADD "add"
#define ACTION_DELETE "delete"
#define ACTION_REPLACE "replace"
#define ACTION_RESET "reset"
/* True and False flags. */
#define FLAG_TRUE "true"
#define FLAG_FALSE "false"
/* Kinds that a node-rev can be. */
#define KIND_FILE "file"
#define KIND_DIR "dir"
/* Kinds of representation. */
#define REP_PLAIN "PLAIN"
#define REP_DELTA "DELTA"
/* Notes:
To avoid opening and closing the rev-files all the time, it would
probably be advantageous to keep each rev-file open for the
lifetime of the transaction object. I'll leave that as a later
optimization for now.
I didn't keep track of pool lifetimes at all in this code. There
are likely some errors because of that.
*/
/* The vtable associated with an open transaction object. */
static txn_vtable_t txn_vtable = {
svn_fs_fs__commit_txn,
svn_fs_fs__abort_txn,
svn_fs_fs__txn_prop,
svn_fs_fs__txn_proplist,
svn_fs_fs__change_txn_prop,
svn_fs_fs__txn_root,
svn_fs_fs__change_txn_props
};
/* Declarations. */
static svn_error_t *
read_min_unpacked_rev(svn_revnum_t *min_unpacked_rev,
const char *path,
apr_pool_t *pool);
static svn_error_t *
update_min_unpacked_rev(svn_fs_t *fs, apr_pool_t *pool);
static svn_error_t *
get_youngest(svn_revnum_t *youngest_p, const char *fs_path, apr_pool_t *pool);
static svn_error_t *
verify_walker(representation_t *rep,
void *baton,
svn_fs_t *fs,
apr_pool_t *scratch_pool);
/* Pathname helper functions */
/* Return TRUE is REV is packed in FS, FALSE otherwise. */
static svn_boolean_t
is_packed_rev(svn_fs_t *fs, svn_revnum_t rev)
{
fs_fs_data_t *ffd = fs->fsap_data;
return (rev < ffd->min_unpacked_rev);
}
/* Return TRUE is REV is packed in FS, FALSE otherwise. */
static svn_boolean_t
is_packed_revprop(svn_fs_t *fs, svn_revnum_t rev)
{
fs_fs_data_t *ffd = fs->fsap_data;
/* rev 0 will not be packed */
return (rev < ffd->min_unpacked_rev)
&& (rev != 0)
&& (ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT);
}
static const char *
path_format(svn_fs_t *fs, apr_pool_t *pool)
{
return svn_dirent_join(fs->path, PATH_FORMAT, pool);
}
static APR_INLINE const char *
path_uuid(svn_fs_t *fs, apr_pool_t *pool)
{
return svn_dirent_join(fs->path, PATH_UUID, pool);
}
const char *
svn_fs_fs__path_current(svn_fs_t *fs, apr_pool_t *pool)
{
return svn_dirent_join(fs->path, PATH_CURRENT, pool);
}
static APR_INLINE const char *
path_txn_current(svn_fs_t *fs, apr_pool_t *pool)
{
return svn_dirent_join(fs->path, PATH_TXN_CURRENT, pool);
}
static APR_INLINE const char *
path_txn_current_lock(svn_fs_t *fs, apr_pool_t *pool)
{
return svn_dirent_join(fs->path, PATH_TXN_CURRENT_LOCK, pool);
}
static APR_INLINE const char *
path_lock(svn_fs_t *fs, apr_pool_t *pool)
{
return svn_dirent_join(fs->path, PATH_LOCK_FILE, pool);
}
static const char *
path_revprop_generation(svn_fs_t *fs, apr_pool_t *pool)
{
return svn_dirent_join(fs->path, PATH_REVPROP_GENERATION, pool);
}
static const char *
path_rev_packed(svn_fs_t *fs, svn_revnum_t rev, const char *kind,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
assert(ffd->max_files_per_dir);
assert(is_packed_rev(fs, rev));
return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR,
apr_psprintf(pool,
"%ld" PATH_EXT_PACKED_SHARD,
rev / ffd->max_files_per_dir),
kind, NULL);
}
static const char *
path_rev_shard(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
assert(ffd->max_files_per_dir);
return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR,
apr_psprintf(pool, "%ld",
rev / ffd->max_files_per_dir),
NULL);
}
static const char *
path_rev(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
assert(! is_packed_rev(fs, rev));
if (ffd->max_files_per_dir)
{
return svn_dirent_join(path_rev_shard(fs, rev, pool),
apr_psprintf(pool, "%ld", rev),
pool);
}
return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR,
apr_psprintf(pool, "%ld", rev), NULL);
}
svn_error_t *
svn_fs_fs__path_rev_absolute(const char **path,
svn_fs_t *fs,
svn_revnum_t rev,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
if (ffd->format < SVN_FS_FS__MIN_PACKED_FORMAT
|| ! is_packed_rev(fs, rev))
{
*path = path_rev(fs, rev, pool);
}
else
{
*path = path_rev_packed(fs, rev, PATH_PACKED, pool);
}
return SVN_NO_ERROR;
}
static const char *
path_revprops_shard(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
assert(ffd->max_files_per_dir);
return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR,
apr_psprintf(pool, "%ld",
rev / ffd->max_files_per_dir),
NULL);
}
static const char *
path_revprops_pack_shard(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
assert(ffd->max_files_per_dir);
return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR,
apr_psprintf(pool, "%ld" PATH_EXT_PACKED_SHARD,
rev / ffd->max_files_per_dir),
NULL);
}
static const char *
path_revprops(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
if (ffd->max_files_per_dir)
{
return svn_dirent_join(path_revprops_shard(fs, rev, pool),
apr_psprintf(pool, "%ld", rev),
pool);
}
return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR,
apr_psprintf(pool, "%ld", rev), NULL);
}
static APR_INLINE const char *
path_txn_dir(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
{
SVN_ERR_ASSERT_NO_RETURN(txn_id != NULL);
return svn_dirent_join_many(pool, fs->path, PATH_TXNS_DIR,
apr_pstrcat(pool, txn_id, PATH_EXT_TXN,
(char *)NULL),
NULL);
}
/* Return the name of the sha1->rep mapping file in transaction TXN_ID
* within FS for the given SHA1 checksum. Use POOL for allocations.
*/
static APR_INLINE const char *
path_txn_sha1(svn_fs_t *fs, const char *txn_id, svn_checksum_t *sha1,
apr_pool_t *pool)
{
return svn_dirent_join(path_txn_dir(fs, txn_id, pool),
svn_checksum_to_cstring(sha1, pool),
pool);
}
static APR_INLINE const char *
path_txn_changes(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
{
return svn_dirent_join(path_txn_dir(fs, txn_id, pool), PATH_CHANGES, pool);
}
static APR_INLINE const char *
path_txn_props(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
{
return svn_dirent_join(path_txn_dir(fs, txn_id, pool), PATH_TXN_PROPS, pool);
}
static APR_INLINE const char *
path_txn_next_ids(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
{
return svn_dirent_join(path_txn_dir(fs, txn_id, pool), PATH_NEXT_IDS, pool);
}
static APR_INLINE const char *
path_min_unpacked_rev(svn_fs_t *fs, apr_pool_t *pool)
{
return svn_dirent_join(fs->path, PATH_MIN_UNPACKED_REV, pool);
}
static APR_INLINE const char *
path_txn_proto_rev(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
return svn_dirent_join_many(pool, fs->path, PATH_TXN_PROTOS_DIR,
apr_pstrcat(pool, txn_id, PATH_EXT_REV,
(char *)NULL),
NULL);
else
return svn_dirent_join(path_txn_dir(fs, txn_id, pool), PATH_REV, pool);
}
static APR_INLINE const char *
path_txn_proto_rev_lock(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
return svn_dirent_join_many(pool, fs->path, PATH_TXN_PROTOS_DIR,
apr_pstrcat(pool, txn_id, PATH_EXT_REV_LOCK,
(char *)NULL),
NULL);
else
return svn_dirent_join(path_txn_dir(fs, txn_id, pool), PATH_REV_LOCK,
pool);
}
static const char *
path_txn_node_rev(svn_fs_t *fs, const svn_fs_id_t *id, apr_pool_t *pool)
{
const char *txn_id = svn_fs_fs__id_txn_id(id);
const char *node_id = svn_fs_fs__id_node_id(id);
const char *copy_id = svn_fs_fs__id_copy_id(id);
const char *name = apr_psprintf(pool, PATH_PREFIX_NODE "%s.%s",
node_id, copy_id);
return svn_dirent_join(path_txn_dir(fs, txn_id, pool), name, pool);
}
static APR_INLINE const char *
path_txn_node_props(svn_fs_t *fs, const svn_fs_id_t *id, apr_pool_t *pool)
{
return apr_pstrcat(pool, path_txn_node_rev(fs, id, pool), PATH_EXT_PROPS,
(char *)NULL);
}
static APR_INLINE const char *
path_txn_node_children(svn_fs_t *fs, const svn_fs_id_t *id, apr_pool_t *pool)
{
return apr_pstrcat(pool, path_txn_node_rev(fs, id, pool),
PATH_EXT_CHILDREN, (char *)NULL);
}
static APR_INLINE const char *
path_node_origin(svn_fs_t *fs, const char *node_id, apr_pool_t *pool)
{
size_t len = strlen(node_id);
const char *node_id_minus_last_char =
(len == 1) ? "0" : apr_pstrmemdup(pool, node_id, len - 1);
return svn_dirent_join_many(pool, fs->path, PATH_NODE_ORIGINS_DIR,
node_id_minus_last_char, NULL);
}
static APR_INLINE const char *
path_and_offset_of(apr_file_t *file, apr_pool_t *pool)
{
const char *path;
apr_off_t offset = 0;
if (apr_file_name_get(&path, file) != APR_SUCCESS)
path = "(unknown)";
if (apr_file_seek(file, APR_CUR, &offset) != APR_SUCCESS)
offset = -1;
return apr_psprintf(pool, "%s:%" APR_OFF_T_FMT, path, offset);
}
/* Functions for working with shared transaction data. */
/* Return the transaction object for transaction TXN_ID from the
transaction list of filesystem FS (which must already be locked via the
txn_list_lock mutex). If the transaction does not exist in the list,
then create a new transaction object and return it (if CREATE_NEW is
true) or return NULL (otherwise). */
static fs_fs_shared_txn_data_t *
get_shared_txn(svn_fs_t *fs, const char *txn_id, svn_boolean_t create_new)
{
fs_fs_data_t *ffd = fs->fsap_data;
fs_fs_shared_data_t *ffsd = ffd->shared;
fs_fs_shared_txn_data_t *txn;
for (txn = ffsd->txns; txn; txn = txn->next)
if (strcmp(txn->txn_id, txn_id) == 0)
break;
if (txn || !create_new)
return txn;
/* Use the transaction object from the (single-object) freelist,
if one is available, or otherwise create a new object. */
if (ffsd->free_txn)
{
txn = ffsd->free_txn;
ffsd->free_txn = NULL;
}
else
{
apr_pool_t *subpool = svn_pool_create(ffsd->common_pool);
txn = apr_palloc(subpool, sizeof(*txn));
txn->pool = subpool;
}
assert(strlen(txn_id) < sizeof(txn->txn_id));
apr_cpystrn(txn->txn_id, txn_id, sizeof(txn->txn_id));
txn->being_written = FALSE;
/* Link this transaction into the head of the list. We will typically
be dealing with only one active transaction at a time, so it makes
sense for searches through the transaction list to look at the
newest transactions first. */
txn->next = ffsd->txns;
ffsd->txns = txn;
return txn;
}
/* Free the transaction object for transaction TXN_ID, and remove it
from the transaction list of filesystem FS (which must already be
locked via the txn_list_lock mutex). Do nothing if the transaction
does not exist. */
static void
free_shared_txn(svn_fs_t *fs, const char *txn_id)
{
fs_fs_data_t *ffd = fs->fsap_data;
fs_fs_shared_data_t *ffsd = ffd->shared;
fs_fs_shared_txn_data_t *txn, *prev = NULL;
for (txn = ffsd->txns; txn; prev = txn, txn = txn->next)
if (strcmp(txn->txn_id, txn_id) == 0)
break;
if (!txn)
return;
if (prev)
prev->next = txn->next;
else
ffsd->txns = txn->next;
/* As we typically will be dealing with one transaction after another,
we will maintain a single-object free list so that we can hopefully
keep reusing the same transaction object. */
if (!ffsd->free_txn)
ffsd->free_txn = txn;
else
svn_pool_destroy(txn->pool);
}
/* Obtain a lock on the transaction list of filesystem FS, call BODY
with FS, BATON, and POOL, and then unlock the transaction list.
Return what BODY returned. */
static svn_error_t *
with_txnlist_lock(svn_fs_t *fs,
svn_error_t *(*body)(svn_fs_t *fs,
const void *baton,
apr_pool_t *pool),
const void *baton,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
fs_fs_shared_data_t *ffsd = ffd->shared;
SVN_MUTEX__WITH_LOCK(ffsd->txn_list_lock,
body(fs, baton, pool));
return SVN_NO_ERROR;
}
/* Get a lock on empty file LOCK_FILENAME, creating it in POOL. */
static svn_error_t *
get_lock_on_filesystem(const char *lock_filename,
apr_pool_t *pool)
{
svn_error_t *err = svn_io_file_lock2(lock_filename, TRUE, FALSE, pool);
if (err && APR_STATUS_IS_ENOENT(err->apr_err))
{
/* No lock file? No big deal; these are just empty files
anyway. Create it and try again. */
svn_error_clear(err);
err = NULL;
SVN_ERR(svn_io_file_create(lock_filename, "", pool));
SVN_ERR(svn_io_file_lock2(lock_filename, TRUE, FALSE, pool));
}
return svn_error_trace(err);
}
/* Reset the HAS_WRITE_LOCK member in the FFD given as BATON_VOID.
When registered with the pool holding the lock on the lock file,
this makes sure the flag gets reset just before we release the lock. */
static apr_status_t
reset_lock_flag(void *baton_void)
{
fs_fs_data_t *ffd = baton_void;
ffd->has_write_lock = FALSE;
return APR_SUCCESS;
}
/* Obtain a write lock on the file LOCK_FILENAME (protecting with
LOCK_MUTEX if APR is threaded) in a subpool of POOL, call BODY with
BATON and that subpool, destroy the subpool (releasing the write
lock) and return what BODY returned. If IS_GLOBAL_LOCK is set,
set the HAS_WRITE_LOCK flag while we keep the write lock. */
static svn_error_t *
with_some_lock_file(svn_fs_t *fs,
svn_error_t *(*body)(void *baton,
apr_pool_t *pool),
void *baton,
const char *lock_filename,
svn_boolean_t is_global_lock,
apr_pool_t *pool)
{
apr_pool_t *subpool = svn_pool_create(pool);
svn_error_t *err = get_lock_on_filesystem(lock_filename, subpool);
if (!err)
{
fs_fs_data_t *ffd = fs->fsap_data;
if (is_global_lock)
{
/* set the "got the lock" flag and register reset function */
apr_pool_cleanup_register(subpool,
ffd,
reset_lock_flag,
apr_pool_cleanup_null);
ffd->has_write_lock = TRUE;
}
/* nobody else will modify the repo state
=> read HEAD & pack info once */
if (ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT)
SVN_ERR(update_min_unpacked_rev(fs, pool));
SVN_ERR(get_youngest(&ffd->youngest_rev_cache, fs->path,
pool));
err = body(baton, subpool);
}
svn_pool_destroy(subpool);
return svn_error_trace(err);
}
svn_error_t *
svn_fs_fs__with_write_lock(svn_fs_t *fs,
svn_error_t *(*body)(void *baton,
apr_pool_t *pool),
void *baton,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
fs_fs_shared_data_t *ffsd = ffd->shared;
SVN_MUTEX__WITH_LOCK(ffsd->fs_write_lock,
with_some_lock_file(fs, body, baton,
path_lock(fs, pool),
TRUE,
pool));
return SVN_NO_ERROR;
}
/* Run BODY (with BATON and POOL) while the txn-current file
of FS is locked. */
static svn_error_t *
with_txn_current_lock(svn_fs_t *fs,
svn_error_t *(*body)(void *baton,
apr_pool_t *pool),
void *baton,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
fs_fs_shared_data_t *ffsd = ffd->shared;
SVN_MUTEX__WITH_LOCK(ffsd->txn_current_lock,
with_some_lock_file(fs, body, baton,
path_txn_current_lock(fs, pool),
FALSE,
pool));
return SVN_NO_ERROR;
}
/* A structure used by unlock_proto_rev() and unlock_proto_rev_body(),
which see. */
struct unlock_proto_rev_baton
{
const char *txn_id;
void *lockcookie;
};
/* Callback used in the implementation of unlock_proto_rev(). */
static svn_error_t *
unlock_proto_rev_body(svn_fs_t *fs, const void *baton, apr_pool_t *pool)
{
const struct unlock_proto_rev_baton *b = baton;
const char *txn_id = b->txn_id;
apr_file_t *lockfile = b->lockcookie;
fs_fs_shared_txn_data_t *txn = get_shared_txn(fs, txn_id, FALSE);
apr_status_t apr_err;
if (!txn)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Can't unlock unknown transaction '%s'"),
txn_id);
if (!txn->being_written)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Can't unlock nonlocked transaction '%s'"),
txn_id);
apr_err = apr_file_unlock(lockfile);
if (apr_err)
return svn_error_wrap_apr
(apr_err,
_("Can't unlock prototype revision lockfile for transaction '%s'"),
txn_id);
apr_err = apr_file_close(lockfile);
if (apr_err)
return svn_error_wrap_apr
(apr_err,
_("Can't close prototype revision lockfile for transaction '%s'"),
txn_id);
txn->being_written = FALSE;
return SVN_NO_ERROR;
}
/* Unlock the prototype revision file for transaction TXN_ID in filesystem
FS using cookie LOCKCOOKIE. The original prototype revision file must
have been closed _before_ calling this function.
Perform temporary allocations in POOL. */
static svn_error_t *
unlock_proto_rev(svn_fs_t *fs, const char *txn_id, void *lockcookie,
apr_pool_t *pool)
{
struct unlock_proto_rev_baton b;
b.txn_id = txn_id;
b.lockcookie = lockcookie;
return with_txnlist_lock(fs, unlock_proto_rev_body, &b, pool);
}
/* Same as unlock_proto_rev(), but requires that the transaction list
lock is already held. */
static svn_error_t *
unlock_proto_rev_list_locked(svn_fs_t *fs, const char *txn_id,
void *lockcookie,
apr_pool_t *pool)
{
struct unlock_proto_rev_baton b;
b.txn_id = txn_id;
b.lockcookie = lockcookie;
return unlock_proto_rev_body(fs, &b, pool);
}
/* A structure used by get_writable_proto_rev() and
get_writable_proto_rev_body(), which see. */
struct get_writable_proto_rev_baton
{
apr_file_t **file;
void **lockcookie;
const char *txn_id;
};
/* Callback used in the implementation of get_writable_proto_rev(). */
static svn_error_t *
get_writable_proto_rev_body(svn_fs_t *fs, const void *baton, apr_pool_t *pool)
{
const struct get_writable_proto_rev_baton *b = baton;
apr_file_t **file = b->file;
void **lockcookie = b->lockcookie;
const char *txn_id = b->txn_id;
svn_error_t *err;
fs_fs_shared_txn_data_t *txn = get_shared_txn(fs, txn_id, TRUE);
/* First, ensure that no thread in this process (including this one)
is currently writing to this transaction's proto-rev file. */
if (txn->being_written)
return svn_error_createf(SVN_ERR_FS_REP_BEING_WRITTEN, NULL,
_("Cannot write to the prototype revision file "
"of transaction '%s' because a previous "
"representation is currently being written by "
"this process"),
txn_id);
/* We know that no thread in this process is writing to the proto-rev
file, and by extension, that no thread in this process is holding a
lock on the prototype revision lock file. It is therefore safe
for us to attempt to lock this file, to see if any other process
is holding a lock. */
{
apr_file_t *lockfile;
apr_status_t apr_err;
const char *lockfile_path = path_txn_proto_rev_lock(fs, txn_id, pool);
/* Open the proto-rev lockfile, creating it if necessary, as it may
not exist if the transaction dates from before the lockfiles were
introduced.
### We'd also like to use something like svn_io_file_lock2(), but
that forces us to create a subpool just to be able to unlock
the file, which seems a waste. */
SVN_ERR(svn_io_file_open(&lockfile, lockfile_path,
APR_WRITE | APR_CREATE, APR_OS_DEFAULT, pool));
apr_err = apr_file_lock(lockfile,
APR_FLOCK_EXCLUSIVE | APR_FLOCK_NONBLOCK);
if (apr_err)
{
svn_error_clear(svn_io_file_close(lockfile, pool));
if (APR_STATUS_IS_EAGAIN(apr_err))
return svn_error_createf(SVN_ERR_FS_REP_BEING_WRITTEN, NULL,
_("Cannot write to the prototype revision "
"file of transaction '%s' because a "
"previous representation is currently "
"being written by another process"),
txn_id);
return svn_error_wrap_apr(apr_err,
_("Can't get exclusive lock on file '%s'"),
svn_dirent_local_style(lockfile_path, pool));
}
*lockcookie = lockfile;
}
/* We've successfully locked the transaction; mark it as such. */
txn->being_written = TRUE;
/* Now open the prototype revision file and seek to the end. */
err = svn_io_file_open(file, path_txn_proto_rev(fs, txn_id, pool),
APR_WRITE | APR_BUFFERED, APR_OS_DEFAULT, pool);
/* You might expect that we could dispense with the following seek
and achieve the same thing by opening the file using APR_APPEND.
Unfortunately, APR's buffered file implementation unconditionally
places its initial file pointer at the start of the file (even for
files opened with APR_APPEND), so we need this seek to reconcile
the APR file pointer to the OS file pointer (since we need to be
able to read the current file position later). */
if (!err)
{
apr_off_t offset = 0;
err = svn_io_file_seek(*file, APR_END, &offset, pool);
}
if (err)
{
err = svn_error_compose_create(
err,
unlock_proto_rev_list_locked(fs, txn_id, *lockcookie, pool));
*lockcookie = NULL;
}
return svn_error_trace(err);
}
/* Get a handle to the prototype revision file for transaction TXN_ID in
filesystem FS, and lock it for writing. Return FILE, a file handle
positioned at the end of the file, and LOCKCOOKIE, a cookie that
should be passed to unlock_proto_rev() to unlock the file once FILE
has been closed.
If the prototype revision file is already locked, return error
SVN_ERR_FS_REP_BEING_WRITTEN.
Perform all allocations in POOL. */
static svn_error_t *
get_writable_proto_rev(apr_file_t **file,
void **lockcookie,
svn_fs_t *fs, const char *txn_id,
apr_pool_t *pool)
{
struct get_writable_proto_rev_baton b;
b.file = file;
b.lockcookie = lockcookie;
b.txn_id = txn_id;
return with_txnlist_lock(fs, get_writable_proto_rev_body, &b, pool);
}
/* Callback used in the implementation of purge_shared_txn(). */
static svn_error_t *
purge_shared_txn_body(svn_fs_t *fs, const void *baton, apr_pool_t *pool)
{
const char *txn_id = baton;
free_shared_txn(fs, txn_id);
svn_fs_fs__reset_txn_caches(fs);
return SVN_NO_ERROR;
}
/* Purge the shared data for transaction TXN_ID in filesystem FS.
Perform all allocations in POOL. */
static svn_error_t *
purge_shared_txn(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
{
return with_txnlist_lock(fs, purge_shared_txn_body, txn_id, pool);
}
/* Fetch the current offset of FILE into *OFFSET_P. */
static svn_error_t *
get_file_offset(apr_off_t *offset_p, apr_file_t *file, apr_pool_t *pool)
{
apr_off_t offset;
/* Note that, for buffered files, one (possibly surprising) side-effect
of this call is to flush any unwritten data to disk. */
offset = 0;
SVN_ERR(svn_io_file_seek(file, APR_CUR, &offset, pool));
*offset_p = offset;
return SVN_NO_ERROR;
}
/* Check that BUF, a nul-terminated buffer of text from file PATH,
contains only digits at OFFSET and beyond, raising an error if not.
TITLE contains a user-visible description of the file, usually the
short file name.
Uses POOL for temporary allocation. */
static svn_error_t *
check_file_buffer_numeric(const char *buf, apr_off_t offset,
const char *path, const char *title,
apr_pool_t *pool)
{
const char *p;
for (p = buf + offset; *p; p++)
if (!svn_ctype_isdigit(*p))
return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
_("%s file '%s' contains unexpected non-digit '%c' within '%s'"),
title, svn_dirent_local_style(path, pool), *p, buf);
return SVN_NO_ERROR;
}
/* Check that BUF, a nul-terminated buffer of text from format file PATH,
contains only digits at OFFSET and beyond, raising an error if not.
Uses POOL for temporary allocation. */
static svn_error_t *
check_format_file_buffer_numeric(const char *buf, apr_off_t offset,
const char *path, apr_pool_t *pool)
{
return check_file_buffer_numeric(buf, offset, path, "Format", pool);
}
/* Return the error SVN_ERR_FS_UNSUPPORTED_FORMAT if FS's format
number is not the same as a format number supported by this
Subversion. */
static svn_error_t *
check_format(int format)
{
/* Blacklist. These formats may be either younger or older than
SVN_FS_FS__FORMAT_NUMBER, but we don't support them. */
if (format == SVN_FS_FS__PACKED_REVPROP_SQLITE_DEV_FORMAT)
return svn_error_createf(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL,
_("Found format '%d', only created by "
"unreleased dev builds; see "
"http://subversion.apache.org"
"/docs/release-notes/1.7#revprop-packing"),
format);
/* We support all formats from 1-current simultaneously */
if (1 <= format && format <= SVN_FS_FS__FORMAT_NUMBER)
return SVN_NO_ERROR;
return svn_error_createf(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL,
_("Expected FS format between '1' and '%d'; found format '%d'"),
SVN_FS_FS__FORMAT_NUMBER, format);
}
/* Read the format number and maximum number of files per directory
from PATH and return them in *PFORMAT and *MAX_FILES_PER_DIR
respectively.
*MAX_FILES_PER_DIR is obtained from the 'layout' format option, and
will be set to zero if a linear scheme should be used.
Use POOL for temporary allocation. */
static svn_error_t *
read_format(int *pformat, int *max_files_per_dir,
const char *path, apr_pool_t *pool)
{
svn_error_t *err;
svn_stream_t *stream;
svn_stringbuf_t *content;
svn_stringbuf_t *buf;
svn_boolean_t eos = FALSE;
err = svn_stringbuf_from_file2(&content, path, pool);
if (err && APR_STATUS_IS_ENOENT(err->apr_err))
{
/* Treat an absent format file as format 1. Do not try to
create the format file on the fly, because the repository
might be read-only for us, or this might be a read-only
operation, and the spirit of FSFS is to make no changes
whatseover in read-only operations. See thread starting at
http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=97600
for more. */
svn_error_clear(err);
*pformat = 1;
*max_files_per_dir = 0;
return SVN_NO_ERROR;
}
SVN_ERR(err);
stream = svn_stream_from_stringbuf(content, pool);
SVN_ERR(svn_stream_readline(stream, &buf, "\n", &eos, pool));
if (buf->len == 0 && eos)
{
/* Return a more useful error message. */
return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
_("Can't read first line of format file '%s'"),
svn_dirent_local_style(path, pool));
}
/* Check that the first line contains only digits. */
SVN_ERR(check_format_file_buffer_numeric(buf->data, 0, path, pool));
SVN_ERR(svn_cstring_atoi(pformat, buf->data));
/* Check that we support this format at all */
SVN_ERR(check_format(*pformat));
/* Set the default values for anything that can be set via an option. */
*max_files_per_dir = 0;
/* Read any options. */
while (!eos)
{
SVN_ERR(svn_stream_readline(stream, &buf, "\n", &eos, pool));
if (buf->len == 0)
break;
if (*pformat >= SVN_FS_FS__MIN_LAYOUT_FORMAT_OPTION_FORMAT &&
strncmp(buf->data, "layout ", 7) == 0)
{
if (strcmp(buf->data + 7, "linear") == 0)
{
*max_files_per_dir = 0;
continue;
}
if (strncmp(buf->data + 7, "sharded ", 8) == 0)
{
/* Check that the argument is numeric. */
SVN_ERR(check_format_file_buffer_numeric(buf->data, 15, path, pool));
SVN_ERR(svn_cstring_atoi(max_files_per_dir, buf->data + 15));
continue;
}
}
return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
_("'%s' contains invalid filesystem format option '%s'"),
svn_dirent_local_style(path, pool), buf->data);
}
return SVN_NO_ERROR;
}
/* Write the format number and maximum number of files per directory
to a new format file in PATH, possibly expecting to overwrite a
previously existing file.
Use POOL for temporary allocation. */
static svn_error_t *
write_format(const char *path, int format, int max_files_per_dir,
svn_boolean_t overwrite, apr_pool_t *pool)
{
svn_stringbuf_t *sb;
SVN_ERR_ASSERT(1 <= format && format <= SVN_FS_FS__FORMAT_NUMBER);
sb = svn_stringbuf_createf(pool, "%d\n", format);
if (format >= SVN_FS_FS__MIN_LAYOUT_FORMAT_OPTION_FORMAT)
{
if (max_files_per_dir)
svn_stringbuf_appendcstr(sb, apr_psprintf(pool, "layout sharded %d\n",
max_files_per_dir));
else
svn_stringbuf_appendcstr(sb, "layout linear\n");
}
/* svn_io_write_version_file() does a load of magic to allow it to
replace version files that already exist. We only need to do
that when we're allowed to overwrite an existing file. */
if (! overwrite)
{
/* Create the file */
SVN_ERR(svn_io_file_create(path, sb->data, pool));
}
else
{
const char *path_tmp;
SVN_ERR(svn_io_write_unique(&path_tmp,
svn_dirent_dirname(path, pool),
sb->data, sb->len,
svn_io_file_del_none, pool));
/* rename the temp file as the real destination */
SVN_ERR(svn_io_file_rename(path_tmp, path, pool));
}
/* And set the perms to make it read only */
return svn_io_set_file_read_only(path, FALSE, pool);
}
svn_boolean_t
svn_fs_fs__fs_supports_mergeinfo(svn_fs_t *fs)
{
fs_fs_data_t *ffd = fs->fsap_data;
return ffd->format >= SVN_FS_FS__MIN_MERGEINFO_FORMAT;
}
/* Read the configuration information of the file system at FS_PATH
* and set the respective values in FFD. Use POOL for allocations.
*/
static svn_error_t *
read_config(fs_fs_data_t *ffd,
const char *fs_path,
apr_pool_t *pool)
{
SVN_ERR(svn_config_read3(&ffd->config,
svn_dirent_join(fs_path, PATH_CONFIG, pool),
FALSE, FALSE, FALSE, pool));
/* Initialize ffd->rep_sharing_allowed. */
if (ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT)
SVN_ERR(svn_config_get_bool(ffd->config, &ffd->rep_sharing_allowed,
CONFIG_SECTION_REP_SHARING,
CONFIG_OPTION_ENABLE_REP_SHARING, TRUE));
else
ffd->rep_sharing_allowed = FALSE;
/* Initialize deltification settings in ffd. */
if (ffd->format >= SVN_FS_FS__MIN_DELTIFICATION_FORMAT)
{
SVN_ERR(svn_config_get_bool(ffd->config, &ffd->deltify_directories,
CONFIG_SECTION_DELTIFICATION,
CONFIG_OPTION_ENABLE_DIR_DELTIFICATION,
FALSE));
SVN_ERR(svn_config_get_bool(ffd->config, &ffd->deltify_properties,
CONFIG_SECTION_DELTIFICATION,
CONFIG_OPTION_ENABLE_PROPS_DELTIFICATION,
FALSE));
SVN_ERR(svn_config_get_int64(ffd->config, &ffd->max_deltification_walk,
CONFIG_SECTION_DELTIFICATION,
CONFIG_OPTION_MAX_DELTIFICATION_WALK,
SVN_FS_FS_MAX_DELTIFICATION_WALK));
SVN_ERR(svn_config_get_int64(ffd->config, &ffd->max_linear_deltification,
CONFIG_SECTION_DELTIFICATION,
CONFIG_OPTION_MAX_LINEAR_DELTIFICATION,
SVN_FS_FS_MAX_LINEAR_DELTIFICATION));
}
else
{
ffd->deltify_directories = FALSE;
ffd->deltify_properties = FALSE;
ffd->max_deltification_walk = SVN_FS_FS_MAX_DELTIFICATION_WALK;
ffd->max_linear_deltification = SVN_FS_FS_MAX_LINEAR_DELTIFICATION;
}
/* Initialize revprop packing settings in ffd. */
if (ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT)
{
SVN_ERR(svn_config_get_bool(ffd->config, &ffd->compress_packed_revprops,
CONFIG_SECTION_PACKED_REVPROPS,
CONFIG_OPTION_COMPRESS_PACKED_REVPROPS,
FALSE));
SVN_ERR(svn_config_get_int64(ffd->config, &ffd->revprop_pack_size,
CONFIG_SECTION_PACKED_REVPROPS,
CONFIG_OPTION_REVPROP_PACK_SIZE,
ffd->compress_packed_revprops
? 0x100
: 0x40));
ffd->revprop_pack_size *= 1024;
}
else
{
ffd->revprop_pack_size = 0x10000;
ffd->compress_packed_revprops = FALSE;
}
return SVN_NO_ERROR;
}
static svn_error_t *
write_config(svn_fs_t *fs,
apr_pool_t *pool)
{
#define NL APR_EOL_STR
static const char * const fsfs_conf_contents =
"### This file controls the configuration of the FSFS filesystem." NL
"" NL
"[" SVN_CACHE_CONFIG_CATEGORY_MEMCACHED_SERVERS "]" NL
"### These options name memcached servers used to cache internal FSFS" NL
"### data. See http://www.danga.com/memcached/ for more information on" NL
"### memcached. To use memcached with FSFS, run one or more memcached" NL
"### servers, and specify each of them as an option like so:" NL
"# first-server = 127.0.0.1:11211" NL
"# remote-memcached = mymemcached.corp.example.com:11212" NL
"### The option name is ignored; the value is of the form HOST:PORT." NL
"### memcached servers can be shared between multiple repositories;" NL
"### however, if you do this, you *must* ensure that repositories have" NL
"### distinct UUIDs and paths, or else cached data from one repository" NL
"### might be used by another accidentally. Note also that memcached has" NL
"### no authentication for reads or writes, so you must ensure that your" NL
"### memcached servers are only accessible by trusted users." NL
"" NL
"[" CONFIG_SECTION_CACHES "]" NL
"### When a cache-related error occurs, normally Subversion ignores it" NL
"### and continues, logging an error if the server is appropriately" NL
"### configured (and ignoring it with file:// access). To make" NL
"### Subversion never ignore cache errors, uncomment this line." NL
"# " CONFIG_OPTION_FAIL_STOP " = true" NL
"" NL
"[" CONFIG_SECTION_REP_SHARING "]" NL
"### To conserve space, the filesystem can optionally avoid storing" NL
"### duplicate representations. This comes at a slight cost in" NL
"### performance, as maintaining a database of shared representations can" NL
"### increase commit times. The space savings are dependent upon the size" NL
"### of the repository, the number of objects it contains and the amount of" NL
"### duplication between them, usually a function of the branching and" NL
"### merging process." NL
"###" NL
"### The following parameter enables rep-sharing in the repository. It can" NL
"### be switched on and off at will, but for best space-saving results" NL
"### should be enabled consistently over the life of the repository." NL
"### 'svnadmin verify' will check the rep-cache regardless of this setting." NL
"### rep-sharing is enabled by default." NL
"# " CONFIG_OPTION_ENABLE_REP_SHARING " = true" NL
"" NL
"[" CONFIG_SECTION_DELTIFICATION "]" NL
"### To conserve space, the filesystem stores data as differences against" NL
"### existing representations. This comes at a slight cost in performance," NL
"### as calculating differences can increase commit times. Reading data" NL
"### will also create higher CPU load and the data will be fragmented." NL
"### Since deltification tends to save significant amounts of disk space," NL
"### the overall I/O load can actually be lower." NL
"###" NL
"### The options in this section allow for tuning the deltification" NL
"### strategy. Their effects on data size and server performance may vary" NL
"### from one repository to another. Versions prior to 1.8 will ignore" NL
"### this section." NL
"###" NL
"### The following parameter enables deltification for directories. It can" NL
"### be switched on and off at will, but for best space-saving results" NL
"### should be enabled consistently over the life of the repository." NL
"### Repositories containing large directories will benefit greatly." NL
"### In rarely read repositories, the I/O overhead may be significant as" NL
"### cache hit rates will most likely be low" NL
"### directory deltification is disabled by default." NL
"# " CONFIG_OPTION_ENABLE_DIR_DELTIFICATION " = false" NL
"###" NL
"### The following parameter enables deltification for properties on files" NL
"### and directories. Overall, this is a minor tuning option but can save" NL
"### some disk space if you merge frequently or frequently change node" NL
"### properties. You should not activate this if rep-sharing has been" NL
"### disabled because this may result in a net increase in repository size." NL
"### property deltification is disabled by default." NL
"# " CONFIG_OPTION_ENABLE_PROPS_DELTIFICATION " = false" NL
"###" NL
"### During commit, the server may need to walk the whole change history of" NL
"### of a given node to find a suitable deltification base. This linear" NL
"### process can impact commit times, svnadmin load and similar operations." NL
"### This setting limits the depth of the deltification history. If the" NL
"### threshold has been reached, the node will be stored as fulltext and a" NL
"### new deltification history begins." NL
"### Note, this is unrelated to svn log." NL
"### Very large values rarely provide significant additional savings but" NL
"### can impact performance greatly - in particular if directory" NL
"### deltification has been activated. Very small values may be useful in" NL
"### repositories that are dominated by large, changing binaries." NL
"### Should be a power of two minus 1. A value of 0 will effectively" NL
"### disable deltification." NL
"### For 1.8, the default value is 1023; earlier versions have no limit." NL
"# " CONFIG_OPTION_MAX_DELTIFICATION_WALK " = 1023" NL
"###" NL
"### The skip-delta scheme used by FSFS tends to repeatably store redundant" NL
"### delta information where a simple delta against the latest version is" NL
"### often smaller. By default, 1.8+ will therefore use skip deltas only" NL
"### after the linear chain of deltas has grown beyond the threshold" NL
"### specified by this setting." NL
"### Values up to 64 can result in some reduction in repository size for" NL
"### the cost of quickly increasing I/O and CPU costs. Similarly, smaller" NL
"### numbers can reduce those costs at the cost of more disk space. For" NL
"### rarely read repositories or those containing larger binaries, this may" NL
"### present a better trade-off." NL
"### Should be a power of two. A value of 1 or smaller will cause the" NL
"### exclusive use of skip-deltas (as in pre-1.8)." NL
"### For 1.8, the default value is 16; earlier versions use 1." NL
"# " CONFIG_OPTION_MAX_LINEAR_DELTIFICATION " = 16" NL
"" NL
"[" CONFIG_SECTION_PACKED_REVPROPS "]" NL
"### This parameter controls the size (in kBytes) of packed revprop files." NL
"### Revprops of consecutive revisions will be concatenated into a single" NL
"### file up to but not exceeding the threshold given here. However, each" NL
"### pack file may be much smaller and revprops of a single revision may be" NL
"### much larger than the limit set here. The threshold will be applied" NL
"### before optional compression takes place." NL
"### Large values will reduce disk space usage at the expense of increased" NL
"### latency and CPU usage reading and changing individual revprops. They" NL
"### become an advantage when revprop caching has been enabled because a" NL
"### lot of data can be read in one go. Values smaller than 4 kByte will" NL
"### not improve latency any further and quickly render revprop packing" NL
"### ineffective." NL
"### revprop-pack-size is 64 kBytes by default for non-compressed revprop" NL
"### pack files and 256 kBytes when compression has been enabled." NL
"# " CONFIG_OPTION_REVPROP_PACK_SIZE " = 64" NL
"###" NL
"### To save disk space, packed revprop files may be compressed. Standard" NL
"### revprops tend to allow for very effective compression. Reading and" NL
"### even more so writing, become significantly more CPU intensive. With" NL
"### revprop caching enabled, the overhead can be offset by reduced I/O" NL
"### unless you often modify revprops after packing." NL
"### Compressing packed revprops is disabled by default." NL
"# " CONFIG_OPTION_COMPRESS_PACKED_REVPROPS " = false" NL
;
#undef NL
return svn_io_file_create(svn_dirent_join(fs->path, PATH_CONFIG, pool),
fsfs_conf_contents, pool);
}
static svn_error_t *
read_min_unpacked_rev(svn_revnum_t *min_unpacked_rev,
const char *path,
apr_pool_t *pool)
{
char buf[80];
apr_file_t *file;
apr_size_t len;
SVN_ERR(svn_io_file_open(&file, path, APR_READ | APR_BUFFERED,
APR_OS_DEFAULT, pool));
len = sizeof(buf);
SVN_ERR(svn_io_read_length_line(file, buf, &len, pool));
SVN_ERR(svn_io_file_close(file, pool));
*min_unpacked_rev = SVN_STR_TO_REV(buf);
return SVN_NO_ERROR;
}
static svn_error_t *
update_min_unpacked_rev(svn_fs_t *fs, apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
SVN_ERR_ASSERT(ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT);
return read_min_unpacked_rev(&ffd->min_unpacked_rev,
path_min_unpacked_rev(fs, pool),
pool);
}
svn_error_t *
svn_fs_fs__open(svn_fs_t *fs, const char *path, apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
apr_file_t *uuid_file;
int format, max_files_per_dir;
char buf[APR_UUID_FORMATTED_LENGTH + 2];
apr_size_t limit;
fs->path = apr_pstrdup(fs->pool, path);
/* Read the FS format number. */
SVN_ERR(read_format(&format, &max_files_per_dir,
path_format(fs, pool), pool));
/* Now we've got a format number no matter what. */
ffd->format = format;
ffd->max_files_per_dir = max_files_per_dir;
/* Read in and cache the repository uuid. */
SVN_ERR(svn_io_file_open(&uuid_file, path_uuid(fs, pool),
APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool));
limit = sizeof(buf);
SVN_ERR(svn_io_read_length_line(uuid_file, buf, &limit, pool));
fs->uuid = apr_pstrdup(fs->pool, buf);
SVN_ERR(svn_io_file_close(uuid_file, pool));
/* Read the min unpacked revision. */
if (ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT)
SVN_ERR(update_min_unpacked_rev(fs, pool));
/* Read the configuration file. */
SVN_ERR(read_config(ffd, fs->path, pool));
return get_youngest(&(ffd->youngest_rev_cache), path, pool);
}
/* Wrapper around svn_io_file_create which ignores EEXIST. */
static svn_error_t *
create_file_ignore_eexist(const char *file,
const char *contents,
apr_pool_t *pool)
{
svn_error_t *err = svn_io_file_create(file, contents, pool);
if (err && APR_STATUS_IS_EEXIST(err->apr_err))
{
svn_error_clear(err);
err = SVN_NO_ERROR;
}
return svn_error_trace(err);
}
/* forward declarations */
static svn_error_t *
pack_revprops_shard(const char *pack_file_dir,
const char *shard_path,
apr_int64_t shard,
int max_files_per_dir,
apr_off_t max_pack_size,
int compression_level,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool);
static svn_error_t *
delete_revprops_shard(const char *shard_path,
apr_int64_t shard,
int max_files_per_dir,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool);
/* In the filesystem FS, pack all revprop shards up to min_unpacked_rev.
*
* NOTE: Keep the old non-packed shards around until after the format bump.
* Otherwise, re-running upgrade will drop the packed revprop shard but
* have no unpacked data anymore. Call upgrade_cleanup_pack_revprops after
* the bump.
*
* Use SCRATCH_POOL for temporary allocations.
*/
static svn_error_t *
upgrade_pack_revprops(svn_fs_t *fs,
apr_pool_t *scratch_pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
const char *revprops_shard_path;
const char *revprops_pack_file_dir;
apr_int64_t shard;
apr_int64_t first_unpacked_shard
= ffd->min_unpacked_rev / ffd->max_files_per_dir;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
const char *revsprops_dir = svn_dirent_join(fs->path, PATH_REVPROPS_DIR,
scratch_pool);
int compression_level = ffd->compress_packed_revprops
? SVN_DELTA_COMPRESSION_LEVEL_DEFAULT
: SVN_DELTA_COMPRESSION_LEVEL_NONE;
/* first, pack all revprops shards to match the packed revision shards */
for (shard = 0; shard < first_unpacked_shard; ++shard)
{
revprops_pack_file_dir = svn_dirent_join(revsprops_dir,
apr_psprintf(iterpool,
"%" APR_INT64_T_FMT PATH_EXT_PACKED_SHARD,
shard),
iterpool);
revprops_shard_path = svn_dirent_join(revsprops_dir,
apr_psprintf(iterpool, "%" APR_INT64_T_FMT, shard),
iterpool);
SVN_ERR(pack_revprops_shard(revprops_pack_file_dir, revprops_shard_path,
shard, ffd->max_files_per_dir,
(int)(0.9 * ffd->revprop_pack_size),
compression_level,
NULL, NULL, iterpool));
svn_pool_clear(iterpool);
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* In the filesystem FS, remove all non-packed revprop shards up to
* min_unpacked_rev. Use SCRATCH_POOL for temporary allocations.
* See upgrade_pack_revprops for more info.
*/
static svn_error_t *
upgrade_cleanup_pack_revprops(svn_fs_t *fs,
apr_pool_t *scratch_pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
const char *revprops_shard_path;
apr_int64_t shard;
apr_int64_t first_unpacked_shard
= ffd->min_unpacked_rev / ffd->max_files_per_dir;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
const char *revsprops_dir = svn_dirent_join(fs->path, PATH_REVPROPS_DIR,
scratch_pool);
/* delete the non-packed revprops shards afterwards */
for (shard = 0; shard < first_unpacked_shard; ++shard)
{
revprops_shard_path = svn_dirent_join(revsprops_dir,
apr_psprintf(iterpool, "%" APR_INT64_T_FMT, shard),
iterpool);
SVN_ERR(delete_revprops_shard(revprops_shard_path,
shard, ffd->max_files_per_dir,
NULL, NULL, iterpool));
svn_pool_clear(iterpool);
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_error_t *
upgrade_body(void *baton, apr_pool_t *pool)
{
svn_fs_t *fs = baton;
int format, max_files_per_dir;
const char *format_path = path_format(fs, pool);
svn_node_kind_t kind;
svn_boolean_t needs_revprop_shard_cleanup = FALSE;
/* Read the FS format number and max-files-per-dir setting. */
SVN_ERR(read_format(&format, &max_files_per_dir, format_path, pool));
/* If the config file does not exist, create one. */
SVN_ERR(svn_io_check_path(svn_dirent_join(fs->path, PATH_CONFIG, pool),
&kind, pool));
switch (kind)
{
case svn_node_none:
SVN_ERR(write_config(fs, pool));
break;
case svn_node_file:
break;
default:
return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
_("'%s' is not a regular file."
" Please move it out of "
"the way and try again"),
svn_dirent_join(fs->path, PATH_CONFIG, pool));
}
/* If we're already up-to-date, there's nothing else to be done here. */
if (format == SVN_FS_FS__FORMAT_NUMBER)
return SVN_NO_ERROR;
/* If our filesystem predates the existance of the 'txn-current
file', make that file and its corresponding lock file. */
if (format < SVN_FS_FS__MIN_TXN_CURRENT_FORMAT)
{
SVN_ERR(create_file_ignore_eexist(path_txn_current(fs, pool), "0\n",
pool));
SVN_ERR(create_file_ignore_eexist(path_txn_current_lock(fs, pool), "",
pool));
}
/* If our filesystem predates the existance of the 'txn-protorevs'
dir, make that directory. */
if (format < SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
{
/* We don't use path_txn_proto_rev() here because it expects
we've already bumped our format. */
SVN_ERR(svn_io_make_dir_recursively(
svn_dirent_join(fs->path, PATH_TXN_PROTOS_DIR, pool), pool));
}
/* If our filesystem is new enough, write the min unpacked rev file. */
if (format < SVN_FS_FS__MIN_PACKED_FORMAT)
SVN_ERR(svn_io_file_create(path_min_unpacked_rev(fs, pool), "0\n", pool));
/* If the file system supports revision packing but not revprop packing
*and* the FS has been sharded, pack the revprops up to the point that
revision data has been packed. However, keep the non-packed revprop
files around until after the format bump */
if ( format >= SVN_FS_FS__MIN_PACKED_FORMAT
&& format < SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT
&& max_files_per_dir > 0)
{
needs_revprop_shard_cleanup = TRUE;
SVN_ERR(upgrade_pack_revprops(fs, pool));
}
/* Bump the format file. */
SVN_ERR(write_format(format_path, SVN_FS_FS__FORMAT_NUMBER,
max_files_per_dir, TRUE, pool));
/* Now, it is safe to remove the redundant revprop files. */
if (needs_revprop_shard_cleanup)
SVN_ERR(upgrade_cleanup_pack_revprops(fs, pool));
/* Done */
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__upgrade(svn_fs_t *fs, apr_pool_t *pool)
{
return svn_fs_fs__with_write_lock(fs, upgrade_body, (void *)fs, pool);
}
/* Functions for dealing with recoverable errors on mutable files
*
* Revprops, current, and txn-current files are mutable; that is, they
* change as part of normal fsfs operation, in constrat to revs files, or
* the format file, which are written once at create (or upgrade) time.
* When more than one host writes to the same repository, we will
* sometimes see these recoverable errors when accesssing these files.
*
* These errors all relate to NFS, and thus we only use this retry code if
* ESTALE is defined.
*
** ESTALE
*
* In NFS v3 and under, the server doesn't track opened files. If you
* unlink(2) or rename(2) a file held open by another process *on the
* same host*, that host's kernel typically renames the file to
* .nfsXXXX and automatically deletes that when it's no longer open,
* but this behavior is not required.
*
* For obvious reasons, this does not work *across hosts*. No one
* knows about the opened file; not the server, and not the deleting
* client. So the file vanishes, and the reader gets stale NFS file
* handle.
*
** EIO, ENOENT
*
* Some client implementations (at least the 2.6.18.5 kernel that ships
* with Ubuntu Dapper) sometimes give spurious ENOENT (only on open) or
* even EIO errors when trying to read these files that have been renamed
* over on some other host.
*
** Solution
*
* Try open and read of such files in try_stringbuf_from_file(). Call
* this function within a loop of RECOVERABLE_RETRY_COUNT iterations
* (though, realistically, the second try will succeed).
*/
#define RECOVERABLE_RETRY_COUNT 10
/* Read the file at PATH and return its content in *CONTENT. *CONTENT will
* not be modified unless the whole file was read successfully.
*
* ESTALE, EIO and ENOENT will not cause this function to return an error
* unless LAST_ATTEMPT has been set. If MISSING is not NULL, indicate
* missing files (ENOENT) there.
*
* Use POOL for allocations.
*/
static svn_error_t *
try_stringbuf_from_file(svn_stringbuf_t **content,
svn_boolean_t *missing,
const char *path,
svn_boolean_t last_attempt,
apr_pool_t *pool)
{
svn_error_t *err = svn_stringbuf_from_file2(content, path, pool);
if (missing)
*missing = FALSE;
if (err)
{
*content = NULL;
if (APR_STATUS_IS_ENOENT(err->apr_err))
{
if (!last_attempt)
{
svn_error_clear(err);
if (missing)
*missing = TRUE;
return SVN_NO_ERROR;
}
}
#ifdef ESTALE
else if (APR_TO_OS_ERROR(err->apr_err) == ESTALE
|| APR_TO_OS_ERROR(err->apr_err) == EIO)
{
if (!last_attempt)
{
svn_error_clear(err);
return SVN_NO_ERROR;
}
}
#endif
}
return svn_error_trace(err);
}
/* Read the 'current' file FNAME and store the contents in *BUF.
Allocations are performed in POOL. */
static svn_error_t *
read_content(svn_stringbuf_t **content, const char *fname, apr_pool_t *pool)
{
int i;
*content = NULL;
for (i = 0; !*content && (i < RECOVERABLE_RETRY_COUNT); ++i)
SVN_ERR(try_stringbuf_from_file(content, NULL,
fname, i + 1 < RECOVERABLE_RETRY_COUNT,
pool));
if (!*content)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Can't read '%s'"),
svn_dirent_local_style(fname, pool));
return SVN_NO_ERROR;
}
/* Find the youngest revision in a repository at path FS_PATH and
return it in *YOUNGEST_P. Perform temporary allocations in
POOL. */
static svn_error_t *
get_youngest(svn_revnum_t *youngest_p,
const char *fs_path,
apr_pool_t *pool)
{
svn_stringbuf_t *buf;
SVN_ERR(read_content(&buf, svn_dirent_join(fs_path, PATH_CURRENT, pool),
pool));
*youngest_p = SVN_STR_TO_REV(buf->data);
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__youngest_rev(svn_revnum_t *youngest_p,
svn_fs_t *fs,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
SVN_ERR(get_youngest(youngest_p, fs->path, pool));
ffd->youngest_rev_cache = *youngest_p;
return SVN_NO_ERROR;
}
/* Given a revision file FILE that has been pre-positioned at the
beginning of a Node-Rev header block, read in that header block and
store it in the apr_hash_t HEADERS. All allocations will be from
POOL. */
static svn_error_t * read_header_block(apr_hash_t **headers,
svn_stream_t *stream,
apr_pool_t *pool)
{
*headers = apr_hash_make(pool);
while (1)
{
svn_stringbuf_t *header_str;
const char *name, *value;
apr_size_t i = 0;
svn_boolean_t eof;
SVN_ERR(svn_stream_readline(stream, &header_str, "\n", &eof, pool));
if (eof || header_str->len == 0)
break; /* end of header block */
while (header_str->data[i] != ':')
{
if (header_str->data[i] == '\0')
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Found malformed header '%s' in "
"revision file"),
header_str->data);
i++;
}
/* Create a 'name' string and point to it. */
header_str->data[i] = '\0';
name = header_str->data;
/* Skip over the NULL byte and the space following it. */
i += 2;
if (i > header_str->len)
{
/* Restore the original line for the error. */
i -= 2;
header_str->data[i] = ':';
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Found malformed header '%s' in "
"revision file"),
header_str->data);
}
value = header_str->data + i;
/* header_str is safely in our pool, so we can use bits of it as
key and value. */
svn_hash_sets(*headers, name, value);
}
return SVN_NO_ERROR;
}
/* Return SVN_ERR_FS_NO_SUCH_REVISION if the given revision is newer
than the current youngest revision or is simply not a valid
revision number, else return success.
FSFS is based around the concept that commits only take effect when
the number in "current" is bumped. Thus if there happens to be a rev
or revprops file installed for a revision higher than the one recorded
in "current" (because a commit failed between installing the rev file
and bumping "current", or because an administrator rolled back the
repository by resetting "current" without deleting rev files, etc), it
ought to be completely ignored. This function provides the check
by which callers can make that decision. */
static svn_error_t *
ensure_revision_exists(svn_fs_t *fs,
svn_revnum_t rev,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
if (! SVN_IS_VALID_REVNUM(rev))
return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
_("Invalid revision number '%ld'"), rev);
/* Did the revision exist the last time we checked the current
file? */
if (rev <= ffd->youngest_rev_cache)
return SVN_NO_ERROR;
SVN_ERR(get_youngest(&(ffd->youngest_rev_cache), fs->path, pool));
/* Check again. */
if (rev <= ffd->youngest_rev_cache)
return SVN_NO_ERROR;
return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
_("No such revision %ld"), rev);
}
svn_error_t *
svn_fs_fs__revision_exists(svn_revnum_t rev,
svn_fs_t *fs,
apr_pool_t *pool)
{
/* Different order of parameters. */
SVN_ERR(ensure_revision_exists(fs, rev, pool));
return SVN_NO_ERROR;
}
/* Open the correct revision file for REV. If the filesystem FS has
been packed, *FILE will be set to the packed file; otherwise, set *FILE
to the revision file for REV. Return SVN_ERR_FS_NO_SUCH_REVISION if the
file doesn't exist.
TODO: Consider returning an indication of whether this is a packed rev
file, so the caller need not rely on is_packed_rev() which in turn
relies on the cached FFD->min_unpacked_rev value not having changed
since the rev file was opened.
Use POOL for allocations. */
static svn_error_t *
open_pack_or_rev_file(apr_file_t **file,
svn_fs_t *fs,
svn_revnum_t rev,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
svn_error_t *err;
const char *path;
svn_boolean_t retry = FALSE;
do
{
err = svn_fs_fs__path_rev_absolute(&path, fs, rev, pool);
/* open the revision file in buffered r/o mode */
if (! err)
err = svn_io_file_open(file, path,
APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool);
if (err && APR_STATUS_IS_ENOENT(err->apr_err))
{
if (ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT)
{
/* Could not open the file. This may happen if the
* file once existed but got packed later. */
svn_error_clear(err);
/* if that was our 2nd attempt, leave it at that. */
if (retry)
return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
_("No such revision %ld"), rev);
/* We failed for the first time. Refresh cache & retry. */
SVN_ERR(update_min_unpacked_rev(fs, pool));
retry = TRUE;
}
else
{
svn_error_clear(err);
return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
_("No such revision %ld"), rev);
}
}
else
{
retry = FALSE;
}
}
while (retry);
return svn_error_trace(err);
}
/* Reads a line from STREAM and converts it to a 64 bit integer to be
* returned in *RESULT. If we encounter eof, set *HIT_EOF and leave
* *RESULT unchanged. If HIT_EOF is NULL, EOF causes an "corrupt FS"
* error return.
* SCRATCH_POOL is used for temporary allocations.
*/
static svn_error_t *
read_number_from_stream(apr_int64_t *result,
svn_boolean_t *hit_eof,
svn_stream_t *stream,
apr_pool_t *scratch_pool)
{
svn_stringbuf_t *sb;
svn_boolean_t eof;
svn_error_t *err;
SVN_ERR(svn_stream_readline(stream, &sb, "\n", &eof, scratch_pool));
if (hit_eof)
*hit_eof = eof;
else
if (eof)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, _("Unexpected EOF"));
if (!eof)
{
err = svn_cstring_atoi64(result, sb->data);
if (err)
return svn_error_createf(SVN_ERR_FS_CORRUPT, err,
_("Number '%s' invalid or too large"),
sb->data);
}
return SVN_NO_ERROR;
}
/* Given REV in FS, set *REV_OFFSET to REV's offset in the packed file.
Use POOL for temporary allocations. */
static svn_error_t *
get_packed_offset(apr_off_t *rev_offset,
svn_fs_t *fs,
svn_revnum_t rev,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
svn_stream_t *manifest_stream;
svn_boolean_t is_cached;
svn_revnum_t shard;
apr_int64_t shard_pos;
apr_array_header_t *manifest;
apr_pool_t *iterpool;
shard = rev / ffd->max_files_per_dir;
/* position of the shard within the manifest */
shard_pos = rev % ffd->max_files_per_dir;
/* fetch exactly that element into *rev_offset, if the manifest is found
in the cache */
SVN_ERR(svn_cache__get_partial((void **) rev_offset, &is_cached,
ffd->packed_offset_cache, &shard,
svn_fs_fs__get_sharded_offset, &shard_pos,
pool));
if (is_cached)
return SVN_NO_ERROR;
/* Open the manifest file. */
SVN_ERR(svn_stream_open_readonly(&manifest_stream,
path_rev_packed(fs, rev, PATH_MANIFEST,
pool),
pool, pool));
/* While we're here, let's just read the entire manifest file into an array,
so we can cache the entire thing. */
iterpool = svn_pool_create(pool);
manifest = apr_array_make(pool, ffd->max_files_per_dir, sizeof(apr_off_t));
while (1)
{
svn_boolean_t eof;
apr_int64_t val;
svn_pool_clear(iterpool);
SVN_ERR(read_number_from_stream(&val, &eof, manifest_stream, iterpool));
if (eof)
break;
APR_ARRAY_PUSH(manifest, apr_off_t) = (apr_off_t)val;
}
svn_pool_destroy(iterpool);
*rev_offset = APR_ARRAY_IDX(manifest, rev % ffd->max_files_per_dir,
apr_off_t);
/* Close up shop and cache the array. */
SVN_ERR(svn_stream_close(manifest_stream));
return svn_cache__set(ffd->packed_offset_cache, &shard, manifest, pool);
}
/* Open the revision file for revision REV in filesystem FS and store
the newly opened file in FILE. Seek to location OFFSET before
returning. Perform temporary allocations in POOL. */
static svn_error_t *
open_and_seek_revision(apr_file_t **file,
svn_fs_t *fs,
svn_revnum_t rev,
apr_off_t offset,
apr_pool_t *pool)
{
apr_file_t *rev_file;
SVN_ERR(ensure_revision_exists(fs, rev, pool));
SVN_ERR(open_pack_or_rev_file(&rev_file, fs, rev, pool));
if (is_packed_rev(fs, rev))
{
apr_off_t rev_offset;
SVN_ERR(get_packed_offset(&rev_offset, fs, rev, pool));
offset += rev_offset;
}
SVN_ERR(svn_io_file_seek(rev_file, APR_SET, &offset, pool));
*file = rev_file;
return SVN_NO_ERROR;
}
/* Open the representation for a node-revision in transaction TXN_ID
in filesystem FS and store the newly opened file in FILE. Seek to
location OFFSET before returning. Perform temporary allocations in
POOL. Only appropriate for file contents, nor props or directory
contents. */
static svn_error_t *
open_and_seek_transaction(apr_file_t **file,
svn_fs_t *fs,
const char *txn_id,
representation_t *rep,
apr_pool_t *pool)
{
apr_file_t *rev_file;
apr_off_t offset;
SVN_ERR(svn_io_file_open(&rev_file, path_txn_proto_rev(fs, txn_id, pool),
APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool));
offset = rep->offset;
SVN_ERR(svn_io_file_seek(rev_file, APR_SET, &offset, pool));
*file = rev_file;
return SVN_NO_ERROR;
}
/* Given a node-id ID, and a representation REP in filesystem FS, open
the correct file and seek to the correction location. Store this
file in *FILE_P. Perform any allocations in POOL. */
static svn_error_t *
open_and_seek_representation(apr_file_t **file_p,
svn_fs_t *fs,
representation_t *rep,
apr_pool_t *pool)
{
if (! rep->txn_id)
return open_and_seek_revision(file_p, fs, rep->revision, rep->offset,
pool);
else
return open_and_seek_transaction(file_p, fs, rep->txn_id, rep, pool);
}
/* Parse the description of a representation from STRING and store it
into *REP_P. If the representation is mutable (the revision is
given as -1), then use TXN_ID for the representation's txn_id
field. If MUTABLE_REP_TRUNCATED is true, then this representation
is for property or directory contents, and no information will be
expected except the "-1" revision number for a mutable
representation. Allocate *REP_P in POOL. */
static svn_error_t *
read_rep_offsets_body(representation_t **rep_p,
char *string,
const char *txn_id,
svn_boolean_t mutable_rep_truncated,
apr_pool_t *pool)
{
representation_t *rep;
char *str;
apr_int64_t val;
rep = apr_pcalloc(pool, sizeof(*rep));
*rep_p = rep;
str = svn_cstring_tokenize(" ", &string);
if (str == NULL)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Malformed text representation offset line in node-rev"));
rep->revision = SVN_STR_TO_REV(str);
if (rep->revision == SVN_INVALID_REVNUM)
{
rep->txn_id = txn_id;
if (mutable_rep_truncated)
return SVN_NO_ERROR;
}
str = svn_cstring_tokenize(" ", &string);
if (str == NULL)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Malformed text representation offset line in node-rev"));
SVN_ERR(svn_cstring_atoi64(&val, str));
rep->offset = (apr_off_t)val;
str = svn_cstring_tokenize(" ", &string);
if (str == NULL)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Malformed text representation offset line in node-rev"));
SVN_ERR(svn_cstring_atoi64(&val, str));
rep->size = (svn_filesize_t)val;
str = svn_cstring_tokenize(" ", &string);
if (str == NULL)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Malformed text representation offset line in node-rev"));
SVN_ERR(svn_cstring_atoi64(&val, str));
rep->expanded_size = (svn_filesize_t)val;
/* Read in the MD5 hash. */
str = svn_cstring_tokenize(" ", &string);
if ((str == NULL) || (strlen(str) != (APR_MD5_DIGESTSIZE * 2)))
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Malformed text representation offset line in node-rev"));
SVN_ERR(svn_checksum_parse_hex(&rep->md5_checksum, svn_checksum_md5, str,
pool));
/* The remaining fields are only used for formats >= 4, so check that. */
str = svn_cstring_tokenize(" ", &string);
if (str == NULL)
return SVN_NO_ERROR;
/* Read the SHA1 hash. */
if (strlen(str) != (APR_SHA1_DIGESTSIZE * 2))
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Malformed text representation offset line in node-rev"));
SVN_ERR(svn_checksum_parse_hex(&rep->sha1_checksum, svn_checksum_sha1, str,
pool));
/* Read the uniquifier. */
str = svn_cstring_tokenize(" ", &string);
if (str == NULL)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Malformed text representation offset line in node-rev"));
rep->uniquifier = apr_pstrdup(pool, str);
return SVN_NO_ERROR;
}
/* Wrap read_rep_offsets_body(), extracting its TXN_ID from our NODEREV_ID,
and adding an error message. */
static svn_error_t *
read_rep_offsets(representation_t **rep_p,
char *string,
const svn_fs_id_t *noderev_id,
svn_boolean_t mutable_rep_truncated,
apr_pool_t *pool)
{
svn_error_t *err;
const char *txn_id;
if (noderev_id)
txn_id = svn_fs_fs__id_txn_id(noderev_id);
else
txn_id = NULL;
err = read_rep_offsets_body(rep_p, string, txn_id, mutable_rep_truncated,
pool);
if (err)
{
const svn_string_t *id_unparsed = svn_fs_fs__id_unparse(noderev_id, pool);
const char *where;
where = apr_psprintf(pool,
_("While reading representation offsets "
"for node-revision '%s':"),
noderev_id ? id_unparsed->data : "(null)");
return svn_error_quick_wrap(err, where);
}
else
return SVN_NO_ERROR;
}
static svn_error_t *
err_dangling_id(svn_fs_t *fs, const svn_fs_id_t *id)
{
svn_string_t *id_str = svn_fs_fs__id_unparse(id, fs->pool);
return svn_error_createf
(SVN_ERR_FS_ID_NOT_FOUND, 0,
_("Reference to non-existent node '%s' in filesystem '%s'"),
id_str->data, fs->path);
}
/* Look up the NODEREV_P for ID in FS' node revsion cache. If noderev
* caching has been enabled and the data can be found, IS_CACHED will
* be set to TRUE. The noderev will be allocated from POOL.
*
* Non-permanent ids (e.g. ids within a TXN) will not be cached.
*/
static svn_error_t *
get_cached_node_revision_body(node_revision_t **noderev_p,
svn_fs_t *fs,
const svn_fs_id_t *id,
svn_boolean_t *is_cached,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
if (! ffd->node_revision_cache || svn_fs_fs__id_txn_id(id))
{
*is_cached = FALSE;
}
else
{
pair_cache_key_t key = { 0 };
key.revision = svn_fs_fs__id_rev(id);
key.second = svn_fs_fs__id_offset(id);
SVN_ERR(svn_cache__get((void **) noderev_p,
is_cached,
ffd->node_revision_cache,
&key,
pool));
}
return SVN_NO_ERROR;
}
/* If noderev caching has been enabled, store the NODEREV_P for the given ID
* in FS' node revsion cache. SCRATCH_POOL is used for temporary allcations.
*
* Non-permanent ids (e.g. ids within a TXN) will not be cached.
*/
static svn_error_t *
set_cached_node_revision_body(node_revision_t *noderev_p,
svn_fs_t *fs,
const svn_fs_id_t *id,
apr_pool_t *scratch_pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
if (ffd->node_revision_cache && !svn_fs_fs__id_txn_id(id))
{
pair_cache_key_t key = { 0 };
key.revision = svn_fs_fs__id_rev(id);
key.second = svn_fs_fs__id_offset(id);
return svn_cache__set(ffd->node_revision_cache,
&key,
noderev_p,
scratch_pool);
}
return SVN_NO_ERROR;
}
/* Get the node-revision for the node ID in FS.
Set *NODEREV_P to the new node-revision structure, allocated in POOL.
See svn_fs_fs__get_node_revision, which wraps this and adds another
error. */
static svn_error_t *
get_node_revision_body(node_revision_t **noderev_p,
svn_fs_t *fs,
const svn_fs_id_t *id,
apr_pool_t *pool)
{
apr_file_t *revision_file;
svn_error_t *err;
svn_boolean_t is_cached = FALSE;
/* First, try a cache lookup. If that succeeds, we are done here. */
SVN_ERR(get_cached_node_revision_body(noderev_p, fs, id, &is_cached, pool));
if (is_cached)
return SVN_NO_ERROR;
if (svn_fs_fs__id_txn_id(id))
{
/* This is a transaction node-rev. */
err = svn_io_file_open(&revision_file, path_txn_node_rev(fs, id, pool),
APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool);
}
else
{
/* This is a revision node-rev. */
err = open_and_seek_revision(&revision_file, fs,
svn_fs_fs__id_rev(id),
svn_fs_fs__id_offset(id),
pool);
}
if (err)
{
if (APR_STATUS_IS_ENOENT(err->apr_err))
{
svn_error_clear(err);
return svn_error_trace(err_dangling_id(fs, id));
}
return svn_error_trace(err);
}
SVN_ERR(svn_fs_fs__read_noderev(noderev_p,
svn_stream_from_aprfile2(revision_file, FALSE,
pool),
pool));
/* The noderev is not in cache, yet. Add it, if caching has been enabled. */
return set_cached_node_revision_body(*noderev_p, fs, id, pool);
}
svn_error_t *
svn_fs_fs__read_noderev(node_revision_t **noderev_p,
svn_stream_t *stream,
apr_pool_t *pool)
{
apr_hash_t *headers;
node_revision_t *noderev;
char *value;
const char *noderev_id;
SVN_ERR(read_header_block(&headers, stream, pool));
noderev = apr_pcalloc(pool, sizeof(*noderev));
/* Read the node-rev id. */
value = svn_hash_gets(headers, HEADER_ID);
if (value == NULL)
/* ### More information: filename/offset coordinates */
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Missing id field in node-rev"));
SVN_ERR(svn_stream_close(stream));
noderev->id = svn_fs_fs__id_parse(value, strlen(value), pool);
noderev_id = value; /* for error messages later */
/* Read the type. */
value = svn_hash_gets(headers, HEADER_TYPE);
if ((value == NULL) ||
(strcmp(value, KIND_FILE) != 0 && strcmp(value, KIND_DIR)))
/* ### s/kind/type/ */
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Missing kind field in node-rev '%s'"),
noderev_id);
noderev->kind = (strcmp(value, KIND_FILE) == 0) ? svn_node_file
: svn_node_dir;
/* Read the 'count' field. */
value = svn_hash_gets(headers, HEADER_COUNT);
if (value)
SVN_ERR(svn_cstring_atoi(&noderev->predecessor_count, value));
else
noderev->predecessor_count = 0;
/* Get the properties location. */
value = svn_hash_gets(headers, HEADER_PROPS);
if (value)
{
SVN_ERR(read_rep_offsets(&noderev->prop_rep, value,
noderev->id, TRUE, pool));
}
/* Get the data location. */
value = svn_hash_gets(headers, HEADER_TEXT);
if (value)
{
SVN_ERR(read_rep_offsets(&noderev->data_rep, value,
noderev->id,
(noderev->kind == svn_node_dir), pool));
}
/* Get the created path. */
value = svn_hash_gets(headers, HEADER_CPATH);
if (value == NULL)
{
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Missing cpath field in node-rev '%s'"),
noderev_id);
}
else
{
noderev->created_path = apr_pstrdup(pool, value);
}
/* Get the predecessor ID. */
value = svn_hash_gets(headers, HEADER_PRED);
if (value)
noderev->predecessor_id = svn_fs_fs__id_parse(value, strlen(value),
pool);
/* Get the copyroot. */
value = svn_hash_gets(headers, HEADER_COPYROOT);
if (value == NULL)
{
noderev->copyroot_path = apr_pstrdup(pool, noderev->created_path);
noderev->copyroot_rev = svn_fs_fs__id_rev(noderev->id);
}
else
{
char *str;
str = svn_cstring_tokenize(" ", &value);
if (str == NULL)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Malformed copyroot line in node-rev '%s'"),
noderev_id);
noderev->copyroot_rev = SVN_STR_TO_REV(str);
if (*value == '\0')
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Malformed copyroot line in node-rev '%s'"),
noderev_id);
noderev->copyroot_path = apr_pstrdup(pool, value);
}
/* Get the copyfrom. */
value = svn_hash_gets(headers, HEADER_COPYFROM);
if (value == NULL)
{
noderev->copyfrom_path = NULL;
noderev->copyfrom_rev = SVN_INVALID_REVNUM;
}
else
{
char *str = svn_cstring_tokenize(" ", &value);
if (str == NULL)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Malformed copyfrom line in node-rev '%s'"),
noderev_id);
noderev->copyfrom_rev = SVN_STR_TO_REV(str);
if (*value == 0)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Malformed copyfrom line in node-rev '%s'"),
noderev_id);
noderev->copyfrom_path = apr_pstrdup(pool, value);
}
/* Get whether this is a fresh txn root. */
value = svn_hash_gets(headers, HEADER_FRESHTXNRT);
noderev->is_fresh_txn_root = (value != NULL);
/* Get the mergeinfo count. */
value = svn_hash_gets(headers, HEADER_MINFO_CNT);
if (value)
SVN_ERR(svn_cstring_atoi64(&noderev->mergeinfo_count, value));
else
noderev->mergeinfo_count = 0;
/* Get whether *this* node has mergeinfo. */
value = svn_hash_gets(headers, HEADER_MINFO_HERE);
noderev->has_mergeinfo = (value != NULL);
*noderev_p = noderev;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__get_node_revision(node_revision_t **noderev_p,
svn_fs_t *fs,
const svn_fs_id_t *id,
apr_pool_t *pool)
{
svn_error_t *err = get_node_revision_body(noderev_p, fs, id, pool);
if (err && err->apr_err == SVN_ERR_FS_CORRUPT)
{
svn_string_t *id_string = svn_fs_fs__id_unparse(id, pool);
return svn_error_createf(SVN_ERR_FS_CORRUPT, err,
"Corrupt node-revision '%s'",
id_string->data);
}
return svn_error_trace(err);
}
/* Return a formatted string, compatible with filesystem format FORMAT,
that represents the location of representation REP. If
MUTABLE_REP_TRUNCATED is given, the rep is for props or dir contents,
and only a "-1" revision number will be given for a mutable rep.
If MAY_BE_CORRUPT is true, guard for NULL when constructing the string.
Perform the allocation from POOL. */
static const char *
representation_string(representation_t *rep,
int format,
svn_boolean_t mutable_rep_truncated,
svn_boolean_t may_be_corrupt,
apr_pool_t *pool)
{
if (rep->txn_id && mutable_rep_truncated)
return "-1";
#define DISPLAY_MAYBE_NULL_CHECKSUM(checksum) \
((!may_be_corrupt || (checksum) != NULL) \
? svn_checksum_to_cstring_display((checksum), pool) \
: "(null)")
if (format < SVN_FS_FS__MIN_REP_SHARING_FORMAT || rep->sha1_checksum == NULL)
return apr_psprintf(pool, "%ld %" APR_OFF_T_FMT " %" SVN_FILESIZE_T_FMT
" %" SVN_FILESIZE_T_FMT " %s",
rep->revision, rep->offset, rep->size,
rep->expanded_size,
DISPLAY_MAYBE_NULL_CHECKSUM(rep->md5_checksum));
return apr_psprintf(pool, "%ld %" APR_OFF_T_FMT " %" SVN_FILESIZE_T_FMT
" %" SVN_FILESIZE_T_FMT " %s %s %s",
rep->revision, rep->offset, rep->size,
rep->expanded_size,
DISPLAY_MAYBE_NULL_CHECKSUM(rep->md5_checksum),
DISPLAY_MAYBE_NULL_CHECKSUM(rep->sha1_checksum),
rep->uniquifier);
#undef DISPLAY_MAYBE_NULL_CHECKSUM
}
svn_error_t *
svn_fs_fs__write_noderev(svn_stream_t *outfile,
node_revision_t *noderev,
int format,
svn_boolean_t include_mergeinfo,
apr_pool_t *pool)
{
SVN_ERR(svn_stream_printf(outfile, pool, HEADER_ID ": %s\n",
svn_fs_fs__id_unparse(noderev->id,
pool)->data));
SVN_ERR(svn_stream_printf(outfile, pool, HEADER_TYPE ": %s\n",
(noderev->kind == svn_node_file) ?
KIND_FILE : KIND_DIR));
if (noderev->predecessor_id)
SVN_ERR(svn_stream_printf(outfile, pool, HEADER_PRED ": %s\n",
svn_fs_fs__id_unparse(noderev->predecessor_id,
pool)->data));
SVN_ERR(svn_stream_printf(outfile, pool, HEADER_COUNT ": %d\n",
noderev->predecessor_count));
if (noderev->data_rep)
SVN_ERR(svn_stream_printf(outfile, pool, HEADER_TEXT ": %s\n",
representation_string(noderev->data_rep,
format,
(noderev->kind
== svn_node_dir),
FALSE,
pool)));
if (noderev->prop_rep)
SVN_ERR(svn_stream_printf(outfile, pool, HEADER_PROPS ": %s\n",
representation_string(noderev->prop_rep, format,
TRUE, FALSE, pool)));
SVN_ERR(svn_stream_printf(outfile, pool, HEADER_CPATH ": %s\n",
noderev->created_path));
if (noderev->copyfrom_path)
SVN_ERR(svn_stream_printf(outfile, pool, HEADER_COPYFROM ": %ld"
" %s\n",
noderev->copyfrom_rev,
noderev->copyfrom_path));
if ((noderev->copyroot_rev != svn_fs_fs__id_rev(noderev->id)) ||
(strcmp(noderev->copyroot_path, noderev->created_path) != 0))
SVN_ERR(svn_stream_printf(outfile, pool, HEADER_COPYROOT ": %ld"
" %s\n",
noderev->copyroot_rev,
noderev->copyroot_path));
if (noderev->is_fresh_txn_root)
SVN_ERR(svn_stream_puts(outfile, HEADER_FRESHTXNRT ": y\n"));
if (include_mergeinfo)
{
if (noderev->mergeinfo_count > 0)
SVN_ERR(svn_stream_printf(outfile, pool, HEADER_MINFO_CNT ": %"
APR_INT64_T_FMT "\n",
noderev->mergeinfo_count));
if (noderev->has_mergeinfo)
SVN_ERR(svn_stream_puts(outfile, HEADER_MINFO_HERE ": y\n"));
}
return svn_stream_puts(outfile, "\n");
}
svn_error_t *
svn_fs_fs__put_node_revision(svn_fs_t *fs,
const svn_fs_id_t *id,
node_revision_t *noderev,
svn_boolean_t fresh_txn_root,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
apr_file_t *noderev_file;
const char *txn_id = svn_fs_fs__id_txn_id(id);
noderev->is_fresh_txn_root = fresh_txn_root;
if (! txn_id)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Attempted to write to non-transaction '%s'"),
svn_fs_fs__id_unparse(id, pool)->data);
SVN_ERR(svn_io_file_open(&noderev_file, path_txn_node_rev(fs, id, pool),
APR_WRITE | APR_CREATE | APR_TRUNCATE
| APR_BUFFERED, APR_OS_DEFAULT, pool));
SVN_ERR(svn_fs_fs__write_noderev(svn_stream_from_aprfile2(noderev_file, TRUE,
pool),
noderev, ffd->format,
svn_fs_fs__fs_supports_mergeinfo(fs),
pool));
SVN_ERR(svn_io_file_close(noderev_file, pool));
return SVN_NO_ERROR;
}
/* For the in-transaction NODEREV within FS, write the sha1->rep mapping
* file in the respective transaction, if rep sharing has been enabled etc.
* Use POOL for temporary allocations.
*/
static svn_error_t *
store_sha1_rep_mapping(svn_fs_t *fs,
node_revision_t *noderev,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
/* if rep sharing has been enabled and the noderev has a data rep and
* its SHA-1 is known, store the rep struct under its SHA1. */
if ( ffd->rep_sharing_allowed
&& noderev->data_rep
&& noderev->data_rep->sha1_checksum)
{
apr_file_t *rep_file;
const char *file_name = path_txn_sha1(fs,
svn_fs_fs__id_txn_id(noderev->id),
noderev->data_rep->sha1_checksum,
pool);
const char *rep_string = representation_string(noderev->data_rep,
ffd->format,
(noderev->kind
== svn_node_dir),
FALSE,
pool);
SVN_ERR(svn_io_file_open(&rep_file, file_name,
APR_WRITE | APR_CREATE | APR_TRUNCATE
| APR_BUFFERED, APR_OS_DEFAULT, pool));
SVN_ERR(svn_io_file_write_full(rep_file, rep_string,
strlen(rep_string), NULL, pool));
SVN_ERR(svn_io_file_close(rep_file, pool));
}
return SVN_NO_ERROR;
}
/* This structure is used to hold the information associated with a
REP line. */
struct rep_args
{
svn_boolean_t is_delta;
svn_boolean_t is_delta_vs_empty;
svn_revnum_t base_revision;
apr_off_t base_offset;
svn_filesize_t base_length;
};
/* Read the next line from file FILE and parse it as a text
representation entry. Return the parsed entry in *REP_ARGS_P.
Perform all allocations in POOL. */
static svn_error_t *
read_rep_line(struct rep_args **rep_args_p,
apr_file_t *file,
apr_pool_t *pool)
{
char buffer[160];
apr_size_t limit;
struct rep_args *rep_args;
char *str, *last_str = buffer;
apr_int64_t val;
limit = sizeof(buffer);
SVN_ERR(svn_io_read_length_line(file, buffer, &limit, pool));
rep_args = apr_pcalloc(pool, sizeof(*rep_args));
rep_args->is_delta = FALSE;
if (strcmp(buffer, REP_PLAIN) == 0)
{
*rep_args_p = rep_args;
return SVN_NO_ERROR;
}
if (strcmp(buffer, REP_DELTA) == 0)
{
/* This is a delta against the empty stream. */
rep_args->is_delta = TRUE;
rep_args->is_delta_vs_empty = TRUE;
*rep_args_p = rep_args;
return SVN_NO_ERROR;
}
rep_args->is_delta = TRUE;
rep_args->is_delta_vs_empty = FALSE;
/* We have hopefully a DELTA vs. a non-empty base revision. */
str = svn_cstring_tokenize(" ", &last_str);
if (! str || (strcmp(str, REP_DELTA) != 0))
goto error;
str = svn_cstring_tokenize(" ", &last_str);
if (! str)
goto error;
rep_args->base_revision = SVN_STR_TO_REV(str);
str = svn_cstring_tokenize(" ", &last_str);
if (! str)
goto error;
SVN_ERR(svn_cstring_atoi64(&val, str));
rep_args->base_offset = (apr_off_t)val;
str = svn_cstring_tokenize(" ", &last_str);
if (! str)
goto error;
SVN_ERR(svn_cstring_atoi64(&val, str));
rep_args->base_length = (svn_filesize_t)val;
*rep_args_p = rep_args;
return SVN_NO_ERROR;
error:
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Malformed representation header at %s"),
path_and_offset_of(file, pool));
}
/* Given a revision file REV_FILE, opened to REV in FS, find the Node-ID
of the header located at OFFSET and store it in *ID_P. Allocate
temporary variables from POOL. */
static svn_error_t *
get_fs_id_at_offset(svn_fs_id_t **id_p,
apr_file_t *rev_file,
svn_fs_t *fs,
svn_revnum_t rev,
apr_off_t offset,
apr_pool_t *pool)
{
svn_fs_id_t *id;
apr_hash_t *headers;
const char *node_id_str;
SVN_ERR(svn_io_file_seek(rev_file, APR_SET, &offset, pool));
SVN_ERR(read_header_block(&headers,
svn_stream_from_aprfile2(rev_file, TRUE, pool),
pool));
/* In error messages, the offset is relative to the pack file,
not to the rev file. */
node_id_str = svn_hash_gets(headers, HEADER_ID);
if (node_id_str == NULL)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Missing node-id in node-rev at r%ld "
"(offset %s)"),
rev,
apr_psprintf(pool, "%" APR_OFF_T_FMT, offset));
id = svn_fs_fs__id_parse(node_id_str, strlen(node_id_str), pool);
if (id == NULL)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Corrupt node-id '%s' in node-rev at r%ld "
"(offset %s)"),
node_id_str, rev,
apr_psprintf(pool, "%" APR_OFF_T_FMT, offset));
*id_p = id;
/* ### assert that the txn_id is REV/OFFSET ? */
return SVN_NO_ERROR;
}
/* Given an open revision file REV_FILE in FS for REV, locate the trailer that
specifies the offset to the root node-id and to the changed path
information. Store the root node offset in *ROOT_OFFSET and the
changed path offset in *CHANGES_OFFSET. If either of these
pointers is NULL, do nothing with it.
If PACKED is true, REV_FILE should be a packed shard file.
### There is currently no such parameter. This function assumes that
is_packed_rev(FS, REV) will indicate whether REV_FILE is a packed
file. Therefore FS->fsap_data->min_unpacked_rev must not have been
refreshed since REV_FILE was opened if there is a possibility that
revision REV may have become packed since then.
TODO: Take an IS_PACKED parameter instead, in order to remove this
requirement.
Allocate temporary variables from POOL. */
static svn_error_t *
get_root_changes_offset(apr_off_t *root_offset,
apr_off_t *changes_offset,
apr_file_t *rev_file,
svn_fs_t *fs,
svn_revnum_t rev,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
apr_off_t offset;
apr_off_t rev_offset;
char buf[64];
int i, num_bytes;
const char *str;
apr_size_t len;
apr_seek_where_t seek_relative;
/* Determine where to seek to in the file.
If we've got a pack file, we want to seek to the end of the desired
revision. But we don't track that, so we seek to the beginning of the
next revision.
Unless the next revision is in a different file, in which case, we can
just seek to the end of the pack file -- just like we do in the
non-packed case. */
if (is_packed_rev(fs, rev) && ((rev + 1) % ffd->max_files_per_dir != 0))
{
SVN_ERR(get_packed_offset(&offset, fs, rev + 1, pool));
seek_relative = APR_SET;
}
else
{
seek_relative = APR_END;
offset = 0;
}
/* Offset of the revision from the start of the pack file, if applicable. */
if (is_packed_rev(fs, rev))
SVN_ERR(get_packed_offset(&rev_offset, fs, rev, pool));
else
rev_offset = 0;
/* We will assume that the last line containing the two offsets
will never be longer than 64 characters. */
SVN_ERR(svn_io_file_seek(rev_file, seek_relative, &offset, pool));
offset -= sizeof(buf);
SVN_ERR(svn_io_file_seek(rev_file, APR_SET, &offset, pool));
/* Read in this last block, from which we will identify the last line. */
len = sizeof(buf);
SVN_ERR(svn_io_file_read(rev_file, buf, &len, pool));
/* This cast should be safe since the maximum amount read, 64, will
never be bigger than the size of an int. */
num_bytes = (int) len;
/* The last byte should be a newline. */
if (buf[num_bytes - 1] != '\n')
{
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Revision file (r%ld) lacks trailing newline"),
rev);
}
/* Look for the next previous newline. */
for (i = num_bytes - 2; i >= 0; i--)
{
if (buf[i] == '\n')
break;
}
if (i < 0)
{
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Final line in revision file (r%ld) longer "
"than 64 characters"),
rev);
}
i++;
str = &buf[i];
/* find the next space */
for ( ; i < (num_bytes - 2) ; i++)
if (buf[i] == ' ')
break;
if (i == (num_bytes - 2))
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Final line in revision file r%ld missing space"),
rev);
if (root_offset)
{
apr_int64_t val;
buf[i] = '\0';
SVN_ERR(svn_cstring_atoi64(&val, str));
*root_offset = rev_offset + (apr_off_t)val;
}
i++;
str = &buf[i];
/* find the next newline */
for ( ; i < num_bytes; i++)
if (buf[i] == '\n')
break;
if (changes_offset)
{
apr_int64_t val;
buf[i] = '\0';
SVN_ERR(svn_cstring_atoi64(&val, str));
*changes_offset = rev_offset + (apr_off_t)val;
}
return SVN_NO_ERROR;
}
/* Move a file into place from OLD_FILENAME in the transactions
directory to its final location NEW_FILENAME in the repository. On
Unix, match the permissions of the new file to the permissions of
PERMS_REFERENCE. Temporary allocations are from POOL.
This function almost duplicates svn_io_file_move(), but it tries to
guarantee a flush. */
static svn_error_t *
move_into_place(const char *old_filename,
const char *new_filename,
const char *perms_reference,
apr_pool_t *pool)
{
svn_error_t *err;
SVN_ERR(svn_io_copy_perms(perms_reference, old_filename, pool));
/* Move the file into place. */
err = svn_io_file_rename(old_filename, new_filename, pool);
if (err && APR_STATUS_IS_EXDEV(err->apr_err))
{
apr_file_t *file;
/* Can't rename across devices; fall back to copying. */
svn_error_clear(err);
err = SVN_NO_ERROR;
SVN_ERR(svn_io_copy_file(old_filename, new_filename, TRUE, pool));
/* Flush the target of the copy to disk. */
SVN_ERR(svn_io_file_open(&file, new_filename, APR_READ,
APR_OS_DEFAULT, pool));
/* ### BH: Does this really guarantee a flush of the data written
### via a completely different handle on all operating systems?
###
### Maybe we should perform the copy ourselves instead of making
### apr do that and flush the real handle? */
SVN_ERR(svn_io_file_flush_to_disk(file, pool));
SVN_ERR(svn_io_file_close(file, pool));
}
if (err)
return svn_error_trace(err);
#ifdef __linux__
{
/* Linux has the unusual feature that fsync() on a file is not
enough to ensure that a file's directory entries have been
flushed to disk; you have to fsync the directory as well.
On other operating systems, we'd only be asking for trouble
by trying to open and fsync a directory. */
const char *dirname;
apr_file_t *file;
dirname = svn_dirent_dirname(new_filename, pool);
SVN_ERR(svn_io_file_open(&file, dirname, APR_READ, APR_OS_DEFAULT,
pool));
SVN_ERR(svn_io_file_flush_to_disk(file, pool));
SVN_ERR(svn_io_file_close(file, pool));
}
#endif
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__rev_get_root(svn_fs_id_t **root_id_p,
svn_fs_t *fs,
svn_revnum_t rev,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
apr_file_t *revision_file;
apr_off_t root_offset;
svn_fs_id_t *root_id = NULL;
svn_boolean_t is_cached;
SVN_ERR(ensure_revision_exists(fs, rev, pool));
SVN_ERR(svn_cache__get((void **) root_id_p, &is_cached,
ffd->rev_root_id_cache, &rev, pool));
if (is_cached)
return SVN_NO_ERROR;
SVN_ERR(open_pack_or_rev_file(&revision_file, fs, rev, pool));
SVN_ERR(get_root_changes_offset(&root_offset, NULL, revision_file, fs, rev,
pool));
SVN_ERR(get_fs_id_at_offset(&root_id, revision_file, fs, rev,
root_offset, pool));
SVN_ERR(svn_io_file_close(revision_file, pool));
SVN_ERR(svn_cache__set(ffd->rev_root_id_cache, &rev, root_id, pool));
*root_id_p = root_id;
return SVN_NO_ERROR;
}
/* Revprop caching management.
*
* Mechanism:
* ----------
*
* Revprop caching needs to be activated and will be deactivated for the
* respective FS instance if the necessary infrastructure could not be
* initialized. In deactivated mode, there is almost no runtime overhead
* associated with revprop caching. As long as no revprops are being read
* or changed, revprop caching imposes no overhead.
*
* When activated, we cache revprops using (revision, generation) pairs
* as keys with the generation being incremented upon every revprop change.
* Since the cache is process-local, the generation needs to be tracked
* for at least as long as the process lives but may be reset afterwards.
*
* To track the revprop generation, we use two-layer approach. On the lower
* level, we use named atomics to have a system-wide consistent value for
* the current revprop generation. However, those named atomics will only
* remain valid for as long as at least one process / thread in the system
* accesses revprops in the respective repository. The underlying shared
* memory gets cleaned up afterwards.
*
* On the second level, we will use a persistent file to track the latest
* revprop generation. It will be written upon each revprop change but
* only be read if we are the first process to initialize the named atomics
* with that value.
*
* The overhead for the second and following accesses to revprops is
* almost zero on most systems.
*
*
* Tech aspects:
* -------------
*
* A problem is that we need to provide a globally available file name to
* back the SHM implementation on OSes that need it. We can only assume
* write access to some file within the respective repositories. Because
* a given server process may access thousands of repositories during its
* lifetime, keeping the SHM data alive for all of them is also not an
* option.
*
* So, we store the new revprop generation on disk as part of each
* setrevprop call, i.e. this write will be serialized and the write order
* be guaranteed by the repository write lock.
*
* The only racy situation occurs when the data is being read again by two
* processes concurrently but in that situation, the first process to
* finish that procedure is guaranteed to be the only one that initializes
* the SHM data. Since even writers will first go through that
* initialization phase, they will never operate on stale data.
*/
/* Read revprop generation as stored on disk for repository FS. The result
* is returned in *CURRENT. Default to 2 if no such file is available.
*/
static svn_error_t *
read_revprop_generation_file(apr_int64_t *current,
svn_fs_t *fs,
apr_pool_t *pool)
{
svn_error_t *err;
apr_file_t *file;
char buf[80];
apr_size_t len;
const char *path = path_revprop_generation(fs, pool);
err = svn_io_file_open(&file, path,
APR_READ | APR_BUFFERED,
APR_OS_DEFAULT, pool);
if (err && APR_STATUS_IS_ENOENT(err->apr_err))
{
svn_error_clear(err);
*current = 2;
return SVN_NO_ERROR;
}
SVN_ERR(err);
len = sizeof(buf);
SVN_ERR(svn_io_read_length_line(file, buf, &len, pool));
/* Check that the first line contains only digits. */
SVN_ERR(check_file_buffer_numeric(buf, 0, path,
"Revprop Generation", pool));
SVN_ERR(svn_cstring_atoi64(current, buf));
return svn_io_file_close(file, pool);
}
/* Write the CURRENT revprop generation to disk for repository FS.
*/
static svn_error_t *
write_revprop_generation_file(svn_fs_t *fs,
apr_int64_t current,
apr_pool_t *pool)
{
apr_file_t *file;
const char *tmp_path;
char buf[SVN_INT64_BUFFER_SIZE];
apr_size_t len = svn__i64toa(buf, current);
buf[len] = '\n';
SVN_ERR(svn_io_open_unique_file3(&file, &tmp_path, fs->path,
svn_io_file_del_none, pool, pool));
SVN_ERR(svn_io_file_write_full(file, buf, len + 1, NULL, pool));
SVN_ERR(svn_io_file_close(file, pool));
return move_into_place(tmp_path, path_revprop_generation(fs, pool),
tmp_path, pool);
}
/* Make sure the revprop_namespace member in FS is set. */
static svn_error_t *
ensure_revprop_namespace(svn_fs_t *fs)
{
fs_fs_data_t *ffd = fs->fsap_data;
return ffd->revprop_namespace == NULL
? svn_atomic_namespace__create(&ffd->revprop_namespace,
svn_dirent_join(fs->path,
ATOMIC_REVPROP_NAMESPACE,
fs->pool),
fs->pool)
: SVN_NO_ERROR;
}
/* Make sure the revprop_namespace member in FS is set. */
static svn_error_t *
cleanup_revprop_namespace(svn_fs_t *fs)
{
const char *name = svn_dirent_join(fs->path,
ATOMIC_REVPROP_NAMESPACE,
fs->pool);
return svn_error_trace(svn_atomic_namespace__cleanup(name, fs->pool));
}
/* Make sure the revprop_generation member in FS is set and, if necessary,
* initialized with the latest value stored on disk.
*/
static svn_error_t *
ensure_revprop_generation(svn_fs_t *fs, apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
SVN_ERR(ensure_revprop_namespace(fs));
if (ffd->revprop_generation == NULL)
{
apr_int64_t current = 0;
SVN_ERR(svn_named_atomic__get(&ffd->revprop_generation,
ffd->revprop_namespace,
ATOMIC_REVPROP_GENERATION,
TRUE));
/* If the generation is at 0, we just created a new namespace
* (it would be at least 2 otherwise). Read the latest generation
* from disk and if we are the first one to initialize the atomic
* (i.e. is still 0), set it to the value just gotten.
*/
SVN_ERR(svn_named_atomic__read(&current, ffd->revprop_generation));
if (current == 0)
{
SVN_ERR(read_revprop_generation_file(&current, fs, pool));
SVN_ERR(svn_named_atomic__cmpxchg(NULL, current, 0,
ffd->revprop_generation));
}
}
return SVN_NO_ERROR;
}
/* Make sure the revprop_timeout member in FS is set. */
static svn_error_t *
ensure_revprop_timeout(svn_fs_t *fs)
{
fs_fs_data_t *ffd = fs->fsap_data;
SVN_ERR(ensure_revprop_namespace(fs));
return ffd->revprop_timeout == NULL
? svn_named_atomic__get(&ffd->revprop_timeout,
ffd->revprop_namespace,
ATOMIC_REVPROP_TIMEOUT,
TRUE)
: SVN_NO_ERROR;
}
/* Create an error object with the given MESSAGE and pass it to the
WARNING member of FS. */
static void
log_revprop_cache_init_warning(svn_fs_t *fs,
svn_error_t *underlying_err,
const char *message)
{
svn_error_t *err = svn_error_createf(SVN_ERR_FS_REVPROP_CACHE_INIT_FAILURE,
underlying_err,
message, fs->path);
if (fs->warning)
(fs->warning)(fs->warning_baton, err);
svn_error_clear(err);
}
/* Test whether revprop cache and necessary infrastructure are
available in FS. */
static svn_boolean_t
has_revprop_cache(svn_fs_t *fs, apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
svn_error_t *error;
/* is the cache (still) enabled? */
if (ffd->revprop_cache == NULL)
return FALSE;
/* is it efficient? */
if (!svn_named_atomic__is_efficient())
{
/* access to it would be quite slow
* -> disable the revprop cache for good
*/
ffd->revprop_cache = NULL;
log_revprop_cache_init_warning(fs, NULL,
"Revprop caching for '%s' disabled"
" because it would be inefficient.");
return FALSE;
}
/* try to access our SHM-backed infrastructure */
error = ensure_revprop_generation(fs, pool);
if (error)
{
/* failure -> disable revprop cache for good */
ffd->revprop_cache = NULL;
log_revprop_cache_init_warning(fs, error,
"Revprop caching for '%s' disabled "
"because SHM infrastructure for revprop "
"caching failed to initialize.");
return FALSE;
}
return TRUE;
}
/* Baton structure for revprop_generation_fixup. */
typedef struct revprop_generation_fixup_t
{
/* revprop generation to read */
apr_int64_t *generation;
/* containing the revprop_generation member to query */
fs_fs_data_t *ffd;
} revprop_generation_upgrade_t;
/* If the revprop generation has an odd value, it means the original writer
of the revprop got killed. We don't know whether that process as able
to change the revprop data but we assume that it was. Therefore, we
increase the generation in that case to basically invalidate everyones
cache content.
Execute this onlx while holding the write lock to the repo in baton->FFD.
*/
static svn_error_t *
revprop_generation_fixup(void *void_baton,
apr_pool_t *pool)
{
revprop_generation_upgrade_t *baton = void_baton;
assert(baton->ffd->has_write_lock);
/* Maybe, either the original revprop writer or some other reader has
already corrected / bumped the revprop generation. Thus, we need
to read it again. */
SVN_ERR(svn_named_atomic__read(baton->generation,
baton->ffd->revprop_generation));
/* Cause everyone to re-read revprops upon their next access, if the
last revprop write did not complete properly. */
while (*baton->generation % 2)
SVN_ERR(svn_named_atomic__add(baton->generation,
1,
baton->ffd->revprop_generation));
return SVN_NO_ERROR;
}
/* Read the current revprop generation and return it in *GENERATION.
Also, detect aborted / crashed writers and recover from that.
Use the access object in FS to set the shared mem values. */
static svn_error_t *
read_revprop_generation(apr_int64_t *generation,
svn_fs_t *fs,
apr_pool_t *pool)
{
apr_int64_t current = 0;
fs_fs_data_t *ffd = fs->fsap_data;
/* read the current revprop generation number */
SVN_ERR(ensure_revprop_generation(fs, pool));
SVN_ERR(svn_named_atomic__read(&current, ffd->revprop_generation));
/* is an unfinished revprop write under the way? */
if (current % 2)
{
apr_int64_t timeout = 0;
/* read timeout for the write operation */
SVN_ERR(ensure_revprop_timeout(fs));
SVN_ERR(svn_named_atomic__read(&timeout, ffd->revprop_timeout));
/* has the writer process been aborted,
* i.e. has the timeout been reached?
*/
if (apr_time_now() > timeout)
{
revprop_generation_upgrade_t baton;
baton.generation = &current;
baton.ffd = ffd;
/* Ensure that the original writer process no longer exists by
* acquiring the write lock to this repository. Then, fix up
* the revprop generation.
*/
if (ffd->has_write_lock)
SVN_ERR(revprop_generation_fixup(&baton, pool));
else
SVN_ERR(svn_fs_fs__with_write_lock(fs, revprop_generation_fixup,
&baton, pool));
}
}
/* return the value we just got */
*generation = current;
return SVN_NO_ERROR;
}
/* Set the revprop generation to the next odd number to indicate that
there is a revprop write process under way. If that times out,
readers shall recover from that state & re-read revprops.
Use the access object in FS to set the shared mem value. */
static svn_error_t *
begin_revprop_change(svn_fs_t *fs, apr_pool_t *pool)
{
apr_int64_t current;
fs_fs_data_t *ffd = fs->fsap_data;
/* set the timeout for the write operation */
SVN_ERR(ensure_revprop_timeout(fs));
SVN_ERR(svn_named_atomic__write(NULL,
apr_time_now() + REVPROP_CHANGE_TIMEOUT,
ffd->revprop_timeout));
/* set the revprop generation to an odd value to indicate
* that a write is in progress
*/
SVN_ERR(ensure_revprop_generation(fs, pool));
do
{
SVN_ERR(svn_named_atomic__add(&current,
1,
ffd->revprop_generation));
}
while (current % 2 == 0);
return SVN_NO_ERROR;
}
/* Set the revprop generation to the next even number to indicate that
a) readers shall re-read revprops, and
b) the write process has been completed (no recovery required)
Use the access object in FS to set the shared mem value. */
static svn_error_t *
end_revprop_change(svn_fs_t *fs, apr_pool_t *pool)
{
apr_int64_t current = 1;
fs_fs_data_t *ffd = fs->fsap_data;
/* set the revprop generation to an even value to indicate
* that a write has been completed
*/
SVN_ERR(ensure_revprop_generation(fs, pool));
do
{
SVN_ERR(svn_named_atomic__add(&current,
1,
ffd->revprop_generation));
}
while (current % 2);
/* Save the latest generation to disk. FS is currently in a "locked"
* state such that we can be sure the be the only ones to write that
* file.
*/
return write_revprop_generation_file(fs, current, pool);
}
/* Container for all data required to access the packed revprop file
* for a given REVISION. This structure will be filled incrementally
* by read_pack_revprops() its sub-routines.
*/
typedef struct packed_revprops_t
{
/* revision number to read (not necessarily the first in the pack) */
svn_revnum_t revision;
/* current revprop generation. Used when populating the revprop cache */
apr_int64_t generation;
/* the actual revision properties */
apr_hash_t *properties;
/* their size when serialized to a single string
* (as found in PACKED_REVPROPS) */
apr_size_t serialized_size;
/* name of the pack file (without folder path) */
const char *filename;
/* packed shard folder path */
const char *folder;
/* sum of values in SIZES */
apr_size_t total_size;
/* first revision in the pack (>= MANIFEST_START) */
svn_revnum_t start_revision;
/* size of the revprops in PACKED_REVPROPS */
apr_array_header_t *sizes;
/* offset of the revprops in PACKED_REVPROPS */
apr_array_header_t *offsets;
/* concatenation of the serialized representation of all revprops
* in the pack, i.e. the pack content without header and compression */
svn_stringbuf_t *packed_revprops;
/* First revision covered by MANIFEST.
* Will equal the shard start revision or 1, for the 1st shard. */
svn_revnum_t manifest_start;
/* content of the manifest.
* Maps long(rev - MANIFEST_START) to const char* pack file name */
apr_array_header_t *manifest;
} packed_revprops_t;
/* Parse the serialized revprops in CONTENT and return them in *PROPERTIES.
* Also, put them into the revprop cache, if activated, for future use.
* Three more parameters are being used to update the revprop cache: FS is
* our file system, the revprops belong to REVISION and the global revprop
* GENERATION is used as well.
*
* The returned hash will be allocated in POOL, SCRATCH_POOL is being used
* for temporary allocations.
*/
static svn_error_t *
parse_revprop(apr_hash_t **properties,
svn_fs_t *fs,
svn_revnum_t revision,
apr_int64_t generation,
svn_string_t *content,
apr_pool_t *pool,
apr_pool_t *scratch_pool)
{
svn_stream_t *stream = svn_stream_from_string(content, scratch_pool);
*properties = apr_hash_make(pool);
SVN_ERR(svn_hash_read2(*properties, stream, SVN_HASH_TERMINATOR, pool));
if (has_revprop_cache(fs, pool))
{
fs_fs_data_t *ffd = fs->fsap_data;
pair_cache_key_t key = { 0 };
key.revision = revision;
key.second = generation;
SVN_ERR(svn_cache__set(ffd->revprop_cache, &key, *properties,
scratch_pool));
}
return SVN_NO_ERROR;
}
/* Read the non-packed revprops for revision REV in FS, put them into the
* revprop cache if activated and return them in *PROPERTIES. GENERATION
* is the current revprop generation.
*
* If the data could not be read due to an otherwise recoverable error,
* leave *PROPERTIES unchanged. No error will be returned in that case.
*
* Allocations will be done in POOL.
*/
static svn_error_t *
read_non_packed_revprop(apr_hash_t **properties,
svn_fs_t *fs,
svn_revnum_t rev,
apr_int64_t generation,
apr_pool_t *pool)
{
svn_stringbuf_t *content = NULL;
apr_pool_t *iterpool = svn_pool_create(pool);
svn_boolean_t missing = FALSE;
int i;
for (i = 0; i < RECOVERABLE_RETRY_COUNT && !missing && !content; ++i)
{
svn_pool_clear(iterpool);
SVN_ERR(try_stringbuf_from_file(&content,
&missing,
path_revprops(fs, rev, iterpool),
i + 1 < RECOVERABLE_RETRY_COUNT,
iterpool));
}
if (content)
SVN_ERR(parse_revprop(properties, fs, rev, generation,
svn_stringbuf__morph_into_string(content),
pool, iterpool));
svn_pool_clear(iterpool);
return SVN_NO_ERROR;
}
/* Given FS and REVPROPS->REVISION, fill the FILENAME, FOLDER and MANIFEST
* members. Use POOL for allocating results and SCRATCH_POOL for temporaries.
*/
static svn_error_t *
get_revprop_packname(svn_fs_t *fs,
packed_revprops_t *revprops,
apr_pool_t *pool,
apr_pool_t *scratch_pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
svn_stringbuf_t *content = NULL;
const char *manifest_file_path;
int idx;
/* read content of the manifest file */
revprops->folder = path_revprops_pack_shard(fs, revprops->revision, pool);
manifest_file_path = svn_dirent_join(revprops->folder, PATH_MANIFEST, pool);
SVN_ERR(read_content(&content, manifest_file_path, pool));
/* parse the manifest. Every line is a file name */
revprops->manifest = apr_array_make(pool, ffd->max_files_per_dir,
sizeof(const char*));
/* Read all lines. Since the last line ends with a newline, we will
end up with a valid but empty string after the last entry. */
while (content->data && *content->data)
{
APR_ARRAY_PUSH(revprops->manifest, const char*) = content->data;
content->data = strchr(content->data, '\n');
if (content->data)
{
*content->data = 0;
content->data++;
}
}
/* Index for our revision. Rev 0 is excluded from the first shard. */
revprops->manifest_start = revprops->revision
- (revprops->revision % ffd->max_files_per_dir);
if (revprops->manifest_start == 0)
++revprops->manifest_start;
idx = (int)(revprops->revision - revprops->manifest_start);
if (revprops->manifest->nelts <= idx)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Packed revprop manifest for r%ld too "
"small"), revprops->revision);
/* Now get the file name */
revprops->filename = APR_ARRAY_IDX(revprops->manifest, idx, const char*);
return SVN_NO_ERROR;
}
/* Return TRUE, if revision R1 and R2 refer to the same shard in FS.
*/
static svn_boolean_t
same_shard(svn_fs_t *fs,
svn_revnum_t r1,
svn_revnum_t r2)
{
fs_fs_data_t *ffd = fs->fsap_data;
return (r1 / ffd->max_files_per_dir) == (r2 / ffd->max_files_per_dir);
}
/* Given FS and the full packed file content in REVPROPS->PACKED_REVPROPS,
* fill the START_REVISION, SIZES, OFFSETS members. Also, make
* PACKED_REVPROPS point to the first serialized revprop.
*
* Parse the revprops for REVPROPS->REVISION and set the PROPERTIES as
* well as the SERIALIZED_SIZE member. If revprop caching has been
* enabled, parse all revprops in the pack and cache them.
*/
static svn_error_t *
parse_packed_revprops(svn_fs_t *fs,
packed_revprops_t *revprops,
apr_pool_t *pool,
apr_pool_t *scratch_pool)
{
svn_stream_t *stream;
apr_int64_t first_rev, count, i;
apr_off_t offset;
const char *header_end;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
/* decompress (even if the data is only "stored", there is still a
* length header to remove) */
svn_string_t *compressed
= svn_stringbuf__morph_into_string(revprops->packed_revprops);
svn_stringbuf_t *uncompressed = svn_stringbuf_create_empty(pool);
SVN_ERR(svn__decompress(compressed, uncompressed, APR_SIZE_MAX));
/* read first revision number and number of revisions in the pack */
stream = svn_stream_from_stringbuf(uncompressed, scratch_pool);
SVN_ERR(read_number_from_stream(&first_rev, NULL, stream, iterpool));
SVN_ERR(read_number_from_stream(&count, NULL, stream, iterpool));
/* Check revision range for validity. */
if ( !same_shard(fs, revprops->revision, first_rev)
|| !same_shard(fs, revprops->revision, first_rev + count - 1)
|| count < 1)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Revprop pack for revision r%ld"
" contains revprops for r%ld .. r%ld"),
revprops->revision,
(svn_revnum_t)first_rev,
(svn_revnum_t)(first_rev + count -1));
/* Since start & end are in the same shard, it is enough to just test
* the FIRST_REV for being actually packed. That will also cover the
* special case of rev 0 never being packed. */
if (!is_packed_revprop(fs, first_rev))
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Revprop pack for revision r%ld"
" starts at non-packed revisions r%ld"),
revprops->revision, (svn_revnum_t)first_rev);
/* make PACKED_REVPROPS point to the first char after the header.
* This is where the serialized revprops are. */
header_end = strstr(uncompressed->data, "\n\n");
if (header_end == NULL)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Header end not found"));
offset = header_end - uncompressed->data + 2;
revprops->packed_revprops = svn_stringbuf_create_empty(pool);
revprops->packed_revprops->data = uncompressed->data + offset;
revprops->packed_revprops->len = (apr_size_t)(uncompressed->len - offset);
revprops->packed_revprops->blocksize = (apr_size_t)(uncompressed->blocksize - offset);
/* STREAM still points to the first entry in the sizes list.
* Init / construct REVPROPS members. */
revprops->start_revision = (svn_revnum_t)first_rev;
revprops->sizes = apr_array_make(pool, (int)count, sizeof(offset));
revprops->offsets = apr_array_make(pool, (int)count, sizeof(offset));
/* Now parse, revision by revision, the size and content of each
* revisions' revprops. */
for (i = 0, offset = 0, revprops->total_size = 0; i < count; ++i)
{
apr_int64_t size;
svn_string_t serialized;
apr_hash_t *properties;
svn_revnum_t revision = (svn_revnum_t)(first_rev + i);
/* read & check the serialized size */
SVN_ERR(read_number_from_stream(&size, NULL, stream, iterpool));
if (size + offset > (apr_int64_t)revprops->packed_revprops->len)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Packed revprop size exceeds pack file size"));
/* Parse this revprops list, if necessary */
serialized.data = revprops->packed_revprops->data + offset;
serialized.len = (apr_size_t)size;
if (revision == revprops->revision)
{
SVN_ERR(parse_revprop(&revprops->properties, fs, revision,
revprops->generation, &serialized,
pool, iterpool));
revprops->serialized_size = serialized.len;
}
else
{
/* If revprop caching is enabled, parse any revprops.
* They will get cached as a side-effect of this. */
if (has_revprop_cache(fs, pool))
SVN_ERR(parse_revprop(&properties, fs, revision,
revprops->generation, &serialized,
iterpool, iterpool));
}
/* fill REVPROPS data structures */
APR_ARRAY_PUSH(revprops->sizes, apr_off_t) = serialized.len;
APR_ARRAY_PUSH(revprops->offsets, apr_off_t) = offset;
revprops->total_size += serialized.len;
offset += serialized.len;
svn_pool_clear(iterpool);
}
return SVN_NO_ERROR;
}
/* In filesystem FS, read the packed revprops for revision REV into
* *REVPROPS. Use GENERATION to populate the revprop cache, if enabled.
* Allocate data in POOL.
*/
static svn_error_t *
read_pack_revprop(packed_revprops_t **revprops,
svn_fs_t *fs,
svn_revnum_t rev,
apr_int64_t generation,
apr_pool_t *pool)
{
apr_pool_t *iterpool = svn_pool_create(pool);
svn_boolean_t missing = FALSE;
svn_error_t *err;
packed_revprops_t *result;
int i;
/* someone insisted that REV is packed. Double-check if necessary */
if (!is_packed_revprop(fs, rev))
SVN_ERR(update_min_unpacked_rev(fs, iterpool));
if (!is_packed_revprop(fs, rev))
return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
_("No such packed revision %ld"), rev);
/* initialize the result data structure */
result = apr_pcalloc(pool, sizeof(*result));
result->revision = rev;
result->generation = generation;
/* try to read the packed revprops. This may require retries if we have
* concurrent writers. */
for (i = 0; i < RECOVERABLE_RETRY_COUNT && !result->packed_revprops; ++i)
{
const char *file_path;
/* there might have been concurrent writes.
* Re-read the manifest and the pack file.
*/
SVN_ERR(get_revprop_packname(fs, result, pool, iterpool));
file_path = svn_dirent_join(result->folder,
result->filename,
iterpool);
SVN_ERR(try_stringbuf_from_file(&result->packed_revprops,
&missing,
file_path,
i + 1 < RECOVERABLE_RETRY_COUNT,
pool));
/* If we could not find the file, there was a write.
* So, we should refresh our revprop generation info as well such
* that others may find data we will put into the cache. They would
* consider it outdated, otherwise.
*/
if (missing && has_revprop_cache(fs, pool))
SVN_ERR(read_revprop_generation(&result->generation, fs, pool));
svn_pool_clear(iterpool);
}
/* the file content should be available now */
if (!result->packed_revprops)
return svn_error_createf(SVN_ERR_FS_PACKED_REVPROP_READ_FAILURE, NULL,
_("Failed to read revprop pack file for r%ld"), rev);
/* parse it. RESULT will be complete afterwards. */
err = parse_packed_revprops(fs, result, pool, iterpool);
svn_pool_destroy(iterpool);
if (err)
return svn_error_createf(SVN_ERR_FS_CORRUPT, err,
_("Revprop pack file for r%ld is corrupt"), rev);
*revprops = result;
return SVN_NO_ERROR;
}
/* Read the revprops for revision REV in FS and return them in *PROPERTIES_P.
*
* Allocations will be done in POOL.
*/
static svn_error_t *
get_revision_proplist(apr_hash_t **proplist_p,
svn_fs_t *fs,
svn_revnum_t rev,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
apr_int64_t generation = 0;
/* not found, yet */
*proplist_p = NULL;
/* should they be available at all? */
SVN_ERR(ensure_revision_exists(fs, rev, pool));
/* Try cache lookup first. */
if (has_revprop_cache(fs, pool))
{
svn_boolean_t is_cached;
pair_cache_key_t key = { 0 };
SVN_ERR(read_revprop_generation(&generation, fs, pool));
key.revision = rev;
key.second = generation;
SVN_ERR(svn_cache__get((void **) proplist_p, &is_cached,
ffd->revprop_cache, &key, pool));
if (is_cached)
return SVN_NO_ERROR;
}
/* if REV had not been packed when we began, try reading it from the
* non-packed shard. If that fails, we will fall through to packed
* shard reads. */
if (!is_packed_revprop(fs, rev))
{
svn_error_t *err = read_non_packed_revprop(proplist_p, fs, rev,
generation, pool);
if (err)
{
if (!APR_STATUS_IS_ENOENT(err->apr_err)
|| ffd->format < SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT)
return svn_error_trace(err);
svn_error_clear(err);
*proplist_p = NULL; /* in case read_non_packed_revprop changed it */
}
}
/* if revprop packing is available and we have not read the revprops, yet,
* try reading them from a packed shard. If that fails, REV is most
* likely invalid (or its revprops highly contested). */
if (ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT && !*proplist_p)
{
packed_revprops_t *packed_revprops;
SVN_ERR(read_pack_revprop(&packed_revprops, fs, rev, generation, pool));
*proplist_p = packed_revprops->properties;
}
/* The revprops should have been there. Did we get them? */
if (!*proplist_p)
return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
_("Could not read revprops for revision %ld"),
rev);
return SVN_NO_ERROR;
}
/* Serialize the revision property list PROPLIST of revision REV in
* filesystem FS to a non-packed file. Return the name of that temporary
* file in *TMP_PATH and the file path that it must be moved to in
* *FINAL_PATH.
*
* Use POOL for allocations.
*/
static svn_error_t *
write_non_packed_revprop(const char **final_path,
const char **tmp_path,
svn_fs_t *fs,
svn_revnum_t rev,
apr_hash_t *proplist,
apr_pool_t *pool)
{
+ apr_file_t *file;
svn_stream_t *stream;
*final_path = path_revprops(fs, rev, pool);
/* ### do we have a directory sitting around already? we really shouldn't
### have to get the dirname here. */
- SVN_ERR(svn_stream_open_unique(&stream, tmp_path,
- svn_dirent_dirname(*final_path, pool),
- svn_io_file_del_none, pool, pool));
+ SVN_ERR(svn_io_open_unique_file3(&file, tmp_path,
+ svn_dirent_dirname(*final_path, pool),
+ svn_io_file_del_none, pool, pool));
+ stream = svn_stream_from_aprfile2(file, TRUE, pool);
SVN_ERR(svn_hash_write2(proplist, stream, SVN_HASH_TERMINATOR, pool));
SVN_ERR(svn_stream_close(stream));
+ /* Flush temporary file to disk and close it. */
+ SVN_ERR(svn_io_file_flush_to_disk(file, pool));
+ SVN_ERR(svn_io_file_close(file, pool));
+
return SVN_NO_ERROR;
}
/* After writing the new revprop file(s), call this function to move the
* file at TMP_PATH to FINAL_PATH and give it the permissions from
* PERMS_REFERENCE.
*
* If indicated in BUMP_GENERATION, increase FS' revprop generation.
* Finally, delete all the temporary files given in FILES_TO_DELETE.
* The latter may be NULL.
*
* Use POOL for temporary allocations.
*/
static svn_error_t *
switch_to_new_revprop(svn_fs_t *fs,
const char *final_path,
const char *tmp_path,
const char *perms_reference,
apr_array_header_t *files_to_delete,
svn_boolean_t bump_generation,
apr_pool_t *pool)
{
/* Now, we may actually be replacing revprops. Make sure that all other
threads and processes will know about this. */
if (bump_generation)
SVN_ERR(begin_revprop_change(fs, pool));
SVN_ERR(move_into_place(tmp_path, final_path, perms_reference, pool));
/* Indicate that the update (if relevant) has been completed. */
if (bump_generation)
SVN_ERR(end_revprop_change(fs, pool));
/* Clean up temporary files, if necessary. */
if (files_to_delete)
{
apr_pool_t *iterpool = svn_pool_create(pool);
int i;
for (i = 0; i < files_to_delete->nelts; ++i)
{
const char *path = APR_ARRAY_IDX(files_to_delete, i, const char*);
SVN_ERR(svn_io_remove_file2(path, TRUE, iterpool));
svn_pool_clear(iterpool);
}
svn_pool_destroy(iterpool);
}
return SVN_NO_ERROR;
}
/* Write a pack file header to STREAM that starts at revision START_REVISION
* and contains the indexes [START,END) of SIZES.
*/
static svn_error_t *
serialize_revprops_header(svn_stream_t *stream,
svn_revnum_t start_revision,
apr_array_header_t *sizes,
int start,
int end,
apr_pool_t *pool)
{
apr_pool_t *iterpool = svn_pool_create(pool);
int i;
SVN_ERR_ASSERT(start < end);
/* start revision and entry count */
SVN_ERR(svn_stream_printf(stream, pool, "%ld\n", start_revision));
SVN_ERR(svn_stream_printf(stream, pool, "%d\n", end - start));
/* the sizes array */
for (i = start; i < end; ++i)
{
apr_off_t size = APR_ARRAY_IDX(sizes, i, apr_off_t);
SVN_ERR(svn_stream_printf(stream, iterpool, "%" APR_OFF_T_FMT "\n",
size));
}
/* the double newline char indicates the end of the header */
SVN_ERR(svn_stream_printf(stream, iterpool, "\n"));
svn_pool_clear(iterpool);
return SVN_NO_ERROR;
}
-/* Writes the a pack file to FILE_STREAM. It copies the serialized data
+/* Writes the a pack file to FILE. It copies the serialized data
* from REVPROPS for the indexes [START,END) except for index CHANGED_INDEX.
*
* The data for the latter is taken from NEW_SERIALIZED. Note, that
* CHANGED_INDEX may be outside the [START,END) range, i.e. no new data is
* taken in that case but only a subset of the old data will be copied.
*
* NEW_TOTAL_SIZE is a hint for pre-allocating buffers of appropriate size.
* POOL is used for temporary allocations.
*/
static svn_error_t *
repack_revprops(svn_fs_t *fs,
packed_revprops_t *revprops,
int start,
int end,
int changed_index,
svn_stringbuf_t *new_serialized,
apr_off_t new_total_size,
- svn_stream_t *file_stream,
+ apr_file_t *file,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
svn_stream_t *stream;
int i;
/* create data empty buffers and the stream object */
svn_stringbuf_t *uncompressed
= svn_stringbuf_create_ensure((apr_size_t)new_total_size, pool);
svn_stringbuf_t *compressed
= svn_stringbuf_create_empty(pool);
stream = svn_stream_from_stringbuf(uncompressed, pool);
/* write the header*/
SVN_ERR(serialize_revprops_header(stream, revprops->start_revision + start,
revprops->sizes, start, end, pool));
/* append the serialized revprops */
for (i = start; i < end; ++i)
if (i == changed_index)
{
SVN_ERR(svn_stream_write(stream,
new_serialized->data,
&new_serialized->len));
}
else
{
apr_size_t size
= (apr_size_t)APR_ARRAY_IDX(revprops->sizes, i, apr_off_t);
apr_size_t offset
= (apr_size_t)APR_ARRAY_IDX(revprops->offsets, i, apr_off_t);
SVN_ERR(svn_stream_write(stream,
revprops->packed_revprops->data + offset,
&size));
}
/* flush the stream buffer (if any) to our underlying data buffer */
SVN_ERR(svn_stream_close(stream));
/* compress / store the data */
SVN_ERR(svn__compress(svn_stringbuf__morph_into_string(uncompressed),
compressed,
ffd->compress_packed_revprops
? SVN_DELTA_COMPRESSION_LEVEL_DEFAULT
: SVN_DELTA_COMPRESSION_LEVEL_NONE));
- /* finally, write the content to the target stream and close it */
- SVN_ERR(svn_stream_write(file_stream, compressed->data, &compressed->len));
- SVN_ERR(svn_stream_close(file_stream));
+ /* finally, write the content to the target file, flush and close it */
+ SVN_ERR(svn_io_file_write_full(file, compressed->data, compressed->len,
+ NULL, pool));
+ SVN_ERR(svn_io_file_flush_to_disk(file, pool));
+ SVN_ERR(svn_io_file_close(file, pool));
return SVN_NO_ERROR;
}
/* Allocate a new pack file name for revisions
* [REVPROPS->START_REVISION + START, REVPROPS->START_REVISION + END - 1]
* of REVPROPS->MANIFEST. Add the name of old file to FILES_TO_DELETE,
- * auto-create that array if necessary. Return an open file stream to
- * the new file in *STREAM allocated in POOL.
+ * auto-create that array if necessary. Return an open file *FILE that is
+ * allocated in POOL.
*/
static svn_error_t *
-repack_stream_open(svn_stream_t **stream,
- svn_fs_t *fs,
- packed_revprops_t *revprops,
- int start,
- int end,
- apr_array_header_t **files_to_delete,
- apr_pool_t *pool)
+repack_file_open(apr_file_t **file,
+ svn_fs_t *fs,
+ packed_revprops_t *revprops,
+ int start,
+ int end,
+ apr_array_header_t **files_to_delete,
+ apr_pool_t *pool)
{
apr_int64_t tag;
const char *tag_string;
svn_string_t *new_filename;
int i;
- apr_file_t *file;
int manifest_offset
= (int)(revprops->start_revision - revprops->manifest_start);
/* get the old (= current) file name and enlist it for later deletion */
const char *old_filename = APR_ARRAY_IDX(revprops->manifest,
start + manifest_offset,
const char*);
if (*files_to_delete == NULL)
*files_to_delete = apr_array_make(pool, 3, sizeof(const char*));
APR_ARRAY_PUSH(*files_to_delete, const char*)
= svn_dirent_join(revprops->folder, old_filename, pool);
/* increase the tag part, i.e. the counter after the dot */
tag_string = strchr(old_filename, '.');
if (tag_string == NULL)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Packed file '%s' misses a tag"),
old_filename);
SVN_ERR(svn_cstring_atoi64(&tag, tag_string + 1));
new_filename = svn_string_createf(pool, "%ld.%" APR_INT64_T_FMT,
revprops->start_revision + start,
++tag);
/* update the manifest to point to the new file */
for (i = start; i < end; ++i)
APR_ARRAY_IDX(revprops->manifest, i + manifest_offset, const char*)
= new_filename->data;
- /* create a file stream for the new file */
- SVN_ERR(svn_io_file_open(&file, svn_dirent_join(revprops->folder,
- new_filename->data,
- pool),
+ /* open the file */
+ SVN_ERR(svn_io_file_open(file, svn_dirent_join(revprops->folder,
+ new_filename->data,
+ pool),
APR_WRITE | APR_CREATE, APR_OS_DEFAULT, pool));
- *stream = svn_stream_from_aprfile2(file, FALSE, pool);
return SVN_NO_ERROR;
}
/* For revision REV in filesystem FS, set the revision properties to
* PROPLIST. Return a new file in *TMP_PATH that the caller shall move
* to *FINAL_PATH to make the change visible. Files to be deleted will
* be listed in *FILES_TO_DELETE which may remain unchanged / unallocated.
* Use POOL for allocations.
*/
static svn_error_t *
write_packed_revprop(const char **final_path,
const char **tmp_path,
apr_array_header_t **files_to_delete,
svn_fs_t *fs,
svn_revnum_t rev,
apr_hash_t *proplist,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
packed_revprops_t *revprops;
apr_int64_t generation = 0;
svn_stream_t *stream;
+ apr_file_t *file;
svn_stringbuf_t *serialized;
apr_off_t new_total_size;
int changed_index;
/* read the current revprop generation. This value will not change
* while we hold the global write lock to this FS. */
if (has_revprop_cache(fs, pool))
SVN_ERR(read_revprop_generation(&generation, fs, pool));
/* read contents of the current pack file */
SVN_ERR(read_pack_revprop(&revprops, fs, rev, generation, pool));
/* serialize the new revprops */
serialized = svn_stringbuf_create_empty(pool);
stream = svn_stream_from_stringbuf(serialized, pool);
SVN_ERR(svn_hash_write2(proplist, stream, SVN_HASH_TERMINATOR, pool));
SVN_ERR(svn_stream_close(stream));
/* calculate the size of the new data */
changed_index = (int)(rev - revprops->start_revision);
new_total_size = revprops->total_size - revprops->serialized_size
+ serialized->len
+ (revprops->offsets->nelts + 2) * SVN_INT64_BUFFER_SIZE;
APR_ARRAY_IDX(revprops->sizes, changed_index, apr_off_t) = serialized->len;
/* can we put the new data into the same pack as the before? */
if ( new_total_size < ffd->revprop_pack_size
|| revprops->sizes->nelts == 1)
{
/* simply replace the old pack file with new content as we do it
* in the non-packed case */
*final_path = svn_dirent_join(revprops->folder, revprops->filename,
pool);
- SVN_ERR(svn_stream_open_unique(&stream, tmp_path, revprops->folder,
- svn_io_file_del_none, pool, pool));
+ SVN_ERR(svn_io_open_unique_file3(&file, tmp_path, revprops->folder,
+ svn_io_file_del_none, pool, pool));
SVN_ERR(repack_revprops(fs, revprops, 0, revprops->sizes->nelts,
changed_index, serialized, new_total_size,
- stream, pool));
+ file, pool));
}
else
{
/* split the pack file into two of roughly equal size */
int right_count, left_count, i;
int left = 0;
int right = revprops->sizes->nelts - 1;
apr_off_t left_size = 2 * SVN_INT64_BUFFER_SIZE;
apr_off_t right_size = 2 * SVN_INT64_BUFFER_SIZE;
/* let left and right side grow such that their size difference
* is minimal after each step. */
while (left <= right)
if ( left_size + APR_ARRAY_IDX(revprops->sizes, left, apr_off_t)
< right_size + APR_ARRAY_IDX(revprops->sizes, right, apr_off_t))
{
left_size += APR_ARRAY_IDX(revprops->sizes, left, apr_off_t)
+ SVN_INT64_BUFFER_SIZE;
++left;
}
else
{
right_size += APR_ARRAY_IDX(revprops->sizes, right, apr_off_t)
+ SVN_INT64_BUFFER_SIZE;
--right;
}
/* since the items need much less than SVN_INT64_BUFFER_SIZE
* bytes to represent their length, the split may not be optimal */
left_count = left;
right_count = revprops->sizes->nelts - left;
/* if new_size is large, one side may exceed the pack size limit.
* In that case, split before and after the modified revprop.*/
if ( left_size > ffd->revprop_pack_size
|| right_size > ffd->revprop_pack_size)
{
left_count = changed_index;
right_count = revprops->sizes->nelts - left_count - 1;
}
/* write the new, split files */
if (left_count)
{
- SVN_ERR(repack_stream_open(&stream, fs, revprops, 0,
- left_count, files_to_delete, pool));
+ SVN_ERR(repack_file_open(&file, fs, revprops, 0,
+ left_count, files_to_delete, pool));
SVN_ERR(repack_revprops(fs, revprops, 0, left_count,
changed_index, serialized, new_total_size,
- stream, pool));
+ file, pool));
}
if (left_count + right_count < revprops->sizes->nelts)
{
- SVN_ERR(repack_stream_open(&stream, fs, revprops, changed_index,
- changed_index + 1, files_to_delete,
- pool));
+ SVN_ERR(repack_file_open(&file, fs, revprops, changed_index,
+ changed_index + 1, files_to_delete,
+ pool));
SVN_ERR(repack_revprops(fs, revprops, changed_index,
changed_index + 1,
changed_index, serialized, new_total_size,
- stream, pool));
+ file, pool));
}
if (right_count)
{
- SVN_ERR(repack_stream_open(&stream, fs, revprops,
- revprops->sizes->nelts - right_count,
- revprops->sizes->nelts,
- files_to_delete, pool));
+ SVN_ERR(repack_file_open(&file, fs, revprops,
+ revprops->sizes->nelts - right_count,
+ revprops->sizes->nelts,
+ files_to_delete, pool));
SVN_ERR(repack_revprops(fs, revprops,
revprops->sizes->nelts - right_count,
revprops->sizes->nelts, changed_index,
- serialized, new_total_size, stream,
+ serialized, new_total_size, file,
pool));
}
/* write the new manifest */
*final_path = svn_dirent_join(revprops->folder, PATH_MANIFEST, pool);
- SVN_ERR(svn_stream_open_unique(&stream, tmp_path, revprops->folder,
- svn_io_file_del_none, pool, pool));
+ SVN_ERR(svn_io_open_unique_file3(&file, tmp_path, revprops->folder,
+ svn_io_file_del_none, pool, pool));
for (i = 0; i < revprops->manifest->nelts; ++i)
{
const char *filename = APR_ARRAY_IDX(revprops->manifest, i,
const char*);
- SVN_ERR(svn_stream_printf(stream, pool, "%s\n", filename));
+ SVN_ERR(svn_io_file_write_full(file, filename, strlen(filename),
+ NULL, pool));
+ SVN_ERR(svn_io_file_putc('\n', file, pool));
}
- SVN_ERR(svn_stream_close(stream));
+ SVN_ERR(svn_io_file_flush_to_disk(file, pool));
+ SVN_ERR(svn_io_file_close(file, pool));
}
return SVN_NO_ERROR;
}
/* Set the revision property list of revision REV in filesystem FS to
PROPLIST. Use POOL for temporary allocations. */
static svn_error_t *
set_revision_proplist(svn_fs_t *fs,
svn_revnum_t rev,
apr_hash_t *proplist,
apr_pool_t *pool)
{
svn_boolean_t is_packed;
svn_boolean_t bump_generation = FALSE;
const char *final_path;
const char *tmp_path;
const char *perms_reference;
apr_array_header_t *files_to_delete = NULL;
SVN_ERR(ensure_revision_exists(fs, rev, pool));
/* this info will not change while we hold the global FS write lock */
is_packed = is_packed_revprop(fs, rev);
/* Test whether revprops already exist for this revision.
* Only then will we need to bump the revprop generation. */
if (has_revprop_cache(fs, pool))
{
if (is_packed)
{
bump_generation = TRUE;
}
else
{
svn_node_kind_t kind;
SVN_ERR(svn_io_check_path(path_revprops(fs, rev, pool), &kind,
pool));
bump_generation = kind != svn_node_none;
}
}
/* Serialize the new revprop data */
if (is_packed)
SVN_ERR(write_packed_revprop(&final_path, &tmp_path, &files_to_delete,
fs, rev, proplist, pool));
else
SVN_ERR(write_non_packed_revprop(&final_path, &tmp_path,
fs, rev, proplist, pool));
/* We use the rev file of this revision as the perms reference,
* because when setting revprops for the first time, the revprop
* file won't exist and therefore can't serve as its own reference.
* (Whereas the rev file should already exist at this point.)
*/
SVN_ERR(svn_fs_fs__path_rev_absolute(&perms_reference, fs, rev, pool));
/* Now, switch to the new revprop data. */
SVN_ERR(switch_to_new_revprop(fs, final_path, tmp_path, perms_reference,
files_to_delete, bump_generation, pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__revision_proplist(apr_hash_t **proplist_p,
svn_fs_t *fs,
svn_revnum_t rev,
apr_pool_t *pool)
{
SVN_ERR(get_revision_proplist(proplist_p, fs, rev, pool));
return SVN_NO_ERROR;
}
/* Represents where in the current svndiff data block each
representation is. */
struct rep_state
{
apr_file_t *file;
/* The txdelta window cache to use or NULL. */
svn_cache__t *window_cache;
/* Caches un-deltified windows. May be NULL. */
svn_cache__t *combined_cache;
apr_off_t start; /* The starting offset for the raw
svndiff/plaintext data minus header. */
apr_off_t off; /* The current offset into the file. */
apr_off_t end; /* The end offset of the raw data. */
int ver; /* If a delta, what svndiff version? */
int chunk_index;
};
/* See create_rep_state, which wraps this and adds another error. */
static svn_error_t *
create_rep_state_body(struct rep_state **rep_state,
struct rep_args **rep_args,
apr_file_t **file_hint,
svn_revnum_t *rev_hint,
representation_t *rep,
svn_fs_t *fs,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
struct rep_state *rs = apr_pcalloc(pool, sizeof(*rs));
struct rep_args *ra;
unsigned char buf[4];
/* If the hint is
* - given,
* - refers to a valid revision,
* - refers to a packed revision,
* - as does the rep we want to read, and
* - refers to the same pack file as the rep
* ...
*/
if ( file_hint && rev_hint && *file_hint
&& SVN_IS_VALID_REVNUM(*rev_hint)
&& *rev_hint < ffd->min_unpacked_rev
&& rep->revision < ffd->min_unpacked_rev
&& ( (*rev_hint / ffd->max_files_per_dir)
== (rep->revision / ffd->max_files_per_dir)))
{
/* ... we can re-use the same, already open file object
*/
apr_off_t offset;
SVN_ERR(get_packed_offset(&offset, fs, rep->revision, pool));
offset += rep->offset;
SVN_ERR(svn_io_file_seek(*file_hint, APR_SET, &offset, pool));
rs->file = *file_hint;
}
else
{
/* otherwise, create a new file object
*/
SVN_ERR(open_and_seek_representation(&rs->file, fs, rep, pool));
}
/* remember the current file, if suggested by the caller */
if (file_hint)
*file_hint = rs->file;
if (rev_hint)
*rev_hint = rep->revision;
/* continue constructing RS and RA */
rs->window_cache = ffd->txdelta_window_cache;
rs->combined_cache = ffd->combined_window_cache;
SVN_ERR(read_rep_line(&ra, rs->file, pool));
SVN_ERR(get_file_offset(&rs->start, rs->file, pool));
rs->off = rs->start;
rs->end = rs->start + rep->size;
*rep_state = rs;
*rep_args = ra;
if (!ra->is_delta)
/* This is a plaintext, so just return the current rep_state. */
return SVN_NO_ERROR;
/* We are dealing with a delta, find out what version. */
SVN_ERR(svn_io_file_read_full2(rs->file, buf, sizeof(buf),
NULL, NULL, pool));
/* ### Layering violation */
if (! ((buf[0] == 'S') && (buf[1] == 'V') && (buf[2] == 'N')))
return svn_error_create
(SVN_ERR_FS_CORRUPT, NULL,
_("Malformed svndiff data in representation"));
rs->ver = buf[3];
rs->chunk_index = 0;
rs->off += 4;
return SVN_NO_ERROR;
}
/* Read the rep args for REP in filesystem FS and create a rep_state
for reading the representation. Return the rep_state in *REP_STATE
and the rep args in *REP_ARGS, both allocated in POOL.
When reading multiple reps, i.e. a skip delta chain, you may provide
non-NULL FILE_HINT and REV_HINT. (If FILE_HINT is not NULL, in the first
call it should be a pointer to NULL.) The function will use these variables
to store the previous call results and tries to re-use them. This may
result in significant savings in I/O for packed files.
*/
static svn_error_t *
create_rep_state(struct rep_state **rep_state,
struct rep_args **rep_args,
apr_file_t **file_hint,
svn_revnum_t *rev_hint,
representation_t *rep,
svn_fs_t *fs,
apr_pool_t *pool)
{
svn_error_t *err = create_rep_state_body(rep_state, rep_args,
file_hint, rev_hint,
rep, fs, pool);
if (err && err->apr_err == SVN_ERR_FS_CORRUPT)
{
fs_fs_data_t *ffd = fs->fsap_data;
/* ### This always returns "-1" for transaction reps, because
### this particular bit of code doesn't know if the rep is
### stored in the protorev or in the mutable area (for props
### or dir contents). It is pretty rare for FSFS to *read*
### from the protorev file, though, so this is probably OK.
### And anyone going to debug corruption errors is probably
### going to jump straight to this comment anyway! */
return svn_error_createf(SVN_ERR_FS_CORRUPT, err,
"Corrupt representation '%s'",
rep
? representation_string(rep, ffd->format, TRUE,
TRUE, pool)
: "(null)");
}
/* ### Call representation_string() ? */
return svn_error_trace(err);
}
struct rep_read_baton
{
/* The FS from which we're reading. */
svn_fs_t *fs;
/* If not NULL, this is the base for the first delta window in rs_list */
svn_stringbuf_t *base_window;
/* The state of all prior delta representations. */
apr_array_header_t *rs_list;
/* The plaintext state, if there is a plaintext. */
struct rep_state *src_state;
/* The index of the current delta chunk, if we are reading a delta. */
int chunk_index;
/* The buffer where we store undeltified data. */
char *buf;
apr_size_t buf_pos;
apr_size_t buf_len;
/* A checksum context for summing the data read in order to verify it.
Note: we don't need to use the sha1 checksum because we're only doing
data verification, for which md5 is perfectly safe. */
svn_checksum_ctx_t *md5_checksum_ctx;
svn_boolean_t checksum_finalized;
/* The stored checksum of the representation we are reading, its
length, and the amount we've read so far. Some of this
information is redundant with rs_list and src_state, but it's
convenient for the checksumming code to have it here. */
svn_checksum_t *md5_checksum;
svn_filesize_t len;
svn_filesize_t off;
/* The key for the fulltext cache for this rep, if there is a
fulltext cache. */
pair_cache_key_t fulltext_cache_key;
/* The text we've been reading, if we're going to cache it. */
svn_stringbuf_t *current_fulltext;
/* Used for temporary allocations during the read. */
apr_pool_t *pool;
/* Pool used to store file handles and other data that is persistant
for the entire stream read. */
apr_pool_t *filehandle_pool;
};
/* Combine the name of the rev file in RS with the given OFFSET to form
* a cache lookup key. Allocations will be made from POOL. May return
* NULL if the key cannot be constructed. */
static const char*
get_window_key(struct rep_state *rs, apr_off_t offset, apr_pool_t *pool)
{
const char *name;
const char *last_part;
const char *name_last;
/* the rev file name containing the txdelta window.
* If this fails we are in serious trouble anyways.
* And if nobody else detects the problems, the file content checksum
* comparison _will_ find them.
*/
if (apr_file_name_get(&name, rs->file))
return NULL;
/* Handle packed files as well by scanning backwards until we find the
* revision or pack number. */
name_last = name + strlen(name) - 1;
while (! svn_ctype_isdigit(*name_last))
--name_last;
last_part = name_last;
while (svn_ctype_isdigit(*last_part))
--last_part;
/* We must differentiate between packed files (as of today, the number
* is being followed by a dot) and non-packed files (followed by \0).
* Otherwise, there might be overlaps in the numbering range if the
* repo gets packed after caching the txdeltas of non-packed revs.
* => add the first non-digit char to the packed number. */
if (name_last[1] != '\0')
++name_last;
/* copy one char MORE than the actual number to mark packed files,
* i.e. packed revision file content uses different key space then
* non-packed ones: keys for packed rev file content ends with a dot
* for non-packed rev files they end with a digit. */
name = apr_pstrndup(pool, last_part + 1, name_last - last_part);
return svn_fs_fs__combine_number_and_string(offset, name, pool);
}
/* Read the WINDOW_P for the rep state RS from the current FSFS session's
* cache. This will be a no-op and IS_CACHED will be set to FALSE if no
* cache has been given. If a cache is available IS_CACHED will inform
* the caller about the success of the lookup. Allocations (of the window
* in particualar) will be made from POOL.
*
* If the information could be found, put RS and the position within the
* rev file into the same state as if the data had just been read from it.
*/
static svn_error_t *
get_cached_window(svn_txdelta_window_t **window_p,
struct rep_state *rs,
svn_boolean_t *is_cached,
apr_pool_t *pool)
{
if (! rs->window_cache)
{
/* txdelta window has not been enabled */
*is_cached = FALSE;
}
else
{
/* ask the cache for the desired txdelta window */
svn_fs_fs__txdelta_cached_window_t *cached_window;
SVN_ERR(svn_cache__get((void **) &cached_window,
is_cached,
rs->window_cache,
get_window_key(rs, rs->off, pool),
pool));
if (*is_cached)
{
/* found it. Pass it back to the caller. */
*window_p = cached_window->window;
/* manipulate the RS as if we just read the data */
rs->chunk_index++;
rs->off = cached_window->end_offset;
/* manipulate the rev file as if we just read from it */
SVN_ERR(svn_io_file_seek(rs->file, APR_SET, &rs->off, pool));
}
}
return SVN_NO_ERROR;
}
/* Store the WINDOW read at OFFSET for the rep state RS in the current
* FSFS session's cache. This will be a no-op if no cache has been given.
* Temporary allocations will be made from SCRATCH_POOL. */
static svn_error_t *
set_cached_window(svn_txdelta_window_t *window,
struct rep_state *rs,
apr_off_t offset,
apr_pool_t *scratch_pool)
{
if (rs->window_cache)
{
/* store the window and the first offset _past_ it */
svn_fs_fs__txdelta_cached_window_t cached_window;
cached_window.window = window;
cached_window.end_offset = rs->off;
/* but key it with the start offset because that is the known state
* when we will look it up */
return svn_cache__set(rs->window_cache,
get_window_key(rs, offset, scratch_pool),
&cached_window,
scratch_pool);
}
return SVN_NO_ERROR;
}
/* Read the WINDOW_P for the rep state RS from the current FSFS session's
* cache. This will be a no-op and IS_CACHED will be set to FALSE if no
* cache has been given. If a cache is available IS_CACHED will inform
* the caller about the success of the lookup. Allocations (of the window
* in particualar) will be made from POOL.
*/
static svn_error_t *
get_cached_combined_window(svn_stringbuf_t **window_p,
struct rep_state *rs,
svn_boolean_t *is_cached,
apr_pool_t *pool)
{
if (! rs->combined_cache)
{
/* txdelta window has not been enabled */
*is_cached = FALSE;
}
else
{
/* ask the cache for the desired txdelta window */
return svn_cache__get((void **)window_p,
is_cached,
rs->combined_cache,
get_window_key(rs, rs->start, pool),
pool);
}
return SVN_NO_ERROR;
}
/* Store the WINDOW read at OFFSET for the rep state RS in the current
* FSFS session's cache. This will be a no-op if no cache has been given.
* Temporary allocations will be made from SCRATCH_POOL. */
static svn_error_t *
set_cached_combined_window(svn_stringbuf_t *window,
struct rep_state *rs,
apr_off_t offset,
apr_pool_t *scratch_pool)
{
if (rs->combined_cache)
{
/* but key it with the start offset because that is the known state
* when we will look it up */
return svn_cache__set(rs->combined_cache,
get_window_key(rs, offset, scratch_pool),
window,
scratch_pool);
}
return SVN_NO_ERROR;
}
/* Build an array of rep_state structures in *LIST giving the delta
reps from first_rep to a plain-text or self-compressed rep. Set
*SRC_STATE to the plain-text rep we find at the end of the chain,
or to NULL if the final delta representation is self-compressed.
The representation to start from is designated by filesystem FS, id
ID, and representation REP.
Also, set *WINDOW_P to the base window content for *LIST, if it
could be found in cache. Otherwise, *LIST will contain the base
representation for the whole delta chain.
Finally, return the expanded size of the representation in
*EXPANDED_SIZE. It will take care of cases where only the on-disk
size is known. */
static svn_error_t *
build_rep_list(apr_array_header_t **list,
svn_stringbuf_t **window_p,
struct rep_state **src_state,
svn_filesize_t *expanded_size,
svn_fs_t *fs,
representation_t *first_rep,
apr_pool_t *pool)
{
representation_t rep;
struct rep_state *rs = NULL;
struct rep_args *rep_args;
svn_boolean_t is_cached = FALSE;
apr_file_t *last_file = NULL;
svn_revnum_t last_revision;
*list = apr_array_make(pool, 1, sizeof(struct rep_state *));
rep = *first_rep;
/* The value as stored in the data struct.
0 is either for unknown length or actually zero length. */
*expanded_size = first_rep->expanded_size;
/* for the top-level rep, we need the rep_args */
SVN_ERR(create_rep_state(&rs, &rep_args, &last_file,
&last_revision, &rep, fs, pool));
/* Unknown size or empty representation?
That implies the this being the first iteration.
Usually size equals on-disk size, except for empty,
compressed representations (delta, size = 4).
Please note that for all non-empty deltas have
a 4-byte header _plus_ some data. */
if (*expanded_size == 0)
if (! rep_args->is_delta || first_rep->size != 4)
*expanded_size = first_rep->size;
while (1)
{
/* fetch state, if that has not been done already */
if (!rs)
SVN_ERR(create_rep_state(&rs, &rep_args, &last_file,
&last_revision, &rep, fs, pool));
SVN_ERR(get_cached_combined_window(window_p, rs, &is_cached, pool));
if (is_cached)
{
/* We already have a reconstructed window in our cache.
Write a pseudo rep_state with the full length. */
rs->off = rs->start;
rs->end = rs->start + (*window_p)->len;
*src_state = rs;
return SVN_NO_ERROR;
}
if (!rep_args->is_delta)
{
/* This is a plaintext, so just return the current rep_state. */
*src_state = rs;
return SVN_NO_ERROR;
}
/* Push this rep onto the list. If it's self-compressed, we're done. */
APR_ARRAY_PUSH(*list, struct rep_state *) = rs;
if (rep_args->is_delta_vs_empty)
{
*src_state = NULL;
return SVN_NO_ERROR;
}
rep.revision = rep_args->base_revision;
rep.offset = rep_args->base_offset;
rep.size = rep_args->base_length;
rep.txn_id = NULL;
rs = NULL;
}
}
/* Create a rep_read_baton structure for node revision NODEREV in
filesystem FS and store it in *RB_P. If FULLTEXT_CACHE_KEY is not
NULL, it is the rep's key in the fulltext cache, and a stringbuf
must be allocated to store the text. Perform all allocations in
POOL. If rep is mutable, it must be for file contents. */
static svn_error_t *
rep_read_get_baton(struct rep_read_baton **rb_p,
svn_fs_t *fs,
representation_t *rep,
pair_cache_key_t fulltext_cache_key,
apr_pool_t *pool)
{
struct rep_read_baton *b;
b = apr_pcalloc(pool, sizeof(*b));
b->fs = fs;
b->base_window = NULL;
b->chunk_index = 0;
b->buf = NULL;
b->md5_checksum_ctx = svn_checksum_ctx_create(svn_checksum_md5, pool);
b->checksum_finalized = FALSE;
b->md5_checksum = svn_checksum_dup(rep->md5_checksum, pool);
b->len = rep->expanded_size;
b->off = 0;
b->fulltext_cache_key = fulltext_cache_key;
b->pool = svn_pool_create(pool);
b->filehandle_pool = svn_pool_create(pool);
SVN_ERR(build_rep_list(&b->rs_list, &b->base_window,
&b->src_state, &b->len, fs, rep,
b->filehandle_pool));
if (SVN_IS_VALID_REVNUM(fulltext_cache_key.revision))
b->current_fulltext = svn_stringbuf_create_ensure
((apr_size_t)b->len,
b->filehandle_pool);
else
b->current_fulltext = NULL;
/* Save our output baton. */
*rb_p = b;
return SVN_NO_ERROR;
}
/* Skip forwards to THIS_CHUNK in REP_STATE and then read the next delta
window into *NWIN. */
static svn_error_t *
read_delta_window(svn_txdelta_window_t **nwin, int this_chunk,
struct rep_state *rs, apr_pool_t *pool)
{
svn_stream_t *stream;
svn_boolean_t is_cached;
apr_off_t old_offset;
SVN_ERR_ASSERT(rs->chunk_index <= this_chunk);
/* RS->FILE may be shared between RS instances -> make sure we point
* to the right data. */
SVN_ERR(svn_io_file_seek(rs->file, APR_SET, &rs->off, pool));
/* Skip windows to reach the current chunk if we aren't there yet. */
while (rs->chunk_index < this_chunk)
{
SVN_ERR(svn_txdelta_skip_svndiff_window(rs->file, rs->ver, pool));
rs->chunk_index++;
SVN_ERR(get_file_offset(&rs->off, rs->file, pool));
if (rs->off >= rs->end)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Reading one svndiff window read "
"beyond the end of the "
"representation"));
}
/* Read the next window. But first, try to find it in the cache. */
SVN_ERR(get_cached_window(nwin, rs, &is_cached, pool));
if (is_cached)
return SVN_NO_ERROR;
/* Actually read the next window. */
old_offset = rs->off;
stream = svn_stream_from_aprfile2(rs->file, TRUE, pool);
SVN_ERR(svn_txdelta_read_svndiff_window(nwin, stream, rs->ver, pool));
rs->chunk_index++;
SVN_ERR(get_file_offset(&rs->off, rs->file, pool));
if (rs->off > rs->end)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Reading one svndiff window read beyond "
"the end of the representation"));
/* the window has not been cached before, thus cache it now
* (if caching is used for them at all) */
return set_cached_window(*nwin, rs, old_offset, pool);
}
/* Read SIZE bytes from the representation RS and return it in *NWIN. */
static svn_error_t *
read_plain_window(svn_stringbuf_t **nwin, struct rep_state *rs,
apr_size_t size, apr_pool_t *pool)
{
/* RS->FILE may be shared between RS instances -> make sure we point
* to the right data. */
SVN_ERR(svn_io_file_seek(rs->file, APR_SET, &rs->off, pool));
/* Read the plain data. */
*nwin = svn_stringbuf_create_ensure(size, pool);
SVN_ERR(svn_io_file_read_full2(rs->file, (*nwin)->data, size, NULL, NULL,
pool));
(*nwin)->data[size] = 0;
/* Update RS. */
rs->off += (apr_off_t)size;
return SVN_NO_ERROR;
}
/* Get the undeltified window that is a result of combining all deltas
from the current desired representation identified in *RB with its
base representation. Store the window in *RESULT. */
static svn_error_t *
get_combined_window(svn_stringbuf_t **result,
struct rep_read_baton *rb)
{
apr_pool_t *pool, *new_pool, *window_pool;
int i;
svn_txdelta_window_t *window;
apr_array_header_t *windows;
svn_stringbuf_t *source, *buf = rb->base_window;
struct rep_state *rs;
/* Read all windows that we need to combine. This is fine because
the size of each window is relatively small (100kB) and skip-
delta limits the number of deltas in a chain to well under 100.
Stop early if one of them does not depend on its predecessors. */
window_pool = svn_pool_create(rb->pool);
windows = apr_array_make(window_pool, 0, sizeof(svn_txdelta_window_t *));
for (i = 0; i < rb->rs_list->nelts; ++i)
{
rs = APR_ARRAY_IDX(rb->rs_list, i, struct rep_state *);
SVN_ERR(read_delta_window(&window, rb->chunk_index, rs, window_pool));
APR_ARRAY_PUSH(windows, svn_txdelta_window_t *) = window;
if (window->src_ops == 0)
{
++i;
break;
}
}
/* Combine in the windows from the other delta reps. */
pool = svn_pool_create(rb->pool);
for (--i; i >= 0; --i)
{
rs = APR_ARRAY_IDX(rb->rs_list, i, struct rep_state *);
window = APR_ARRAY_IDX(windows, i, svn_txdelta_window_t *);
/* Maybe, we've got a PLAIN start representation. If we do, read
as much data from it as the needed for the txdelta window's source
view.
- Note that BUF / SOURCE may only be NULL in the first iteration. */
+ Note that BUF / SOURCE may only be NULL in the first iteration.
+ Also note that we may have short-cut reading the delta chain --
+ in which case SRC_OPS is 0 and it might not be a PLAIN rep. */
source = buf;
- if (source == NULL && rb->src_state != NULL)
+ if (source == NULL && rb->src_state != NULL && window->src_ops)
SVN_ERR(read_plain_window(&source, rb->src_state, window->sview_len,
pool));
/* Combine this window with the current one. */
new_pool = svn_pool_create(rb->pool);
buf = svn_stringbuf_create_ensure(window->tview_len, new_pool);
buf->len = window->tview_len;
svn_txdelta_apply_instructions(window, source ? source->data : NULL,
buf->data, &buf->len);
if (buf->len != window->tview_len)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("svndiff window length is "
"corrupt"));
/* Cache windows only if the whole rep content could be read as a
single chunk. Only then will no other chunk need a deeper RS
list than the cached chunk. */
if ((rb->chunk_index == 0) && (rs->off == rs->end))
SVN_ERR(set_cached_combined_window(buf, rs, rs->start, new_pool));
/* Cycle pools so that we only need to hold three windows at a time. */
svn_pool_destroy(pool);
pool = new_pool;
}
svn_pool_destroy(window_pool);
*result = buf;
return SVN_NO_ERROR;
}
/* Returns whether or not the expanded fulltext of the file is cachable
* based on its size SIZE. The decision depends on the cache used by RB.
*/
static svn_boolean_t
fulltext_size_is_cachable(fs_fs_data_t *ffd, svn_filesize_t size)
{
return (size < APR_SIZE_MAX)
&& svn_cache__is_cachable(ffd->fulltext_cache, (apr_size_t)size);
}
/* Close method used on streams returned by read_representation().
*/
static svn_error_t *
rep_read_contents_close(void *baton)
{
struct rep_read_baton *rb = baton;
svn_pool_destroy(rb->pool);
svn_pool_destroy(rb->filehandle_pool);
return SVN_NO_ERROR;
}
/* Return the next *LEN bytes of the rep and store them in *BUF. */
static svn_error_t *
get_contents(struct rep_read_baton *rb,
char *buf,
apr_size_t *len)
{
apr_size_t copy_len, remaining = *len;
char *cur = buf;
struct rep_state *rs;
/* Special case for when there are no delta reps, only a plain
text. */
if (rb->rs_list->nelts == 0)
{
copy_len = remaining;
rs = rb->src_state;
if (rb->base_window != NULL)
{
/* We got the desired rep directly from the cache.
This is where we need the pseudo rep_state created
by build_rep_list(). */
apr_size_t offset = (apr_size_t)(rs->off - rs->start);
if (copy_len + offset > rb->base_window->len)
copy_len = offset < rb->base_window->len
? rb->base_window->len - offset
: 0ul;
memcpy (cur, rb->base_window->data + offset, copy_len);
}
else
{
if (((apr_off_t) copy_len) > rs->end - rs->off)
copy_len = (apr_size_t) (rs->end - rs->off);
SVN_ERR(svn_io_file_read_full2(rs->file, cur, copy_len, NULL,
NULL, rb->pool));
}
rs->off += copy_len;
*len = copy_len;
return SVN_NO_ERROR;
}
while (remaining > 0)
{
/* If we have buffered data from a previous chunk, use that. */
if (rb->buf)
{
/* Determine how much to copy from the buffer. */
copy_len = rb->buf_len - rb->buf_pos;
if (copy_len > remaining)
copy_len = remaining;
/* Actually copy the data. */
memcpy(cur, rb->buf + rb->buf_pos, copy_len);
rb->buf_pos += copy_len;
cur += copy_len;
remaining -= copy_len;
/* If the buffer is all used up, clear it and empty the
local pool. */
if (rb->buf_pos == rb->buf_len)
{
svn_pool_clear(rb->pool);
rb->buf = NULL;
}
}
else
{
svn_stringbuf_t *sbuf = NULL;
rs = APR_ARRAY_IDX(rb->rs_list, 0, struct rep_state *);
if (rs->off == rs->end)
break;
/* Get more buffered data by evaluating a chunk. */
SVN_ERR(get_combined_window(&sbuf, rb));
rb->chunk_index++;
rb->buf_len = sbuf->len;
rb->buf = sbuf->data;
rb->buf_pos = 0;
}
}
*len = cur - buf;
return SVN_NO_ERROR;
}
/* BATON is of type `rep_read_baton'; read the next *LEN bytes of the
representation and store them in *BUF. Sum as we read and verify
the MD5 sum at the end. */
static svn_error_t *
rep_read_contents(void *baton,
char *buf,
apr_size_t *len)
{
struct rep_read_baton *rb = baton;
/* Get the next block of data. */
SVN_ERR(get_contents(rb, buf, len));
if (rb->current_fulltext)
svn_stringbuf_appendbytes(rb->current_fulltext, buf, *len);
/* Perform checksumming. We want to check the checksum as soon as
the last byte of data is read, in case the caller never performs
a short read, but we don't want to finalize the MD5 context
twice. */
if (!rb->checksum_finalized)
{
SVN_ERR(svn_checksum_update(rb->md5_checksum_ctx, buf, *len));
rb->off += *len;
if (rb->off == rb->len)
{
svn_checksum_t *md5_checksum;
rb->checksum_finalized = TRUE;
SVN_ERR(svn_checksum_final(&md5_checksum, rb->md5_checksum_ctx,
rb->pool));
if (!svn_checksum_match(md5_checksum, rb->md5_checksum))
return svn_error_create(SVN_ERR_FS_CORRUPT,
svn_checksum_mismatch_err(rb->md5_checksum, md5_checksum,
rb->pool,
_("Checksum mismatch while reading representation")),
NULL);
}
}
if (rb->off == rb->len && rb->current_fulltext)
{
fs_fs_data_t *ffd = rb->fs->fsap_data;
SVN_ERR(svn_cache__set(ffd->fulltext_cache, &rb->fulltext_cache_key,
rb->current_fulltext, rb->pool));
rb->current_fulltext = NULL;
}
return SVN_NO_ERROR;
}
/* Return a stream in *CONTENTS_P that will read the contents of a
representation stored at the location given by REP. Appropriate
for any kind of immutable representation, but only for file
contents (not props or directory contents) in mutable
representations.
If REP is NULL, the representation is assumed to be empty, and the
empty stream is returned.
*/
static svn_error_t *
read_representation(svn_stream_t **contents_p,
svn_fs_t *fs,
representation_t *rep,
apr_pool_t *pool)
{
if (! rep)
{
*contents_p = svn_stream_empty(pool);
}
else
{
fs_fs_data_t *ffd = fs->fsap_data;
pair_cache_key_t fulltext_cache_key = { 0 };
svn_filesize_t len = rep->expanded_size ? rep->expanded_size : rep->size;
struct rep_read_baton *rb;
fulltext_cache_key.revision = rep->revision;
fulltext_cache_key.second = rep->offset;
if (ffd->fulltext_cache && SVN_IS_VALID_REVNUM(rep->revision)
&& fulltext_size_is_cachable(ffd, len))
{
svn_stringbuf_t *fulltext;
svn_boolean_t is_cached;
SVN_ERR(svn_cache__get((void **) &fulltext, &is_cached,
ffd->fulltext_cache, &fulltext_cache_key,
pool));
if (is_cached)
{
*contents_p = svn_stream_from_stringbuf(fulltext, pool);
return SVN_NO_ERROR;
}
}
else
fulltext_cache_key.revision = SVN_INVALID_REVNUM;
SVN_ERR(rep_read_get_baton(&rb, fs, rep, fulltext_cache_key, pool));
*contents_p = svn_stream_create(rb, pool);
svn_stream_set_read(*contents_p, rep_read_contents);
svn_stream_set_close(*contents_p, rep_read_contents_close);
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__get_contents(svn_stream_t **contents_p,
svn_fs_t *fs,
node_revision_t *noderev,
apr_pool_t *pool)
{
return read_representation(contents_p, fs, noderev->data_rep, pool);
}
/* Baton used when reading delta windows. */
struct delta_read_baton
{
struct rep_state *rs;
svn_checksum_t *checksum;
};
/* This implements the svn_txdelta_next_window_fn_t interface. */
static svn_error_t *
delta_read_next_window(svn_txdelta_window_t **window, void *baton,
apr_pool_t *pool)
{
struct delta_read_baton *drb = baton;
if (drb->rs->off == drb->rs->end)
{
*window = NULL;
return SVN_NO_ERROR;
}
return read_delta_window(window, drb->rs->chunk_index, drb->rs, pool);
}
/* This implements the svn_txdelta_md5_digest_fn_t interface. */
static const unsigned char *
delta_read_md5_digest(void *baton)
{
struct delta_read_baton *drb = baton;
if (drb->checksum->kind == svn_checksum_md5)
return drb->checksum->digest;
else
return NULL;
}
svn_error_t *
svn_fs_fs__get_file_delta_stream(svn_txdelta_stream_t **stream_p,
svn_fs_t *fs,
node_revision_t *source,
node_revision_t *target,
apr_pool_t *pool)
{
svn_stream_t *source_stream, *target_stream;
/* Try a shortcut: if the target is stored as a delta against the source,
then just use that delta. */
if (source && source->data_rep && target->data_rep)
{
struct rep_state *rep_state;
struct rep_args *rep_args;
/* Read target's base rep if any. */
SVN_ERR(create_rep_state(&rep_state, &rep_args, NULL, NULL,
target->data_rep, fs, pool));
/* If that matches source, then use this delta as is.
Note that we want an actual delta here. E.g. a self-delta would
not be good enough. */
if (rep_args->is_delta
&& rep_args->base_revision == source->data_rep->revision
&& rep_args->base_offset == source->data_rep->offset)
{
/* Create the delta read baton. */
struct delta_read_baton *drb = apr_pcalloc(pool, sizeof(*drb));
drb->rs = rep_state;
drb->checksum = svn_checksum_dup(target->data_rep->md5_checksum,
pool);
*stream_p = svn_txdelta_stream_create(drb, delta_read_next_window,
delta_read_md5_digest, pool);
return SVN_NO_ERROR;
}
else
SVN_ERR(svn_io_file_close(rep_state->file, pool));
}
/* Read both fulltexts and construct a delta. */
if (source)
SVN_ERR(read_representation(&source_stream, fs, source->data_rep, pool));
else
source_stream = svn_stream_empty(pool);
SVN_ERR(read_representation(&target_stream, fs, target->data_rep, pool));
/* Because source and target stream will already verify their content,
* there is no need to do this once more. In particular if the stream
* content is being fetched from cache. */
svn_txdelta2(stream_p, source_stream, target_stream, FALSE, pool);
return SVN_NO_ERROR;
}
/* Baton for cache_access_wrapper. Wraps the original parameters of
* svn_fs_fs__try_process_file_content().
*/
typedef struct cache_access_wrapper_baton_t
{
svn_fs_process_contents_func_t func;
void* baton;
} cache_access_wrapper_baton_t;
/* Wrapper to translate between svn_fs_process_contents_func_t and
* svn_cache__partial_getter_func_t.
*/
static svn_error_t *
cache_access_wrapper(void **out,
const void *data,
apr_size_t data_len,
void *baton,
apr_pool_t *pool)
{
cache_access_wrapper_baton_t *wrapper_baton = baton;
SVN_ERR(wrapper_baton->func((const unsigned char *)data,
data_len - 1, /* cache adds terminating 0 */
wrapper_baton->baton,
pool));
/* non-NULL value to signal the calling cache that all went well */
*out = baton;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__try_process_file_contents(svn_boolean_t *success,
svn_fs_t *fs,
node_revision_t *noderev,
svn_fs_process_contents_func_t processor,
void* baton,
apr_pool_t *pool)
{
representation_t *rep = noderev->data_rep;
if (rep)
{
fs_fs_data_t *ffd = fs->fsap_data;
pair_cache_key_t fulltext_cache_key = { 0 };
fulltext_cache_key.revision = rep->revision;
fulltext_cache_key.second = rep->offset;
if (ffd->fulltext_cache && SVN_IS_VALID_REVNUM(rep->revision)
&& fulltext_size_is_cachable(ffd, rep->expanded_size))
{
cache_access_wrapper_baton_t wrapper_baton;
void *dummy = NULL;
wrapper_baton.func = processor;
wrapper_baton.baton = baton;
return svn_cache__get_partial(&dummy, success,
ffd->fulltext_cache,
&fulltext_cache_key,
cache_access_wrapper,
&wrapper_baton,
pool);
}
}
*success = FALSE;
return SVN_NO_ERROR;
}
/* Fetch the contents of a directory into ENTRIES. Values are stored
as filename to string mappings; further conversion is necessary to
convert them into svn_fs_dirent_t values. */
static svn_error_t *
get_dir_contents(apr_hash_t *entries,
svn_fs_t *fs,
node_revision_t *noderev,
apr_pool_t *pool)
{
svn_stream_t *contents;
if (noderev->data_rep && noderev->data_rep->txn_id)
{
const char *filename = path_txn_node_children(fs, noderev->id, pool);
/* The representation is mutable. Read the old directory
contents from the mutable children file, followed by the
changes we've made in this transaction. */
SVN_ERR(svn_stream_open_readonly(&contents, filename, pool, pool));
SVN_ERR(svn_hash_read2(entries, contents, SVN_HASH_TERMINATOR, pool));
SVN_ERR(svn_hash_read_incremental(entries, contents, NULL, pool));
SVN_ERR(svn_stream_close(contents));
}
else if (noderev->data_rep)
{
/* use a temporary pool for temp objects.
* Also undeltify content before parsing it. Otherwise, we could only
* parse it byte-by-byte.
*/
apr_pool_t *text_pool = svn_pool_create(pool);
apr_size_t len = noderev->data_rep->expanded_size
? (apr_size_t)noderev->data_rep->expanded_size
: (apr_size_t)noderev->data_rep->size;
svn_stringbuf_t *text = svn_stringbuf_create_ensure(len, text_pool);
text->len = len;
/* The representation is immutable. Read it normally. */
SVN_ERR(read_representation(&contents, fs, noderev->data_rep, text_pool));
SVN_ERR(svn_stream_read(contents, text->data, &text->len));
SVN_ERR(svn_stream_close(contents));
/* de-serialize hash */
contents = svn_stream_from_stringbuf(text, text_pool);
SVN_ERR(svn_hash_read2(entries, contents, SVN_HASH_TERMINATOR, pool));
svn_pool_destroy(text_pool);
}
return SVN_NO_ERROR;
}
static const char *
unparse_dir_entry(svn_node_kind_t kind, const svn_fs_id_t *id,
apr_pool_t *pool)
{
return apr_psprintf(pool, "%s %s",
(kind == svn_node_file) ? KIND_FILE : KIND_DIR,
svn_fs_fs__id_unparse(id, pool)->data);
}
/* Given a hash ENTRIES of dirent structions, return a hash in
*STR_ENTRIES_P, that has svn_string_t as the values in the format
specified by the fs_fs directory contents file. Perform
allocations in POOL. */
static svn_error_t *
unparse_dir_entries(apr_hash_t **str_entries_p,
apr_hash_t *entries,
apr_pool_t *pool)
{
apr_hash_index_t *hi;
/* For now, we use a our own hash function to ensure that we get a
* (largely) stable order when serializing the data. It also gives
* us some performance improvement.
*
* ### TODO ###
* Use some sorted or other fixed order data container.
*/
*str_entries_p = svn_hash__make(pool);
for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
{
const void *key;
apr_ssize_t klen;
svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
const char *new_val;
apr_hash_this(hi, &key, &klen, NULL);
new_val = unparse_dir_entry(dirent->kind, dirent->id, pool);
apr_hash_set(*str_entries_p, key, klen,
svn_string_create(new_val, pool));
}
return SVN_NO_ERROR;
}
/* Given a hash STR_ENTRIES with values as svn_string_t as specified
in an FSFS directory contents listing, return a hash of dirents in
*ENTRIES_P. Perform allocations in POOL. */
static svn_error_t *
parse_dir_entries(apr_hash_t **entries_p,
apr_hash_t *str_entries,
const char *unparsed_id,
apr_pool_t *pool)
{
apr_hash_index_t *hi;
*entries_p = apr_hash_make(pool);
/* Translate the string dir entries into real entries. */
for (hi = apr_hash_first(pool, str_entries); hi; hi = apr_hash_next(hi))
{
const char *name = svn__apr_hash_index_key(hi);
svn_string_t *str_val = svn__apr_hash_index_val(hi);
char *str, *last_str;
svn_fs_dirent_t *dirent = apr_pcalloc(pool, sizeof(*dirent));
last_str = apr_pstrdup(pool, str_val->data);
dirent->name = apr_pstrdup(pool, name);
str = svn_cstring_tokenize(" ", &last_str);
if (str == NULL)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Directory entry corrupt in '%s'"),
unparsed_id);
if (strcmp(str, KIND_FILE) == 0)
{
dirent->kind = svn_node_file;
}
else if (strcmp(str, KIND_DIR) == 0)
{
dirent->kind = svn_node_dir;
}
else
{
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Directory entry corrupt in '%s'"),
unparsed_id);
}
str = svn_cstring_tokenize(" ", &last_str);
if (str == NULL)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Directory entry corrupt in '%s'"),
unparsed_id);
dirent->id = svn_fs_fs__id_parse(str, strlen(str), pool);
svn_hash_sets(*entries_p, dirent->name, dirent);
}
return SVN_NO_ERROR;
}
/* Return the cache object in FS responsible to storing the directory
* the NODEREV. If none exists, return NULL. */
static svn_cache__t *
locate_dir_cache(svn_fs_t *fs,
node_revision_t *noderev)
{
fs_fs_data_t *ffd = fs->fsap_data;
return svn_fs_fs__id_txn_id(noderev->id)
? ffd->txn_dir_cache
: ffd->dir_cache;
}
svn_error_t *
svn_fs_fs__rep_contents_dir(apr_hash_t **entries_p,
svn_fs_t *fs,
node_revision_t *noderev,
apr_pool_t *pool)
{
const char *unparsed_id = NULL;
apr_hash_t *unparsed_entries, *parsed_entries;
/* find the cache we may use */
svn_cache__t *cache = locate_dir_cache(fs, noderev);
if (cache)
{
svn_boolean_t found;
unparsed_id = svn_fs_fs__id_unparse(noderev->id, pool)->data;
SVN_ERR(svn_cache__get((void **) entries_p, &found, cache,
unparsed_id, pool));
if (found)
return SVN_NO_ERROR;
}
/* Read in the directory hash. */
unparsed_entries = apr_hash_make(pool);
SVN_ERR(get_dir_contents(unparsed_entries, fs, noderev, pool));
SVN_ERR(parse_dir_entries(&parsed_entries, unparsed_entries,
unparsed_id, pool));
/* Update the cache, if we are to use one. */
if (cache)
SVN_ERR(svn_cache__set(cache, unparsed_id, parsed_entries, pool));
*entries_p = parsed_entries;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__rep_contents_dir_entry(svn_fs_dirent_t **dirent,
svn_fs_t *fs,
node_revision_t *noderev,
const char *name,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_boolean_t found = FALSE;
/* find the cache we may use */
svn_cache__t *cache = locate_dir_cache(fs, noderev);
if (cache)
{
const char *unparsed_id =
svn_fs_fs__id_unparse(noderev->id, scratch_pool)->data;
/* Cache lookup. */
SVN_ERR(svn_cache__get_partial((void **)dirent,
&found,
cache,
unparsed_id,
svn_fs_fs__extract_dir_entry,
(void*)name,
result_pool));
}
/* fetch data from disk if we did not find it in the cache */
if (! found)
{
apr_hash_t *entries;
svn_fs_dirent_t *entry;
svn_fs_dirent_t *entry_copy = NULL;
/* read the dir from the file system. It will probably be put it
into the cache for faster lookup in future calls. */
SVN_ERR(svn_fs_fs__rep_contents_dir(&entries, fs, noderev,
scratch_pool));
/* find desired entry and return a copy in POOL, if found */
entry = svn_hash_gets(entries, name);
if (entry != NULL)
{
entry_copy = apr_palloc(result_pool, sizeof(*entry_copy));
entry_copy->name = apr_pstrdup(result_pool, entry->name);
entry_copy->id = svn_fs_fs__id_copy(entry->id, result_pool);
entry_copy->kind = entry->kind;
}
*dirent = entry_copy;
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__get_proplist(apr_hash_t **proplist_p,
svn_fs_t *fs,
node_revision_t *noderev,
apr_pool_t *pool)
{
apr_hash_t *proplist;
svn_stream_t *stream;
if (noderev->prop_rep && noderev->prop_rep->txn_id)
{
const char *filename = path_txn_node_props(fs, noderev->id, pool);
proplist = apr_hash_make(pool);
SVN_ERR(svn_stream_open_readonly(&stream, filename, pool, pool));
SVN_ERR(svn_hash_read2(proplist, stream, SVN_HASH_TERMINATOR, pool));
SVN_ERR(svn_stream_close(stream));
}
else if (noderev->prop_rep)
{
fs_fs_data_t *ffd = fs->fsap_data;
representation_t *rep = noderev->prop_rep;
pair_cache_key_t key = { 0 };
key.revision = rep->revision;
key.second = rep->offset;
if (ffd->properties_cache && SVN_IS_VALID_REVNUM(rep->revision))
{
svn_boolean_t is_cached;
SVN_ERR(svn_cache__get((void **) proplist_p, &is_cached,
ffd->properties_cache, &key, pool));
if (is_cached)
return SVN_NO_ERROR;
}
proplist = apr_hash_make(pool);
SVN_ERR(read_representation(&stream, fs, noderev->prop_rep, pool));
SVN_ERR(svn_hash_read2(proplist, stream, SVN_HASH_TERMINATOR, pool));
SVN_ERR(svn_stream_close(stream));
if (ffd->properties_cache && SVN_IS_VALID_REVNUM(rep->revision))
SVN_ERR(svn_cache__set(ffd->properties_cache, &key, proplist, pool));
}
else
{
/* return an empty prop list if the node doesn't have any props */
proplist = apr_hash_make(pool);
}
*proplist_p = proplist;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__file_length(svn_filesize_t *length,
node_revision_t *noderev,
apr_pool_t *pool)
{
if (noderev->data_rep)
*length = noderev->data_rep->expanded_size;
else
*length = 0;
return SVN_NO_ERROR;
}
svn_boolean_t
svn_fs_fs__noderev_same_rep_key(representation_t *a,
representation_t *b)
{
if (a == b)
return TRUE;
if (a == NULL || b == NULL)
return FALSE;
if (a->offset != b->offset)
return FALSE;
if (a->revision != b->revision)
return FALSE;
if (a->uniquifier == b->uniquifier)
return TRUE;
if (a->uniquifier == NULL || b->uniquifier == NULL)
return FALSE;
return strcmp(a->uniquifier, b->uniquifier) == 0;
}
svn_error_t *
svn_fs_fs__file_checksum(svn_checksum_t **checksum,
node_revision_t *noderev,
svn_checksum_kind_t kind,
apr_pool_t *pool)
{
if (noderev->data_rep)
{
switch(kind)
{
case svn_checksum_md5:
*checksum = svn_checksum_dup(noderev->data_rep->md5_checksum,
pool);
break;
case svn_checksum_sha1:
*checksum = svn_checksum_dup(noderev->data_rep->sha1_checksum,
pool);
break;
default:
*checksum = NULL;
}
}
else
*checksum = NULL;
return SVN_NO_ERROR;
}
representation_t *
svn_fs_fs__rep_copy(representation_t *rep,
apr_pool_t *pool)
{
representation_t *rep_new;
if (rep == NULL)
return NULL;
rep_new = apr_pcalloc(pool, sizeof(*rep_new));
memcpy(rep_new, rep, sizeof(*rep_new));
rep_new->md5_checksum = svn_checksum_dup(rep->md5_checksum, pool);
rep_new->sha1_checksum = svn_checksum_dup(rep->sha1_checksum, pool);
rep_new->uniquifier = apr_pstrdup(pool, rep->uniquifier);
return rep_new;
}
/* Merge the internal-use-only CHANGE into a hash of public-FS
svn_fs_path_change2_t CHANGES, collapsing multiple changes into a
single summarical (is that real word?) change per path. Also keep
the COPYFROM_CACHE up to date with new adds and replaces. */
static svn_error_t *
fold_change(apr_hash_t *changes,
const change_t *change,
apr_hash_t *copyfrom_cache)
{
apr_pool_t *pool = apr_hash_pool_get(changes);
svn_fs_path_change2_t *old_change, *new_change;
const char *path;
apr_size_t path_len = strlen(change->path);
if ((old_change = apr_hash_get(changes, change->path, path_len)))
{
/* This path already exists in the hash, so we have to merge
this change into the already existing one. */
/* Sanity check: only allow NULL node revision ID in the
`reset' case. */
if ((! change->noderev_id) && (change->kind != svn_fs_path_change_reset))
return svn_error_create
(SVN_ERR_FS_CORRUPT, NULL,
_("Missing required node revision ID"));
/* Sanity check: we should be talking about the same node
revision ID as our last change except where the last change
was a deletion. */
if (change->noderev_id
&& (! svn_fs_fs__id_eq(old_change->node_rev_id, change->noderev_id))
&& (old_change->change_kind != svn_fs_path_change_delete))
return svn_error_create
(SVN_ERR_FS_CORRUPT, NULL,
_("Invalid change ordering: new node revision ID "
"without delete"));
/* Sanity check: an add, replacement, or reset must be the first
thing to follow a deletion. */
if ((old_change->change_kind == svn_fs_path_change_delete)
&& (! ((change->kind == svn_fs_path_change_replace)
|| (change->kind == svn_fs_path_change_reset)
|| (change->kind == svn_fs_path_change_add))))
return svn_error_create
(SVN_ERR_FS_CORRUPT, NULL,
_("Invalid change ordering: non-add change on deleted path"));
/* Sanity check: an add can't follow anything except
a delete or reset. */
if ((change->kind == svn_fs_path_change_add)
&& (old_change->change_kind != svn_fs_path_change_delete)
&& (old_change->change_kind != svn_fs_path_change_reset))
return svn_error_create
(SVN_ERR_FS_CORRUPT, NULL,
_("Invalid change ordering: add change on preexisting path"));
/* Now, merge that change in. */
switch (change->kind)
{
case svn_fs_path_change_reset:
/* A reset here will simply remove the path change from the
hash. */
old_change = NULL;
break;
case svn_fs_path_change_delete:
if (old_change->change_kind == svn_fs_path_change_add)
{
/* If the path was introduced in this transaction via an
add, and we are deleting it, just remove the path
altogether. */
old_change = NULL;
}
else
{
/* A deletion overrules all previous changes. */
old_change->change_kind = svn_fs_path_change_delete;
old_change->text_mod = change->text_mod;
old_change->prop_mod = change->prop_mod;
old_change->copyfrom_rev = SVN_INVALID_REVNUM;
old_change->copyfrom_path = NULL;
}
break;
case svn_fs_path_change_add:
case svn_fs_path_change_replace:
/* An add at this point must be following a previous delete,
so treat it just like a replace. */
old_change->change_kind = svn_fs_path_change_replace;
old_change->node_rev_id = svn_fs_fs__id_copy(change->noderev_id,
pool);
old_change->text_mod = change->text_mod;
old_change->prop_mod = change->prop_mod;
if (change->copyfrom_rev == SVN_INVALID_REVNUM)
{
old_change->copyfrom_rev = SVN_INVALID_REVNUM;
old_change->copyfrom_path = NULL;
}
else
{
old_change->copyfrom_rev = change->copyfrom_rev;
old_change->copyfrom_path = apr_pstrdup(pool,
change->copyfrom_path);
}
break;
case svn_fs_path_change_modify:
default:
if (change->text_mod)
old_change->text_mod = TRUE;
if (change->prop_mod)
old_change->prop_mod = TRUE;
break;
}
/* Point our new_change to our (possibly modified) old_change. */
new_change = old_change;
}
else
{
/* This change is new to the hash, so make a new public change
structure from the internal one (in the hash's pool), and dup
the path into the hash's pool, too. */
new_change = apr_pcalloc(pool, sizeof(*new_change));
new_change->node_rev_id = svn_fs_fs__id_copy(change->noderev_id, pool);
new_change->change_kind = change->kind;
new_change->text_mod = change->text_mod;
new_change->prop_mod = change->prop_mod;
/* In FSFS, copyfrom_known is *always* true, since we've always
* stored copyfroms in changed paths lists. */
new_change->copyfrom_known = TRUE;
if (change->copyfrom_rev != SVN_INVALID_REVNUM)
{
new_change->copyfrom_rev = change->copyfrom_rev;
new_change->copyfrom_path = apr_pstrdup(pool, change->copyfrom_path);
}
else
{
new_change->copyfrom_rev = SVN_INVALID_REVNUM;
new_change->copyfrom_path = NULL;
}
}
if (new_change)
new_change->node_kind = change->node_kind;
/* Add (or update) this path.
Note: this key might already be present, and it would be nice to
re-use its value, but there is no way to fetch it. The API makes no
guarantees that this (new) key will not be retained. Thus, we (again)
copy the key into the target pool to ensure a proper lifetime. */
path = apr_pstrmemdup(pool, change->path, path_len);
apr_hash_set(changes, path, path_len, new_change);
/* Update the copyfrom cache, if any. */
if (copyfrom_cache)
{
apr_pool_t *copyfrom_pool = apr_hash_pool_get(copyfrom_cache);
const char *copyfrom_string = NULL, *copyfrom_key = path;
if (new_change)
{
if (SVN_IS_VALID_REVNUM(new_change->copyfrom_rev))
copyfrom_string = apr_psprintf(copyfrom_pool, "%ld %s",
new_change->copyfrom_rev,
new_change->copyfrom_path);
else
copyfrom_string = "";
}
/* We need to allocate a copy of the key in the copyfrom_pool if
* we're not doing a deletion and if it isn't already there. */
if ( copyfrom_string
&& ( ! apr_hash_count(copyfrom_cache)
|| ! apr_hash_get(copyfrom_cache, copyfrom_key, path_len)))
copyfrom_key = apr_pstrmemdup(copyfrom_pool, copyfrom_key, path_len);
apr_hash_set(copyfrom_cache, copyfrom_key, path_len,
copyfrom_string);
}
return SVN_NO_ERROR;
}
/* The 256 is an arbitrary size large enough to hold the node id and the
* various flags. */
#define MAX_CHANGE_LINE_LEN FSFS_MAX_PATH_LEN + 256
/* Read the next entry in the changes record from file FILE and store
the resulting change in *CHANGE_P. If there is no next record,
store NULL there. Perform all allocations from POOL. */
static svn_error_t *
read_change(change_t **change_p,
apr_file_t *file,
apr_pool_t *pool)
{
char buf[MAX_CHANGE_LINE_LEN];
apr_size_t len = sizeof(buf);
change_t *change;
char *str, *last_str = buf, *kind_str;
svn_error_t *err;
/* Default return value. */
*change_p = NULL;
err = svn_io_read_length_line(file, buf, &len, pool);
/* Check for a blank line. */
if (err || (len == 0))
{
if (err && APR_STATUS_IS_EOF(err->apr_err))
{
svn_error_clear(err);
return SVN_NO_ERROR;
}
if ((len == 0) && (! err))
return SVN_NO_ERROR;
return svn_error_trace(err);
}
change = apr_pcalloc(pool, sizeof(*change));
/* Get the node-id of the change. */
str = svn_cstring_tokenize(" ", &last_str);
if (str == NULL)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Invalid changes line in rev-file"));
change->noderev_id = svn_fs_fs__id_parse(str, strlen(str), pool);
if (change->noderev_id == NULL)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Invalid changes line in rev-file"));
/* Get the change type. */
str = svn_cstring_tokenize(" ", &last_str);
if (str == NULL)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Invalid changes line in rev-file"));
/* Don't bother to check the format number before looking for
* node-kinds: just read them if you find them. */
change->node_kind = svn_node_unknown;
kind_str = strchr(str, '-');
if (kind_str)
{
/* Cap off the end of "str" (the action). */
*kind_str = '\0';
kind_str++;
if (strcmp(kind_str, KIND_FILE) == 0)
change->node_kind = svn_node_file;
else if (strcmp(kind_str, KIND_DIR) == 0)
change->node_kind = svn_node_dir;
else
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Invalid changes line in rev-file"));
}
if (strcmp(str, ACTION_MODIFY) == 0)
{
change->kind = svn_fs_path_change_modify;
}
else if (strcmp(str, ACTION_ADD) == 0)
{
change->kind = svn_fs_path_change_add;
}
else if (strcmp(str, ACTION_DELETE) == 0)
{
change->kind = svn_fs_path_change_delete;
}
else if (strcmp(str, ACTION_REPLACE) == 0)
{
change->kind = svn_fs_path_change_replace;
}
else if (strcmp(str, ACTION_RESET) == 0)
{
change->kind = svn_fs_path_change_reset;
}
else
{
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Invalid change kind in rev file"));
}
/* Get the text-mod flag. */
str = svn_cstring_tokenize(" ", &last_str);
if (str == NULL)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Invalid changes line in rev-file"));
if (strcmp(str, FLAG_TRUE) == 0)
{
change->text_mod = TRUE;
}
else if (strcmp(str, FLAG_FALSE) == 0)
{
change->text_mod = FALSE;
}
else
{
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Invalid text-mod flag in rev-file"));
}
/* Get the prop-mod flag. */
str = svn_cstring_tokenize(" ", &last_str);
if (str == NULL)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Invalid changes line in rev-file"));
if (strcmp(str, FLAG_TRUE) == 0)
{
change->prop_mod = TRUE;
}
else if (strcmp(str, FLAG_FALSE) == 0)
{
change->prop_mod = FALSE;
}
else
{
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Invalid prop-mod flag in rev-file"));
}
/* Get the changed path. */
change->path = apr_pstrdup(pool, last_str);
/* Read the next line, the copyfrom line. */
len = sizeof(buf);
SVN_ERR(svn_io_read_length_line(file, buf, &len, pool));
if (len == 0)
{
change->copyfrom_rev = SVN_INVALID_REVNUM;
change->copyfrom_path = NULL;
}
else
{
last_str = buf;
str = svn_cstring_tokenize(" ", &last_str);
if (! str)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Invalid changes line in rev-file"));
change->copyfrom_rev = SVN_STR_TO_REV(str);
if (! last_str)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Invalid changes line in rev-file"));
change->copyfrom_path = apr_pstrdup(pool, last_str);
}
*change_p = change;
return SVN_NO_ERROR;
}
/* Examine all the changed path entries in CHANGES and store them in
*CHANGED_PATHS. Folding is done to remove redundant or unnecessary
*data. Store a hash of paths to copyfrom "REV PATH" strings in
COPYFROM_HASH if it is non-NULL. If PREFOLDED is true, assume that
the changed-path entries have already been folded (by
write_final_changed_path_info) and may be out of order, so we shouldn't
remove children of replaced or deleted directories. Do all
allocations in POOL. */
static svn_error_t *
process_changes(apr_hash_t *changed_paths,
apr_hash_t *copyfrom_cache,
apr_array_header_t *changes,
svn_boolean_t prefolded,
apr_pool_t *pool)
{
apr_pool_t *iterpool = svn_pool_create(pool);
int i;
/* Read in the changes one by one, folding them into our local hash
as necessary. */
for (i = 0; i < changes->nelts; ++i)
{
change_t *change = APR_ARRAY_IDX(changes, i, change_t *);
SVN_ERR(fold_change(changed_paths, change, copyfrom_cache));
/* Now, if our change was a deletion or replacement, we have to
blow away any changes thus far on paths that are (or, were)
children of this path.
### i won't bother with another iteration pool here -- at
most we talking about a few extra dups of paths into what
is already a temporary subpool.
*/
if (((change->kind == svn_fs_path_change_delete)
|| (change->kind == svn_fs_path_change_replace))
&& ! prefolded)
{
apr_hash_index_t *hi;
/* a potential child path must contain at least 2 more chars
(the path separator plus at least one char for the name).
Also, we should not assume that all paths have been normalized
i.e. some might have trailing path separators.
*/
apr_ssize_t change_path_len = strlen(change->path);
apr_ssize_t min_child_len = change_path_len == 0
? 1
: change->path[change_path_len-1] == '/'
? change_path_len + 1
: change_path_len + 2;
/* CAUTION: This is the inner loop of an O(n^2) algorithm.
The number of changes to process may be >> 1000.
Therefore, keep the inner loop as tight as possible.
*/
for (hi = apr_hash_first(iterpool, changed_paths);
hi;
hi = apr_hash_next(hi))
{
/* KEY is the path. */
const void *path;
apr_ssize_t klen;
apr_hash_this(hi, &path, &klen, NULL);
/* If we come across a child of our path, remove it.
Call svn_dirent_is_child only if there is a chance that
this is actually a sub-path.
*/
if ( klen >= min_child_len
&& svn_dirent_is_child(change->path, path, iterpool))
apr_hash_set(changed_paths, path, klen, NULL);
}
}
/* Clear the per-iteration subpool. */
svn_pool_clear(iterpool);
}
/* Destroy the per-iteration subpool. */
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Fetch all the changes from FILE and store them in *CHANGES. Do all
allocations in POOL. */
static svn_error_t *
read_all_changes(apr_array_header_t **changes,
apr_file_t *file,
apr_pool_t *pool)
{
change_t *change;
/* pre-allocate enough room for most change lists
(will be auto-expanded as necessary) */
*changes = apr_array_make(pool, 30, sizeof(change_t *));
SVN_ERR(read_change(&change, file, pool));
while (change)
{
APR_ARRAY_PUSH(*changes, change_t*) = change;
SVN_ERR(read_change(&change, file, pool));
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__txn_changes_fetch(apr_hash_t **changed_paths_p,
svn_fs_t *fs,
const char *txn_id,
apr_pool_t *pool)
{
apr_file_t *file;
apr_hash_t *changed_paths = apr_hash_make(pool);
apr_array_header_t *changes;
apr_pool_t *scratch_pool = svn_pool_create(pool);
SVN_ERR(svn_io_file_open(&file, path_txn_changes(fs, txn_id, pool),
APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool));
SVN_ERR(read_all_changes(&changes, file, scratch_pool));
SVN_ERR(process_changes(changed_paths, NULL, changes, FALSE, pool));
svn_pool_destroy(scratch_pool);
SVN_ERR(svn_io_file_close(file, pool));
*changed_paths_p = changed_paths;
return SVN_NO_ERROR;
}
/* Fetch the list of change in revision REV in FS and return it in *CHANGES.
* Allocate the result in POOL.
*/
static svn_error_t *
get_changes(apr_array_header_t **changes,
svn_fs_t *fs,
svn_revnum_t rev,
apr_pool_t *pool)
{
apr_off_t changes_offset;
apr_file_t *revision_file;
svn_boolean_t found;
fs_fs_data_t *ffd = fs->fsap_data;
/* try cache lookup first */
if (ffd->changes_cache)
{
SVN_ERR(svn_cache__get((void **) changes, &found, ffd->changes_cache,
&rev, pool));
if (found)
return SVN_NO_ERROR;
}
/* read changes from revision file */
SVN_ERR(ensure_revision_exists(fs, rev, pool));
SVN_ERR(open_pack_or_rev_file(&revision_file, fs, rev, pool));
SVN_ERR(get_root_changes_offset(NULL, &changes_offset, revision_file, fs,
rev, pool));
SVN_ERR(svn_io_file_seek(revision_file, APR_SET, &changes_offset, pool));
SVN_ERR(read_all_changes(changes, revision_file, pool));
SVN_ERR(svn_io_file_close(revision_file, pool));
/* cache for future reference */
if (ffd->changes_cache)
SVN_ERR(svn_cache__set(ffd->changes_cache, &rev, *changes, pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__paths_changed(apr_hash_t **changed_paths_p,
svn_fs_t *fs,
svn_revnum_t rev,
apr_hash_t *copyfrom_cache,
apr_pool_t *pool)
{
apr_hash_t *changed_paths;
apr_array_header_t *changes;
apr_pool_t *scratch_pool = svn_pool_create(pool);
SVN_ERR(get_changes(&changes, fs, rev, scratch_pool));
changed_paths = svn_hash__make(pool);
SVN_ERR(process_changes(changed_paths, copyfrom_cache, changes,
TRUE, pool));
svn_pool_destroy(scratch_pool);
*changed_paths_p = changed_paths;
return SVN_NO_ERROR;
}
/* Copy a revision node-rev SRC into the current transaction TXN_ID in
the filesystem FS. This is only used to create the root of a transaction.
Allocations are from POOL. */
static svn_error_t *
create_new_txn_noderev_from_rev(svn_fs_t *fs,
const char *txn_id,
svn_fs_id_t *src,
apr_pool_t *pool)
{
node_revision_t *noderev;
const char *node_id, *copy_id;
SVN_ERR(svn_fs_fs__get_node_revision(&noderev, fs, src, pool));
if (svn_fs_fs__id_txn_id(noderev->id))
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Copying from transactions not allowed"));
noderev->predecessor_id = noderev->id;
noderev->predecessor_count++;
noderev->copyfrom_path = NULL;
noderev->copyfrom_rev = SVN_INVALID_REVNUM;
/* For the transaction root, the copyroot never changes. */
node_id = svn_fs_fs__id_node_id(noderev->id);
copy_id = svn_fs_fs__id_copy_id(noderev->id);
noderev->id = svn_fs_fs__id_txn_create(node_id, copy_id, txn_id, pool);
return svn_fs_fs__put_node_revision(fs, noderev->id, noderev, TRUE, pool);
}
/* A structure used by get_and_increment_txn_key_body(). */
struct get_and_increment_txn_key_baton {
svn_fs_t *fs;
char *txn_id;
apr_pool_t *pool;
};
/* Callback used in the implementation of create_txn_dir(). This gets
the current base 36 value in PATH_TXN_CURRENT and increments it.
It returns the original value by the baton. */
static svn_error_t *
get_and_increment_txn_key_body(void *baton, apr_pool_t *pool)
{
struct get_and_increment_txn_key_baton *cb = baton;
const char *txn_current_filename = path_txn_current(cb->fs, pool);
const char *tmp_filename;
char next_txn_id[MAX_KEY_SIZE+3];
apr_size_t len;
svn_stringbuf_t *buf;
SVN_ERR(read_content(&buf, txn_current_filename, cb->pool));
/* remove trailing newlines */
svn_stringbuf_strip_whitespace(buf);
cb->txn_id = buf->data;
len = buf->len;
/* Increment the key and add a trailing \n to the string so the
txn-current file has a newline in it. */
svn_fs_fs__next_key(cb->txn_id, &len, next_txn_id);
next_txn_id[len] = '\n';
++len;
next_txn_id[len] = '\0';
SVN_ERR(svn_io_write_unique(&tmp_filename,
svn_dirent_dirname(txn_current_filename, pool),
next_txn_id, len, svn_io_file_del_none, pool));
SVN_ERR(move_into_place(tmp_filename, txn_current_filename,
txn_current_filename, pool));
return SVN_NO_ERROR;
}
/* Create a unique directory for a transaction in FS based on revision
REV. Return the ID for this transaction in *ID_P. Use a sequence
value in the transaction ID to prevent reuse of transaction IDs. */
static svn_error_t *
create_txn_dir(const char **id_p, svn_fs_t *fs, svn_revnum_t rev,
apr_pool_t *pool)
{
struct get_and_increment_txn_key_baton cb;
const char *txn_dir;
/* Get the current transaction sequence value, which is a base-36
number, from the txn-current file, and write an
incremented value back out to the file. Place the revision
number the transaction is based off into the transaction id. */
cb.pool = pool;
cb.fs = fs;
SVN_ERR(with_txn_current_lock(fs,
get_and_increment_txn_key_body,
&cb,
pool));
*id_p = apr_psprintf(pool, "%ld-%s", rev, cb.txn_id);
txn_dir = svn_dirent_join_many(pool,
fs->path,
PATH_TXNS_DIR,
apr_pstrcat(pool, *id_p, PATH_EXT_TXN,
(char *)NULL),
NULL);
return svn_io_dir_make(txn_dir, APR_OS_DEFAULT, pool);
}
/* Create a unique directory for a transaction in FS based on revision
REV. Return the ID for this transaction in *ID_P. This
implementation is used in svn 1.4 and earlier repositories and is
kept in 1.5 and greater to support the --pre-1.4-compatible and
--pre-1.5-compatible repository creation options. Reused
transaction IDs are possible with this implementation. */
static svn_error_t *
create_txn_dir_pre_1_5(const char **id_p, svn_fs_t *fs, svn_revnum_t rev,
apr_pool_t *pool)
{
unsigned int i;
apr_pool_t *subpool;
const char *unique_path, *prefix;
/* Try to create directories named "<txndir>/<rev>-<uniqueifier>.txn". */
prefix = svn_dirent_join_many(pool, fs->path, PATH_TXNS_DIR,
apr_psprintf(pool, "%ld", rev), NULL);
subpool = svn_pool_create(pool);
for (i = 1; i <= 99999; i++)
{
svn_error_t *err;
svn_pool_clear(subpool);
unique_path = apr_psprintf(subpool, "%s-%u" PATH_EXT_TXN, prefix, i);
err = svn_io_dir_make(unique_path, APR_OS_DEFAULT, subpool);
if (! err)
{
/* We succeeded. Return the basename minus the ".txn" extension. */
const char *name = svn_dirent_basename(unique_path, subpool);
*id_p = apr_pstrndup(pool, name,
strlen(name) - strlen(PATH_EXT_TXN));
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
if (! APR_STATUS_IS_EEXIST(err->apr_err))
return svn_error_trace(err);
svn_error_clear(err);
}
return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
NULL,
_("Unable to create transaction directory "
"in '%s' for revision %ld"),
svn_dirent_local_style(fs->path, pool),
rev);
}
svn_error_t *
svn_fs_fs__create_txn(svn_fs_txn_t **txn_p,
svn_fs_t *fs,
svn_revnum_t rev,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
svn_fs_txn_t *txn;
svn_fs_id_t *root_id;
txn = apr_pcalloc(pool, sizeof(*txn));
/* Get the txn_id. */
if (ffd->format >= SVN_FS_FS__MIN_TXN_CURRENT_FORMAT)
SVN_ERR(create_txn_dir(&txn->id, fs, rev, pool));
else
SVN_ERR(create_txn_dir_pre_1_5(&txn->id, fs, rev, pool));
txn->fs = fs;
txn->base_rev = rev;
txn->vtable = &txn_vtable;
*txn_p = txn;
/* Create a new root node for this transaction. */
SVN_ERR(svn_fs_fs__rev_get_root(&root_id, fs, rev, pool));
SVN_ERR(create_new_txn_noderev_from_rev(fs, txn->id, root_id, pool));
/* Create an empty rev file. */
SVN_ERR(svn_io_file_create(path_txn_proto_rev(fs, txn->id, pool), "",
pool));
/* Create an empty rev-lock file. */
SVN_ERR(svn_io_file_create(path_txn_proto_rev_lock(fs, txn->id, pool), "",
pool));
/* Create an empty changes file. */
SVN_ERR(svn_io_file_create(path_txn_changes(fs, txn->id, pool), "",
pool));
/* Create the next-ids file. */
return svn_io_file_create(path_txn_next_ids(fs, txn->id, pool), "0 0\n",
pool);
}
/* Store the property list for transaction TXN_ID in PROPLIST.
Perform temporary allocations in POOL. */
static svn_error_t *
get_txn_proplist(apr_hash_t *proplist,
svn_fs_t *fs,
const char *txn_id,
apr_pool_t *pool)
{
svn_stream_t *stream;
/* Check for issue #3696. (When we find and fix the cause, we can change
* this to an assertion.) */
if (txn_id == NULL)
return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
_("Internal error: a null transaction id was "
"passed to get_txn_proplist()"));
/* Open the transaction properties file. */
SVN_ERR(svn_stream_open_readonly(&stream, path_txn_props(fs, txn_id, pool),
pool, pool));
/* Read in the property list. */
SVN_ERR(svn_hash_read2(proplist, stream, SVN_HASH_TERMINATOR, pool));
return svn_stream_close(stream);
}
svn_error_t *
svn_fs_fs__change_txn_prop(svn_fs_txn_t *txn,
const char *name,
const svn_string_t *value,
apr_pool_t *pool)
{
apr_array_header_t *props = apr_array_make(pool, 1, sizeof(svn_prop_t));
svn_prop_t prop;
prop.name = name;
prop.value = value;
APR_ARRAY_PUSH(props, svn_prop_t) = prop;
return svn_fs_fs__change_txn_props(txn, props, pool);
}
svn_error_t *
svn_fs_fs__change_txn_props(svn_fs_txn_t *txn,
const apr_array_header_t *props,
apr_pool_t *pool)
{
const char *txn_prop_filename;
svn_stringbuf_t *buf;
svn_stream_t *stream;
apr_hash_t *txn_prop = apr_hash_make(pool);
int i;
svn_error_t *err;
err = get_txn_proplist(txn_prop, txn->fs, txn->id, pool);
/* Here - and here only - we need to deal with the possibility that the
transaction property file doesn't yet exist. The rest of the
implementation assumes that the file exists, but we're called to set the
initial transaction properties as the transaction is being created. */
if (err && (APR_STATUS_IS_ENOENT(err->apr_err)))
svn_error_clear(err);
else if (err)
return svn_error_trace(err);
for (i = 0; i < props->nelts; i++)
{
svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
svn_hash_sets(txn_prop, prop->name, prop->value);
}
/* Create a new version of the file and write out the new props. */
/* Open the transaction properties file. */
buf = svn_stringbuf_create_ensure(1024, pool);
stream = svn_stream_from_stringbuf(buf, pool);
SVN_ERR(svn_hash_write2(txn_prop, stream, SVN_HASH_TERMINATOR, pool));
SVN_ERR(svn_stream_close(stream));
SVN_ERR(svn_io_write_unique(&txn_prop_filename,
path_txn_dir(txn->fs, txn->id, pool),
buf->data,
buf->len,
svn_io_file_del_none,
pool));
return svn_io_file_rename(txn_prop_filename,
path_txn_props(txn->fs, txn->id, pool),
pool);
}
svn_error_t *
svn_fs_fs__get_txn(transaction_t **txn_p,
svn_fs_t *fs,
const char *txn_id,
apr_pool_t *pool)
{
transaction_t *txn;
node_revision_t *noderev;
svn_fs_id_t *root_id;
txn = apr_pcalloc(pool, sizeof(*txn));
txn->proplist = apr_hash_make(pool);
SVN_ERR(get_txn_proplist(txn->proplist, fs, txn_id, pool));
root_id = svn_fs_fs__id_txn_create("0", "0", txn_id, pool);
SVN_ERR(svn_fs_fs__get_node_revision(&noderev, fs, root_id, pool));
txn->root_id = svn_fs_fs__id_copy(noderev->id, pool);
txn->base_id = svn_fs_fs__id_copy(noderev->predecessor_id, pool);
txn->copies = NULL;
*txn_p = txn;
return SVN_NO_ERROR;
}
/* Write out the currently available next node_id NODE_ID and copy_id
COPY_ID for transaction TXN_ID in filesystem FS. The next node-id is
used both for creating new unique nodes for the given transaction, as
well as uniquifying representations. Perform temporary allocations in
POOL. */
static svn_error_t *
write_next_ids(svn_fs_t *fs,
const char *txn_id,
const char *node_id,
const char *copy_id,
apr_pool_t *pool)
{
apr_file_t *file;
svn_stream_t *out_stream;
SVN_ERR(svn_io_file_open(&file, path_txn_next_ids(fs, txn_id, pool),
APR_WRITE | APR_TRUNCATE,
APR_OS_DEFAULT, pool));
out_stream = svn_stream_from_aprfile2(file, TRUE, pool);
SVN_ERR(svn_stream_printf(out_stream, pool, "%s %s\n", node_id, copy_id));
SVN_ERR(svn_stream_close(out_stream));
return svn_io_file_close(file, pool);
}
/* Find out what the next unique node-id and copy-id are for
transaction TXN_ID in filesystem FS. Store the results in *NODE_ID
and *COPY_ID. The next node-id is used both for creating new unique
nodes for the given transaction, as well as uniquifying representations.
Perform all allocations in POOL. */
static svn_error_t *
read_next_ids(const char **node_id,
const char **copy_id,
svn_fs_t *fs,
const char *txn_id,
apr_pool_t *pool)
{
apr_file_t *file;
char buf[MAX_KEY_SIZE*2+3];
apr_size_t limit;
char *str, *last_str = buf;
SVN_ERR(svn_io_file_open(&file, path_txn_next_ids(fs, txn_id, pool),
APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool));
limit = sizeof(buf);
SVN_ERR(svn_io_read_length_line(file, buf, &limit, pool));
SVN_ERR(svn_io_file_close(file, pool));
/* Parse this into two separate strings. */
str = svn_cstring_tokenize(" ", &last_str);
if (! str)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("next-id file corrupt"));
*node_id = apr_pstrdup(pool, str);
str = svn_cstring_tokenize(" ", &last_str);
if (! str)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("next-id file corrupt"));
*copy_id = apr_pstrdup(pool, str);
return SVN_NO_ERROR;
}
/* Get a new and unique to this transaction node-id for transaction
TXN_ID in filesystem FS. Store the new node-id in *NODE_ID_P.
Node-ids are guaranteed to be unique to this transction, but may
not necessarily be sequential. Perform all allocations in POOL. */
static svn_error_t *
get_new_txn_node_id(const char **node_id_p,
svn_fs_t *fs,
const char *txn_id,
apr_pool_t *pool)
{
const char *cur_node_id, *cur_copy_id;
char *node_id;
apr_size_t len;
/* First read in the current next-ids file. */
SVN_ERR(read_next_ids(&cur_node_id, &cur_copy_id, fs, txn_id, pool));
node_id = apr_pcalloc(pool, strlen(cur_node_id) + 2);
len = strlen(cur_node_id);
svn_fs_fs__next_key(cur_node_id, &len, node_id);
SVN_ERR(write_next_ids(fs, txn_id, node_id, cur_copy_id, pool));
*node_id_p = apr_pstrcat(pool, "_", cur_node_id, (char *)NULL);
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__create_node(const svn_fs_id_t **id_p,
svn_fs_t *fs,
node_revision_t *noderev,
const char *copy_id,
const char *txn_id,
apr_pool_t *pool)
{
const char *node_id;
const svn_fs_id_t *id;
/* Get a new node-id for this node. */
SVN_ERR(get_new_txn_node_id(&node_id, fs, txn_id, pool));
id = svn_fs_fs__id_txn_create(node_id, copy_id, txn_id, pool);
noderev->id = id;
SVN_ERR(svn_fs_fs__put_node_revision(fs, noderev->id, noderev, FALSE, pool));
*id_p = id;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__purge_txn(svn_fs_t *fs,
const char *txn_id,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
/* Remove the shared transaction object associated with this transaction. */
SVN_ERR(purge_shared_txn(fs, txn_id, pool));
/* Remove the directory associated with this transaction. */
SVN_ERR(svn_io_remove_dir2(path_txn_dir(fs, txn_id, pool), FALSE,
NULL, NULL, pool));
if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
{
/* Delete protorev and its lock, which aren't in the txn
directory. It's OK if they don't exist (for example, if this
is post-commit and the proto-rev has been moved into
place). */
SVN_ERR(svn_io_remove_file2(path_txn_proto_rev(fs, txn_id, pool),
TRUE, pool));
SVN_ERR(svn_io_remove_file2(path_txn_proto_rev_lock(fs, txn_id, pool),
TRUE, pool));
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__abort_txn(svn_fs_txn_t *txn,
apr_pool_t *pool)
{
SVN_ERR(svn_fs__check_fs(txn->fs, TRUE));
/* Now, purge the transaction. */
SVN_ERR_W(svn_fs_fs__purge_txn(txn->fs, txn->id, pool),
apr_psprintf(pool, _("Transaction '%s' cleanup failed"),
txn->id));
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__set_entry(svn_fs_t *fs,
const char *txn_id,
node_revision_t *parent_noderev,
const char *name,
const svn_fs_id_t *id,
svn_node_kind_t kind,
apr_pool_t *pool)
{
representation_t *rep = parent_noderev->data_rep;
const char *filename = path_txn_node_children(fs, parent_noderev->id, pool);
apr_file_t *file;
svn_stream_t *out;
fs_fs_data_t *ffd = fs->fsap_data;
apr_pool_t *subpool = svn_pool_create(pool);
if (!rep || !rep->txn_id)
{
const char *unique_suffix;
apr_hash_t *entries;
/* Before we can modify the directory, we need to dump its old
contents into a mutable representation file. */
SVN_ERR(svn_fs_fs__rep_contents_dir(&entries, fs, parent_noderev,
subpool));
SVN_ERR(unparse_dir_entries(&entries, entries, subpool));
SVN_ERR(svn_io_file_open(&file, filename,
APR_WRITE | APR_CREATE | APR_BUFFERED,
APR_OS_DEFAULT, pool));
out = svn_stream_from_aprfile2(file, TRUE, pool);
SVN_ERR(svn_hash_write2(entries, out, SVN_HASH_TERMINATOR, subpool));
svn_pool_clear(subpool);
/* Mark the node-rev's data rep as mutable. */
rep = apr_pcalloc(pool, sizeof(*rep));
rep->revision = SVN_INVALID_REVNUM;
rep->txn_id = txn_id;
- SVN_ERR(get_new_txn_node_id(&unique_suffix, fs, txn_id, pool));
- rep->uniquifier = apr_psprintf(pool, "%s/%s", txn_id, unique_suffix);
+
+ if (ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT)
+ {
+ SVN_ERR(get_new_txn_node_id(&unique_suffix, fs, txn_id, pool));
+ rep->uniquifier = apr_psprintf(pool, "%s/%s", txn_id, unique_suffix);
+ }
+
parent_noderev->data_rep = rep;
SVN_ERR(svn_fs_fs__put_node_revision(fs, parent_noderev->id,
parent_noderev, FALSE, pool));
}
else
{
/* The directory rep is already mutable, so just open it for append. */
SVN_ERR(svn_io_file_open(&file, filename, APR_WRITE | APR_APPEND,
APR_OS_DEFAULT, pool));
out = svn_stream_from_aprfile2(file, TRUE, pool);
}
/* if we have a directory cache for this transaction, update it */
if (ffd->txn_dir_cache)
{
/* build parameters: (name, new entry) pair */
const char *key =
svn_fs_fs__id_unparse(parent_noderev->id, subpool)->data;
replace_baton_t baton;
baton.name = name;
baton.new_entry = NULL;
if (id)
{
baton.new_entry = apr_pcalloc(subpool, sizeof(*baton.new_entry));
baton.new_entry->name = name;
baton.new_entry->kind = kind;
baton.new_entry->id = id;
}
/* actually update the cached directory (if cached) */
SVN_ERR(svn_cache__set_partial(ffd->txn_dir_cache, key,
svn_fs_fs__replace_dir_entry, &baton,
subpool));
}
svn_pool_clear(subpool);
/* Append an incremental hash entry for the entry change. */
if (id)
{
const char *val = unparse_dir_entry(kind, id, subpool);
SVN_ERR(svn_stream_printf(out, subpool, "K %" APR_SIZE_T_FMT "\n%s\n"
"V %" APR_SIZE_T_FMT "\n%s\n",
strlen(name), name,
strlen(val), val));
}
else
{
SVN_ERR(svn_stream_printf(out, subpool, "D %" APR_SIZE_T_FMT "\n%s\n",
strlen(name), name));
}
SVN_ERR(svn_io_file_close(file, subpool));
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
/* Write a single change entry, path PATH, change CHANGE, and copyfrom
string COPYFROM, into the file specified by FILE. Only include the
node kind field if INCLUDE_NODE_KIND is true. All temporary
allocations are in POOL. */
static svn_error_t *
write_change_entry(apr_file_t *file,
const char *path,
svn_fs_path_change2_t *change,
svn_boolean_t include_node_kind,
apr_pool_t *pool)
{
const char *idstr, *buf;
const char *change_string = NULL;
const char *kind_string = "";
switch (change->change_kind)
{
case svn_fs_path_change_modify:
change_string = ACTION_MODIFY;
break;
case svn_fs_path_change_add:
change_string = ACTION_ADD;
break;
case svn_fs_path_change_delete:
change_string = ACTION_DELETE;
break;
case svn_fs_path_change_replace:
change_string = ACTION_REPLACE;
break;
case svn_fs_path_change_reset:
change_string = ACTION_RESET;
break;
default:
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Invalid change type %d"),
change->change_kind);
}
if (change->node_rev_id)
idstr = svn_fs_fs__id_unparse(change->node_rev_id, pool)->data;
else
idstr = ACTION_RESET;
if (include_node_kind)
{
SVN_ERR_ASSERT(change->node_kind == svn_node_dir
|| change->node_kind == svn_node_file);
kind_string = apr_psprintf(pool, "-%s",
change->node_kind == svn_node_dir
? KIND_DIR : KIND_FILE);
}
buf = apr_psprintf(pool, "%s %s%s %s %s %s\n",
idstr, change_string, kind_string,
change->text_mod ? FLAG_TRUE : FLAG_FALSE,
change->prop_mod ? FLAG_TRUE : FLAG_FALSE,
path);
SVN_ERR(svn_io_file_write_full(file, buf, strlen(buf), NULL, pool));
if (SVN_IS_VALID_REVNUM(change->copyfrom_rev))
{
buf = apr_psprintf(pool, "%ld %s", change->copyfrom_rev,
change->copyfrom_path);
SVN_ERR(svn_io_file_write_full(file, buf, strlen(buf), NULL, pool));
}
return svn_io_file_write_full(file, "\n", 1, NULL, pool);
}
svn_error_t *
svn_fs_fs__add_change(svn_fs_t *fs,
const char *txn_id,
const char *path,
const svn_fs_id_t *id,
svn_fs_path_change_kind_t change_kind,
svn_boolean_t text_mod,
svn_boolean_t prop_mod,
svn_node_kind_t node_kind,
svn_revnum_t copyfrom_rev,
const char *copyfrom_path,
apr_pool_t *pool)
{
apr_file_t *file;
svn_fs_path_change2_t *change;
SVN_ERR(svn_io_file_open(&file, path_txn_changes(fs, txn_id, pool),
APR_APPEND | APR_WRITE | APR_CREATE
| APR_BUFFERED, APR_OS_DEFAULT, pool));
change = svn_fs__path_change_create_internal(id, change_kind, pool);
change->text_mod = text_mod;
change->prop_mod = prop_mod;
change->node_kind = node_kind;
change->copyfrom_rev = copyfrom_rev;
change->copyfrom_path = apr_pstrdup(pool, copyfrom_path);
SVN_ERR(write_change_entry(file, path, change, TRUE, pool));
return svn_io_file_close(file, pool);
}
/* This baton is used by the representation writing streams. It keeps
track of the checksum information as well as the total size of the
representation so far. */
struct rep_write_baton
{
/* The FS we are writing to. */
svn_fs_t *fs;
/* Actual file to which we are writing. */
svn_stream_t *rep_stream;
/* A stream from the delta combiner. Data written here gets
deltified, then eventually written to rep_stream. */
svn_stream_t *delta_stream;
/* Where is this representation header stored. */
apr_off_t rep_offset;
/* Start of the actual data. */
apr_off_t delta_start;
/* How many bytes have been written to this rep already. */
svn_filesize_t rep_size;
/* The node revision for which we're writing out info. */
node_revision_t *noderev;
/* Actual output file. */
apr_file_t *file;
/* Lock 'cookie' used to unlock the output file once we've finished
writing to it. */
void *lockcookie;
svn_checksum_ctx_t *md5_checksum_ctx;
svn_checksum_ctx_t *sha1_checksum_ctx;
apr_pool_t *pool;
apr_pool_t *parent_pool;
};
/* Handler for the write method of the representation writable stream.
BATON is a rep_write_baton, DATA is the data to write, and *LEN is
the length of this data. */
static svn_error_t *
rep_write_contents(void *baton,
const char *data,
apr_size_t *len)
{
struct rep_write_baton *b = baton;
SVN_ERR(svn_checksum_update(b->md5_checksum_ctx, data, *len));
SVN_ERR(svn_checksum_update(b->sha1_checksum_ctx, data, *len));
b->rep_size += *len;
/* If we are writing a delta, use that stream. */
if (b->delta_stream)
return svn_stream_write(b->delta_stream, data, len);
else
return svn_stream_write(b->rep_stream, data, len);
}
/* Given a node-revision NODEREV in filesystem FS, return the
representation in *REP to use as the base for a text representation
delta if PROPS is FALSE. If PROPS has been set, a suitable props
base representation will be returned. Perform temporary allocations
in *POOL. */
static svn_error_t *
choose_delta_base(representation_t **rep,
svn_fs_t *fs,
node_revision_t *noderev,
svn_boolean_t props,
apr_pool_t *pool)
{
int count;
int walk;
node_revision_t *base;
fs_fs_data_t *ffd = fs->fsap_data;
svn_boolean_t maybe_shared_rep = FALSE;
/* If we have no predecessors, then use the empty stream as a
base. */
if (! noderev->predecessor_count)
{
*rep = NULL;
return SVN_NO_ERROR;
}
/* Flip the rightmost '1' bit of the predecessor count to determine
which file rev (counting from 0) we want to use. (To see why
count & (count - 1) unsets the rightmost set bit, think about how
you decrement a binary number.) */
count = noderev->predecessor_count;
count = count & (count - 1);
/* We use skip delta for limiting the number of delta operations
along very long node histories. Close to HEAD however, we create
a linear history to minimize delta size. */
walk = noderev->predecessor_count - count;
if (walk < (int)ffd->max_linear_deltification)
count = noderev->predecessor_count - 1;
/* Finding the delta base over a very long distance can become extremely
expensive for very deep histories, possibly causing client timeouts etc.
OTOH, this is a rare operation and its gains are minimal. Lets simply
start deltification anew close every other 1000 changes or so. */
if (walk > (int)ffd->max_deltification_walk)
{
*rep = NULL;
return SVN_NO_ERROR;
}
/* Walk back a number of predecessors equal to the difference
between count and the original predecessor count. (For example,
if noderev has ten predecessors and we want the eighth file rev,
walk back two predecessors.) */
base = noderev;
while ((count++) < noderev->predecessor_count)
{
SVN_ERR(svn_fs_fs__get_node_revision(&base, fs,
base->predecessor_id, pool));
/* If there is a shared rep along the way, we need to limit the
* length of the deltification chain.
*
* Please note that copied nodes - such as branch directories - will
* look the same (false positive) while reps shared within the same
* revision will not be caught (false negative).
*/
if (props)
{
if ( base->prop_rep
&& svn_fs_fs__id_rev(base->id) > base->prop_rep->revision)
maybe_shared_rep = TRUE;
}
else
{
if ( base->data_rep
&& svn_fs_fs__id_rev(base->id) > base->data_rep->revision)
maybe_shared_rep = TRUE;
}
}
/* return a suitable base representation */
*rep = props ? base->prop_rep : base->data_rep;
/* if we encountered a shared rep, it's parent chain may be different
* from the node-rev parent chain. */
if (*rep && maybe_shared_rep)
{
/* Check whether the length of the deltification chain is acceptable.
* Otherwise, shared reps may form a non-skipping delta chain in
* extreme cases. */
apr_pool_t *sub_pool = svn_pool_create(pool);
representation_t base_rep = **rep;
/* Some reasonable limit, depending on how acceptable longer linear
* chains are in this repo. Also, allow for some minimal chain. */
int max_chain_length = 2 * (int)ffd->max_linear_deltification + 2;
/* re-use open files between iterations */
svn_revnum_t rev_hint = SVN_INVALID_REVNUM;
apr_file_t *file_hint = NULL;
/* follow the delta chain towards the end but for at most
* MAX_CHAIN_LENGTH steps. */
for (; max_chain_length; --max_chain_length)
{
struct rep_state *rep_state;
struct rep_args *rep_args;
SVN_ERR(create_rep_state_body(&rep_state,
&rep_args,
&file_hint,
&rev_hint,
&base_rep,
fs,
sub_pool));
if (!rep_args->is_delta || !rep_args->base_revision)
break;
base_rep.revision = rep_args->base_revision;
base_rep.offset = rep_args->base_offset;
base_rep.size = rep_args->base_length;
base_rep.txn_id = NULL;
}
/* start new delta chain if the current one has grown too long */
if (max_chain_length == 0)
*rep = NULL;
svn_pool_destroy(sub_pool);
}
/* verify that the reps don't form a degenerated '*/
return SVN_NO_ERROR;
}
/* Something went wrong and the pool for the rep write is being
cleared before we've finished writing the rep. So we need
to remove the rep from the protorevfile and we need to unlock
the protorevfile. */
static apr_status_t
rep_write_cleanup(void *data)
{
struct rep_write_baton *b = data;
const char *txn_id = svn_fs_fs__id_txn_id(b->noderev->id);
svn_error_t *err;
/* Truncate and close the protorevfile. */
err = svn_io_file_trunc(b->file, b->rep_offset, b->pool);
err = svn_error_compose_create(err, svn_io_file_close(b->file, b->pool));
/* Remove our lock regardless of any preceeding errors so that the
being_written flag is always removed and stays consistent with the
file lock which will be removed no matter what since the pool is
going away. */
err = svn_error_compose_create(err, unlock_proto_rev(b->fs, txn_id,
b->lockcookie, b->pool));
if (err)
{
apr_status_t rc = err->apr_err;
svn_error_clear(err);
return rc;
}
return APR_SUCCESS;
}
/* Get a rep_write_baton and store it in *WB_P for the representation
indicated by NODEREV in filesystem FS. Perform allocations in
POOL. Only appropriate for file contents, not for props or
directory contents. */
static svn_error_t *
rep_write_get_baton(struct rep_write_baton **wb_p,
svn_fs_t *fs,
node_revision_t *noderev,
apr_pool_t *pool)
{
struct rep_write_baton *b;
apr_file_t *file;
representation_t *base_rep;
svn_stream_t *source;
const char *header;
svn_txdelta_window_handler_t wh;
void *whb;
fs_fs_data_t *ffd = fs->fsap_data;
int diff_version = ffd->format >= SVN_FS_FS__MIN_SVNDIFF1_FORMAT ? 1 : 0;
b = apr_pcalloc(pool, sizeof(*b));
b->sha1_checksum_ctx = svn_checksum_ctx_create(svn_checksum_sha1, pool);
b->md5_checksum_ctx = svn_checksum_ctx_create(svn_checksum_md5, pool);
b->fs = fs;
b->parent_pool = pool;
b->pool = svn_pool_create(pool);
b->rep_size = 0;
b->noderev = noderev;
/* Open the prototype rev file and seek to its end. */
SVN_ERR(get_writable_proto_rev(&file, &b->lockcookie,
fs, svn_fs_fs__id_txn_id(noderev->id),
b->pool));
b->file = file;
b->rep_stream = svn_stream_from_aprfile2(file, TRUE, b->pool);
SVN_ERR(get_file_offset(&b->rep_offset, file, b->pool));
/* Get the base for this delta. */
SVN_ERR(choose_delta_base(&base_rep, fs, noderev, FALSE, b->pool));
SVN_ERR(read_representation(&source, fs, base_rep, b->pool));
/* Write out the rep header. */
if (base_rep)
{
header = apr_psprintf(b->pool, REP_DELTA " %ld %" APR_OFF_T_FMT " %"
SVN_FILESIZE_T_FMT "\n",
base_rep->revision, base_rep->offset,
base_rep->size);
}
else
{
header = REP_DELTA "\n";
}
SVN_ERR(svn_io_file_write_full(file, header, strlen(header), NULL,
b->pool));
/* Now determine the offset of the actual svndiff data. */
SVN_ERR(get_file_offset(&b->delta_start, file, b->pool));
/* Cleanup in case something goes wrong. */
apr_pool_cleanup_register(b->pool, b, rep_write_cleanup,
apr_pool_cleanup_null);
/* Prepare to write the svndiff data. */
svn_txdelta_to_svndiff3(&wh,
&whb,
b->rep_stream,
diff_version,
SVN_DELTA_COMPRESSION_LEVEL_DEFAULT,
pool);
b->delta_stream = svn_txdelta_target_push(wh, whb, source, b->pool);
*wb_p = b;
return SVN_NO_ERROR;
}
/* For the hash REP->SHA1, try to find an already existing representation
in FS and return it in *OUT_REP. If no such representation exists or
if rep sharing has been disabled for FS, NULL will be returned. Since
there may be new duplicate representations within the same uncommitted
revision, those can be passed in REPS_HASH (maps a sha1 digest onto
representation_t*), otherwise pass in NULL for REPS_HASH.
POOL will be used for allocations. The lifetime of the returned rep is
limited by both, POOL and REP lifetime.
*/
static svn_error_t *
get_shared_rep(representation_t **old_rep,
svn_fs_t *fs,
representation_t *rep,
apr_hash_t *reps_hash,
apr_pool_t *pool)
{
svn_error_t *err;
fs_fs_data_t *ffd = fs->fsap_data;
/* Return NULL, if rep sharing has been disabled. */
*old_rep = NULL;
if (!ffd->rep_sharing_allowed)
return SVN_NO_ERROR;
/* Check and see if we already have a representation somewhere that's
identical to the one we just wrote out. Start with the hash lookup
because it is cheepest. */
if (reps_hash)
*old_rep = apr_hash_get(reps_hash,
rep->sha1_checksum->digest,
APR_SHA1_DIGESTSIZE);
/* If we haven't found anything yet, try harder and consult our DB. */
if (*old_rep == NULL)
{
err = svn_fs_fs__get_rep_reference(old_rep, fs, rep->sha1_checksum,
pool);
/* ### Other error codes that we shouldn't mask out? */
if (err == SVN_NO_ERROR)
{
if (*old_rep)
SVN_ERR(verify_walker(*old_rep, NULL, fs, pool));
}
else if (err->apr_err == SVN_ERR_FS_CORRUPT
|| SVN_ERROR_IN_CATEGORY(err->apr_err,
SVN_ERR_MALFUNC_CATEGORY_START))
{
/* Fatal error; don't mask it.
In particular, this block is triggered when the rep-cache refers
to revisions in the future. We signal that as a corruption situation
since, once those revisions are less than youngest (because of more
commits), the rep-cache would be invalid.
*/
SVN_ERR(err);
}
else
{
/* Something's wrong with the rep-sharing index. We can continue
without rep-sharing, but warn.
*/
(fs->warning)(fs->warning_baton, err);
svn_error_clear(err);
*old_rep = NULL;
}
}
/* look for intra-revision matches (usually data reps but not limited
to them in case props happen to look like some data rep)
*/
if (*old_rep == NULL && rep->txn_id)
{
svn_node_kind_t kind;
const char *file_name
= path_txn_sha1(fs, rep->txn_id, rep->sha1_checksum, pool);
/* in our txn, is there a rep file named with the wanted SHA1?
If so, read it and use that rep.
*/
SVN_ERR(svn_io_check_path(file_name, &kind, pool));
if (kind == svn_node_file)
{
svn_stringbuf_t *rep_string;
SVN_ERR(svn_stringbuf_from_file2(&rep_string, file_name, pool));
SVN_ERR(read_rep_offsets_body(old_rep, rep_string->data,
rep->txn_id, FALSE, pool));
}
}
/* Add information that is missing in the cached data. */
if (*old_rep)
{
/* Use the old rep for this content. */
(*old_rep)->md5_checksum = rep->md5_checksum;
(*old_rep)->uniquifier = rep->uniquifier;
}
return SVN_NO_ERROR;
}
/* Close handler for the representation write stream. BATON is a
rep_write_baton. Writes out a new node-rev that correctly
references the representation we just finished writing. */
static svn_error_t *
rep_write_contents_close(void *baton)
{
struct rep_write_baton *b = baton;
const char *unique_suffix;
representation_t *rep;
representation_t *old_rep;
apr_off_t offset;
+ fs_fs_data_t *ffd = b->fs->fsap_data;
rep = apr_pcalloc(b->parent_pool, sizeof(*rep));
rep->offset = b->rep_offset;
/* Close our delta stream so the last bits of svndiff are written
out. */
if (b->delta_stream)
SVN_ERR(svn_stream_close(b->delta_stream));
/* Determine the length of the svndiff data. */
SVN_ERR(get_file_offset(&offset, b->file, b->pool));
rep->size = offset - b->delta_start;
/* Fill in the rest of the representation field. */
rep->expanded_size = b->rep_size;
rep->txn_id = svn_fs_fs__id_txn_id(b->noderev->id);
- SVN_ERR(get_new_txn_node_id(&unique_suffix, b->fs, rep->txn_id, b->pool));
- rep->uniquifier = apr_psprintf(b->parent_pool, "%s/%s", rep->txn_id,
- unique_suffix);
+
+ if (ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT)
+ {
+ SVN_ERR(get_new_txn_node_id(&unique_suffix, b->fs, rep->txn_id, b->pool));
+ rep->uniquifier = apr_psprintf(b->parent_pool, "%s/%s", rep->txn_id,
+ unique_suffix);
+ }
rep->revision = SVN_INVALID_REVNUM;
/* Finalize the checksum. */
SVN_ERR(svn_checksum_final(&rep->md5_checksum, b->md5_checksum_ctx,
b->parent_pool));
SVN_ERR(svn_checksum_final(&rep->sha1_checksum, b->sha1_checksum_ctx,
b->parent_pool));
/* Check and see if we already have a representation somewhere that's
identical to the one we just wrote out. */
SVN_ERR(get_shared_rep(&old_rep, b->fs, rep, NULL, b->parent_pool));
if (old_rep)
{
/* We need to erase from the protorev the data we just wrote. */
SVN_ERR(svn_io_file_trunc(b->file, b->rep_offset, b->pool));
/* Use the old rep for this content. */
b->noderev->data_rep = old_rep;
}
else
{
/* Write out our cosmetic end marker. */
SVN_ERR(svn_stream_puts(b->rep_stream, "ENDREP\n"));
b->noderev->data_rep = rep;
}
/* Remove cleanup callback. */
apr_pool_cleanup_kill(b->pool, b, rep_write_cleanup);
/* Write out the new node-rev information. */
SVN_ERR(svn_fs_fs__put_node_revision(b->fs, b->noderev->id, b->noderev, FALSE,
b->pool));
if (!old_rep)
SVN_ERR(store_sha1_rep_mapping(b->fs, b->noderev, b->pool));
SVN_ERR(svn_io_file_close(b->file, b->pool));
SVN_ERR(unlock_proto_rev(b->fs, rep->txn_id, b->lockcookie, b->pool));
svn_pool_destroy(b->pool);
return SVN_NO_ERROR;
}
/* Store a writable stream in *CONTENTS_P that will receive all data
written and store it as the file data representation referenced by
NODEREV in filesystem FS. Perform temporary allocations in
POOL. Only appropriate for file data, not props or directory
contents. */
static svn_error_t *
set_representation(svn_stream_t **contents_p,
svn_fs_t *fs,
node_revision_t *noderev,
apr_pool_t *pool)
{
struct rep_write_baton *wb;
if (! svn_fs_fs__id_txn_id(noderev->id))
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Attempted to write to non-transaction '%s'"),
svn_fs_fs__id_unparse(noderev->id, pool)->data);
SVN_ERR(rep_write_get_baton(&wb, fs, noderev, pool));
*contents_p = svn_stream_create(wb, pool);
svn_stream_set_write(*contents_p, rep_write_contents);
svn_stream_set_close(*contents_p, rep_write_contents_close);
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__set_contents(svn_stream_t **stream,
svn_fs_t *fs,
node_revision_t *noderev,
apr_pool_t *pool)
{
if (noderev->kind != svn_node_file)
return svn_error_create(SVN_ERR_FS_NOT_FILE, NULL,
_("Can't set text contents of a directory"));
return set_representation(stream, fs, noderev, pool);
}
svn_error_t *
svn_fs_fs__create_successor(const svn_fs_id_t **new_id_p,
svn_fs_t *fs,
const svn_fs_id_t *old_idp,
node_revision_t *new_noderev,
const char *copy_id,
const char *txn_id,
apr_pool_t *pool)
{
const svn_fs_id_t *id;
if (! copy_id)
copy_id = svn_fs_fs__id_copy_id(old_idp);
id = svn_fs_fs__id_txn_create(svn_fs_fs__id_node_id(old_idp), copy_id,
txn_id, pool);
new_noderev->id = id;
if (! new_noderev->copyroot_path)
{
new_noderev->copyroot_path = apr_pstrdup(pool,
new_noderev->created_path);
new_noderev->copyroot_rev = svn_fs_fs__id_rev(new_noderev->id);
}
SVN_ERR(svn_fs_fs__put_node_revision(fs, new_noderev->id, new_noderev, FALSE,
pool));
*new_id_p = id;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__set_proplist(svn_fs_t *fs,
node_revision_t *noderev,
apr_hash_t *proplist,
apr_pool_t *pool)
{
const char *filename = path_txn_node_props(fs, noderev->id, pool);
apr_file_t *file;
svn_stream_t *out;
/* Dump the property list to the mutable property file. */
SVN_ERR(svn_io_file_open(&file, filename,
APR_WRITE | APR_CREATE | APR_TRUNCATE
| APR_BUFFERED, APR_OS_DEFAULT, pool));
out = svn_stream_from_aprfile2(file, TRUE, pool);
SVN_ERR(svn_hash_write2(proplist, out, SVN_HASH_TERMINATOR, pool));
SVN_ERR(svn_io_file_close(file, pool));
/* Mark the node-rev's prop rep as mutable, if not already done. */
if (!noderev->prop_rep || !noderev->prop_rep->txn_id)
{
noderev->prop_rep = apr_pcalloc(pool, sizeof(*noderev->prop_rep));
noderev->prop_rep->txn_id = svn_fs_fs__id_txn_id(noderev->id);
SVN_ERR(svn_fs_fs__put_node_revision(fs, noderev->id, noderev, FALSE, pool));
}
return SVN_NO_ERROR;
}
/* Read the 'current' file for filesystem FS and store the next
available node id in *NODE_ID, and the next available copy id in
*COPY_ID. Allocations are performed from POOL. */
static svn_error_t *
get_next_revision_ids(const char **node_id,
const char **copy_id,
svn_fs_t *fs,
apr_pool_t *pool)
{
char *buf;
char *str;
svn_stringbuf_t *content;
SVN_ERR(read_content(&content, svn_fs_fs__path_current(fs, pool), pool));
buf = content->data;
str = svn_cstring_tokenize(" ", &buf);
if (! str)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Corrupt 'current' file"));
str = svn_cstring_tokenize(" ", &buf);
if (! str)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Corrupt 'current' file"));
*node_id = apr_pstrdup(pool, str);
str = svn_cstring_tokenize(" \n", &buf);
if (! str)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Corrupt 'current' file"));
*copy_id = apr_pstrdup(pool, str);
return SVN_NO_ERROR;
}
/* This baton is used by the stream created for write_hash_rep. */
struct write_hash_baton
{
svn_stream_t *stream;
apr_size_t size;
svn_checksum_ctx_t *md5_ctx;
svn_checksum_ctx_t *sha1_ctx;
};
/* The handler for the write_hash_rep stream. BATON is a
write_hash_baton, DATA has the data to write and *LEN is the number
of bytes to write. */
static svn_error_t *
write_hash_handler(void *baton,
const char *data,
apr_size_t *len)
{
struct write_hash_baton *whb = baton;
SVN_ERR(svn_checksum_update(whb->md5_ctx, data, *len));
SVN_ERR(svn_checksum_update(whb->sha1_ctx, data, *len));
SVN_ERR(svn_stream_write(whb->stream, data, len));
whb->size += *len;
return SVN_NO_ERROR;
}
/* Write out the hash HASH as a text representation to file FILE. In
the process, record position, the total size of the dump and MD5 as
well as SHA1 in REP. If rep sharing has been enabled and REPS_HASH
is not NULL, it will be used in addition to the on-disk cache to find
earlier reps with the same content. When such existing reps can be
found, we will truncate the one just written from the file and return
the existing rep. Perform temporary allocations in POOL. */
static svn_error_t *
write_hash_rep(representation_t *rep,
apr_file_t *file,
apr_hash_t *hash,
svn_fs_t *fs,
apr_hash_t *reps_hash,
apr_pool_t *pool)
{
svn_stream_t *stream;
struct write_hash_baton *whb;
representation_t *old_rep;
SVN_ERR(get_file_offset(&rep->offset, file, pool));
whb = apr_pcalloc(pool, sizeof(*whb));
whb->stream = svn_stream_from_aprfile2(file, TRUE, pool);
whb->size = 0;
whb->md5_ctx = svn_checksum_ctx_create(svn_checksum_md5, pool);
whb->sha1_ctx = svn_checksum_ctx_create(svn_checksum_sha1, pool);
stream = svn_stream_create(whb, pool);
svn_stream_set_write(stream, write_hash_handler);
SVN_ERR(svn_stream_puts(whb->stream, "PLAIN\n"));
SVN_ERR(svn_hash_write2(hash, stream, SVN_HASH_TERMINATOR, pool));
/* Store the results. */
SVN_ERR(svn_checksum_final(&rep->md5_checksum, whb->md5_ctx, pool));
SVN_ERR(svn_checksum_final(&rep->sha1_checksum, whb->sha1_ctx, pool));
/* Check and see if we already have a representation somewhere that's
identical to the one we just wrote out. */
SVN_ERR(get_shared_rep(&old_rep, fs, rep, reps_hash, pool));
if (old_rep)
{
/* We need to erase from the protorev the data we just wrote. */
SVN_ERR(svn_io_file_trunc(file, rep->offset, pool));
/* Use the old rep for this content. */
memcpy(rep, old_rep, sizeof (*rep));
}
else
{
/* Write out our cosmetic end marker. */
SVN_ERR(svn_stream_puts(whb->stream, "ENDREP\n"));
/* update the representation */
rep->size = whb->size;
- rep->expanded_size = 0;
+ rep->expanded_size = whb->size;
}
return SVN_NO_ERROR;
}
/* Write out the hash HASH pertaining to the NODEREV in FS as a deltified
text representation to file FILE. In the process, record the total size
and the md5 digest in REP. If rep sharing has been enabled and REPS_HASH
is not NULL, it will be used in addition to the on-disk cache to find
earlier reps with the same content. When such existing reps can be found,
we will truncate the one just written from the file and return the existing
rep. If PROPS is set, assume that we want to a props representation as
the base for our delta. Perform temporary allocations in POOL. */
static svn_error_t *
write_hash_delta_rep(representation_t *rep,
apr_file_t *file,
apr_hash_t *hash,
svn_fs_t *fs,
node_revision_t *noderev,
apr_hash_t *reps_hash,
svn_boolean_t props,
apr_pool_t *pool)
{
svn_txdelta_window_handler_t diff_wh;
void *diff_whb;
svn_stream_t *file_stream;
svn_stream_t *stream;
representation_t *base_rep;
representation_t *old_rep;
svn_stream_t *source;
const char *header;
apr_off_t rep_end = 0;
apr_off_t delta_start = 0;
struct write_hash_baton *whb;
fs_fs_data_t *ffd = fs->fsap_data;
int diff_version = ffd->format >= SVN_FS_FS__MIN_SVNDIFF1_FORMAT ? 1 : 0;
/* Get the base for this delta. */
SVN_ERR(choose_delta_base(&base_rep, fs, noderev, props, pool));
SVN_ERR(read_representation(&source, fs, base_rep, pool));
SVN_ERR(get_file_offset(&rep->offset, file, pool));
/* Write out the rep header. */
if (base_rep)
{
header = apr_psprintf(pool, REP_DELTA " %ld %" APR_OFF_T_FMT " %"
SVN_FILESIZE_T_FMT "\n",
base_rep->revision, base_rep->offset,
base_rep->size);
}
else
{
header = REP_DELTA "\n";
}
SVN_ERR(svn_io_file_write_full(file, header, strlen(header), NULL,
pool));
SVN_ERR(get_file_offset(&delta_start, file, pool));
file_stream = svn_stream_from_aprfile2(file, TRUE, pool);
/* Prepare to write the svndiff data. */
svn_txdelta_to_svndiff3(&diff_wh,
&diff_whb,
file_stream,
diff_version,
SVN_DELTA_COMPRESSION_LEVEL_DEFAULT,
pool);
whb = apr_pcalloc(pool, sizeof(*whb));
whb->stream = svn_txdelta_target_push(diff_wh, diff_whb, source, pool);
whb->size = 0;
whb->md5_ctx = svn_checksum_ctx_create(svn_checksum_md5, pool);
whb->sha1_ctx = svn_checksum_ctx_create(svn_checksum_sha1, pool);
/* serialize the hash */
stream = svn_stream_create(whb, pool);
svn_stream_set_write(stream, write_hash_handler);
SVN_ERR(svn_hash_write2(hash, stream, SVN_HASH_TERMINATOR, pool));
SVN_ERR(svn_stream_close(whb->stream));
/* Store the results. */
SVN_ERR(svn_checksum_final(&rep->md5_checksum, whb->md5_ctx, pool));
SVN_ERR(svn_checksum_final(&rep->sha1_checksum, whb->sha1_ctx, pool));
/* Check and see if we already have a representation somewhere that's
identical to the one we just wrote out. */
SVN_ERR(get_shared_rep(&old_rep, fs, rep, reps_hash, pool));
if (old_rep)
{
/* We need to erase from the protorev the data we just wrote. */
SVN_ERR(svn_io_file_trunc(file, rep->offset, pool));
/* Use the old rep for this content. */
memcpy(rep, old_rep, sizeof (*rep));
}
else
{
/* Write out our cosmetic end marker. */
SVN_ERR(get_file_offset(&rep_end, file, pool));
SVN_ERR(svn_stream_puts(file_stream, "ENDREP\n"));
/* update the representation */
rep->expanded_size = whb->size;
rep->size = rep_end - delta_start;
}
return SVN_NO_ERROR;
}
/* Sanity check ROOT_NODEREV, a candidate for being the root node-revision
of (not yet committed) revision REV in FS. Use POOL for temporary
allocations.
If you change this function, consider updating svn_fs_fs__verify() too.
*/
static svn_error_t *
validate_root_noderev(svn_fs_t *fs,
node_revision_t *root_noderev,
svn_revnum_t rev,
apr_pool_t *pool)
{
svn_revnum_t head_revnum = rev-1;
int head_predecessor_count;
SVN_ERR_ASSERT(rev > 0);
/* Compute HEAD_PREDECESSOR_COUNT. */
{
svn_fs_root_t *head_revision;
const svn_fs_id_t *head_root_id;
node_revision_t *head_root_noderev;
/* Get /@HEAD's noderev. */
SVN_ERR(svn_fs_fs__revision_root(&head_revision, fs, head_revnum, pool));
SVN_ERR(svn_fs_fs__node_id(&head_root_id, head_revision, "/", pool));
SVN_ERR(svn_fs_fs__get_node_revision(&head_root_noderev, fs, head_root_id,
pool));
head_predecessor_count = head_root_noderev->predecessor_count;
}
/* Check that the root noderev's predecessor count equals REV.
This kind of corruption was seen on svn.apache.org (both on
the root noderev and on other fspaths' noderevs); see
issue #4129.
Normally (rev == root_noderev->predecessor_count), but here we
use a more roundabout check that should only trigger on new instances
of the corruption, rather then trigger on each and every new commit
to a repository that has triggered the bug somewhere in its root
noderev's history.
*/
if (root_noderev->predecessor_count != -1
&& (root_noderev->predecessor_count - head_predecessor_count)
!= (rev - head_revnum))
{
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("predecessor count for "
"the root node-revision is wrong: "
"found (%d+%ld != %d), committing r%ld"),
head_predecessor_count,
rev - head_revnum, /* This is equal to 1. */
root_noderev->predecessor_count,
rev);
}
return SVN_NO_ERROR;
}
/* Copy a node-revision specified by id ID in fileystem FS from a
transaction into the proto-rev-file FILE. Set *NEW_ID_P to a
pointer to the new node-id which will be allocated in POOL.
If this is a directory, copy all children as well.
START_NODE_ID and START_COPY_ID are
the first available node and copy ids for this filesystem, for older
FS formats.
REV is the revision number that this proto-rev-file will represent.
INITIAL_OFFSET is the offset of the proto-rev-file on entry to
commit_body.
If REPS_TO_CACHE is not NULL, append to it a copy (allocated in
REPS_POOL) of each data rep that is new in this revision.
If REPS_HASH is not NULL, append copies (allocated in REPS_POOL)
of the representations of each property rep that is new in this
revision.
AT_ROOT is true if the node revision being written is the root
node-revision. It is only controls additional sanity checking
logic.
Temporary allocations are also from POOL. */
static svn_error_t *
write_final_rev(const svn_fs_id_t **new_id_p,
apr_file_t *file,
svn_revnum_t rev,
svn_fs_t *fs,
const svn_fs_id_t *id,
const char *start_node_id,
const char *start_copy_id,
apr_off_t initial_offset,
apr_array_header_t *reps_to_cache,
apr_hash_t *reps_hash,
apr_pool_t *reps_pool,
svn_boolean_t at_root,
apr_pool_t *pool)
{
node_revision_t *noderev;
apr_off_t my_offset;
char my_node_id_buf[MAX_KEY_SIZE + 2];
char my_copy_id_buf[MAX_KEY_SIZE + 2];
const svn_fs_id_t *new_id;
const char *node_id, *copy_id, *my_node_id, *my_copy_id;
fs_fs_data_t *ffd = fs->fsap_data;
*new_id_p = NULL;
/* Check to see if this is a transaction node. */
if (! svn_fs_fs__id_txn_id(id))
return SVN_NO_ERROR;
SVN_ERR(svn_fs_fs__get_node_revision(&noderev, fs, id, pool));
if (noderev->kind == svn_node_dir)
{
apr_pool_t *subpool;
apr_hash_t *entries, *str_entries;
apr_array_header_t *sorted_entries;
int i;
/* This is a directory. Write out all the children first. */
subpool = svn_pool_create(pool);
SVN_ERR(svn_fs_fs__rep_contents_dir(&entries, fs, noderev, pool));
/* For the sake of the repository administrator sort the entries
so that the final file is deterministic and repeatable,
however the rest of the FSFS code doesn't require any
particular order here. */
sorted_entries = svn_sort__hash(entries, svn_sort_compare_items_lexically,
pool);
for (i = 0; i < sorted_entries->nelts; ++i)
{
svn_fs_dirent_t *dirent = APR_ARRAY_IDX(sorted_entries, i,
svn_sort__item_t).value;
svn_pool_clear(subpool);
SVN_ERR(write_final_rev(&new_id, file, rev, fs, dirent->id,
start_node_id, start_copy_id, initial_offset,
reps_to_cache, reps_hash, reps_pool, FALSE,
subpool));
if (new_id && (svn_fs_fs__id_rev(new_id) == rev))
dirent->id = svn_fs_fs__id_copy(new_id, pool);
}
svn_pool_destroy(subpool);
if (noderev->data_rep && noderev->data_rep->txn_id)
{
/* Write out the contents of this directory as a text rep. */
SVN_ERR(unparse_dir_entries(&str_entries, entries, pool));
noderev->data_rep->txn_id = NULL;
noderev->data_rep->revision = rev;
if (ffd->deltify_directories)
SVN_ERR(write_hash_delta_rep(noderev->data_rep, file,
str_entries, fs, noderev, NULL,
FALSE, pool));
else
SVN_ERR(write_hash_rep(noderev->data_rep, file, str_entries,
fs, NULL, pool));
}
}
else
{
/* This is a file. We should make sure the data rep, if it
exists in a "this" state, gets rewritten to our new revision
num. */
if (noderev->data_rep && noderev->data_rep->txn_id)
{
noderev->data_rep->txn_id = NULL;
noderev->data_rep->revision = rev;
/* See issue 3845. Some unknown mechanism caused the
protorev file to get truncated, so check for that
here. */
if (noderev->data_rep->offset + noderev->data_rep->size
> initial_offset)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Truncated protorev file detected"));
}
}
/* Fix up the property reps. */
if (noderev->prop_rep && noderev->prop_rep->txn_id)
{
apr_hash_t *proplist;
SVN_ERR(svn_fs_fs__get_proplist(&proplist, fs, noderev, pool));
noderev->prop_rep->txn_id = NULL;
noderev->prop_rep->revision = rev;
if (ffd->deltify_properties)
SVN_ERR(write_hash_delta_rep(noderev->prop_rep, file,
proplist, fs, noderev, reps_hash,
TRUE, pool));
else
SVN_ERR(write_hash_rep(noderev->prop_rep, file, proplist,
fs, reps_hash, pool));
}
/* Convert our temporary ID into a permanent revision one. */
SVN_ERR(get_file_offset(&my_offset, file, pool));
node_id = svn_fs_fs__id_node_id(noderev->id);
if (*node_id == '_')
{
if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT)
my_node_id = apr_psprintf(pool, "%s-%ld", node_id + 1, rev);
else
{
svn_fs_fs__add_keys(start_node_id, node_id + 1, my_node_id_buf);
my_node_id = my_node_id_buf;
}
}
else
my_node_id = node_id;
copy_id = svn_fs_fs__id_copy_id(noderev->id);
if (*copy_id == '_')
{
if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT)
my_copy_id = apr_psprintf(pool, "%s-%ld", copy_id + 1, rev);
else
{
svn_fs_fs__add_keys(start_copy_id, copy_id + 1, my_copy_id_buf);
my_copy_id = my_copy_id_buf;
}
}
else
my_copy_id = copy_id;
if (noderev->copyroot_rev == SVN_INVALID_REVNUM)
noderev->copyroot_rev = rev;
new_id = svn_fs_fs__id_rev_create(my_node_id, my_copy_id, rev, my_offset,
pool);
noderev->id = new_id;
if (ffd->rep_sharing_allowed)
{
/* Save the data representation's hash in the rep cache. */
if ( noderev->data_rep && noderev->kind == svn_node_file
&& noderev->data_rep->revision == rev)
{
SVN_ERR_ASSERT(reps_to_cache && reps_pool);
APR_ARRAY_PUSH(reps_to_cache, representation_t *)
= svn_fs_fs__rep_copy(noderev->data_rep, reps_pool);
}
if (noderev->prop_rep && noderev->prop_rep->revision == rev)
{
/* Add new property reps to hash and on-disk cache. */
representation_t *copy
= svn_fs_fs__rep_copy(noderev->prop_rep, reps_pool);
SVN_ERR_ASSERT(reps_to_cache && reps_pool);
APR_ARRAY_PUSH(reps_to_cache, representation_t *) = copy;
apr_hash_set(reps_hash,
copy->sha1_checksum->digest,
APR_SHA1_DIGESTSIZE,
copy);
}
}
/* don't serialize SHA1 for dirs to disk (waste of space) */
if (noderev->data_rep && noderev->kind == svn_node_dir)
noderev->data_rep->sha1_checksum = NULL;
/* don't serialize SHA1 for props to disk (waste of space) */
if (noderev->prop_rep)
noderev->prop_rep->sha1_checksum = NULL;
/* Workaround issue #4031: is-fresh-txn-root in revision files. */
noderev->is_fresh_txn_root = FALSE;
/* Write out our new node-revision. */
if (at_root)
SVN_ERR(validate_root_noderev(fs, noderev, rev, pool));
SVN_ERR(svn_fs_fs__write_noderev(svn_stream_from_aprfile2(file, TRUE, pool),
noderev, ffd->format,
svn_fs_fs__fs_supports_mergeinfo(fs),
pool));
/* Return our ID that references the revision file. */
*new_id_p = noderev->id;
return SVN_NO_ERROR;
}
/* Write the changed path info from transaction TXN_ID in filesystem
FS to the permanent rev-file FILE. *OFFSET_P is set the to offset
in the file of the beginning of this information. Perform
temporary allocations in POOL. */
static svn_error_t *
write_final_changed_path_info(apr_off_t *offset_p,
apr_file_t *file,
svn_fs_t *fs,
const char *txn_id,
apr_pool_t *pool)
{
apr_hash_t *changed_paths;
apr_off_t offset;
apr_pool_t *iterpool = svn_pool_create(pool);
fs_fs_data_t *ffd = fs->fsap_data;
svn_boolean_t include_node_kinds =
ffd->format >= SVN_FS_FS__MIN_KIND_IN_CHANGED_FORMAT;
apr_array_header_t *sorted_changed_paths;
int i;
SVN_ERR(get_file_offset(&offset, file, pool));
SVN_ERR(svn_fs_fs__txn_changes_fetch(&changed_paths, fs, txn_id, pool));
/* For the sake of the repository administrator sort the changes so
that the final file is deterministic and repeatable, however the
rest of the FSFS code doesn't require any particular order here. */
sorted_changed_paths = svn_sort__hash(changed_paths,
svn_sort_compare_items_lexically, pool);
/* Iterate through the changed paths one at a time, and convert the
temporary node-id into a permanent one for each change entry. */
for (i = 0; i < sorted_changed_paths->nelts; ++i)
{
node_revision_t *noderev;
const svn_fs_id_t *id;
svn_fs_path_change2_t *change;
const char *path;
svn_pool_clear(iterpool);
change = APR_ARRAY_IDX(sorted_changed_paths, i, svn_sort__item_t).value;
path = APR_ARRAY_IDX(sorted_changed_paths, i, svn_sort__item_t).key;
id = change->node_rev_id;
/* If this was a delete of a mutable node, then it is OK to
leave the change entry pointing to the non-existent temporary
node, since it will never be used. */
if ((change->change_kind != svn_fs_path_change_delete) &&
(! svn_fs_fs__id_txn_id(id)))
{
SVN_ERR(svn_fs_fs__get_node_revision(&noderev, fs, id, iterpool));
/* noderev has the permanent node-id at this point, so we just
substitute it for the temporary one. */
change->node_rev_id = noderev->id;
}
/* Write out the new entry into the final rev-file. */
SVN_ERR(write_change_entry(file, path, change, include_node_kinds,
iterpool));
}
svn_pool_destroy(iterpool);
*offset_p = offset;
return SVN_NO_ERROR;
}
/* Atomically update the 'current' file to hold the specifed REV,
NEXT_NODE_ID, and NEXT_COPY_ID. (The two next-ID parameters are
ignored and may be NULL if the FS format does not use them.)
Perform temporary allocations in POOL. */
static svn_error_t *
write_current(svn_fs_t *fs, svn_revnum_t rev, const char *next_node_id,
const char *next_copy_id, apr_pool_t *pool)
{
char *buf;
const char *tmp_name, *name;
fs_fs_data_t *ffd = fs->fsap_data;
/* Now we can just write out this line. */
if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT)
buf = apr_psprintf(pool, "%ld\n", rev);
else
buf = apr_psprintf(pool, "%ld %s %s\n", rev, next_node_id, next_copy_id);
name = svn_fs_fs__path_current(fs, pool);
SVN_ERR(svn_io_write_unique(&tmp_name,
svn_dirent_dirname(name, pool),
buf, strlen(buf),
svn_io_file_del_none, pool));
return move_into_place(tmp_name, name, name, pool);
}
/* Open a new svn_fs_t handle to FS, set that handle's concept of "current
youngest revision" to NEW_REV, and call svn_fs_fs__verify_root() on
NEW_REV's revision root.
Intended to be called as the very last step in a commit before 'current'
is bumped. This implies that we are holding the write lock. */
static svn_error_t *
verify_as_revision_before_current_plus_plus(svn_fs_t *fs,
svn_revnum_t new_rev,
apr_pool_t *pool)
{
#ifdef SVN_DEBUG
fs_fs_data_t *ffd = fs->fsap_data;
svn_fs_t *ft; /* fs++ == ft */
svn_fs_root_t *root;
fs_fs_data_t *ft_ffd;
apr_hash_t *fs_config;
SVN_ERR_ASSERT(ffd->svn_fs_open_);
/* make sure FT does not simply return data cached by other instances
* but actually retrieves it from disk at least once.
*/
fs_config = apr_hash_make(pool);
svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_NS,
svn_uuid_generate(pool));
SVN_ERR(ffd->svn_fs_open_(&ft, fs->path,
fs_config,
pool));
ft_ffd = ft->fsap_data;
/* Don't let FT consult rep-cache.db, either. */
ft_ffd->rep_sharing_allowed = FALSE;
/* Time travel! */
ft_ffd->youngest_rev_cache = new_rev;
SVN_ERR(svn_fs_fs__revision_root(&root, ft, new_rev, pool));
SVN_ERR_ASSERT(root->is_txn_root == FALSE && root->rev == new_rev);
SVN_ERR_ASSERT(ft_ffd->youngest_rev_cache == new_rev);
SVN_ERR(svn_fs_fs__verify_root(root, pool));
#endif /* SVN_DEBUG */
return SVN_NO_ERROR;
}
/* Update the 'current' file to hold the correct next node and copy_ids
from transaction TXN_ID in filesystem FS. The current revision is
set to REV. Perform temporary allocations in POOL. */
static svn_error_t *
write_final_current(svn_fs_t *fs,
const char *txn_id,
svn_revnum_t rev,
const char *start_node_id,
const char *start_copy_id,
apr_pool_t *pool)
{
const char *txn_node_id, *txn_copy_id;
char new_node_id[MAX_KEY_SIZE + 2];
char new_copy_id[MAX_KEY_SIZE + 2];
fs_fs_data_t *ffd = fs->fsap_data;
if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT)
return write_current(fs, rev, NULL, NULL, pool);
/* To find the next available ids, we add the id that used to be in
the 'current' file, to the next ids from the transaction file. */
SVN_ERR(read_next_ids(&txn_node_id, &txn_copy_id, fs, txn_id, pool));
svn_fs_fs__add_keys(start_node_id, txn_node_id, new_node_id);
svn_fs_fs__add_keys(start_copy_id, txn_copy_id, new_copy_id);
return write_current(fs, rev, new_node_id, new_copy_id, pool);
}
/* Verify that the user registed with FS has all the locks necessary to
permit all the changes associate with TXN_NAME.
The FS write lock is assumed to be held by the caller. */
static svn_error_t *
verify_locks(svn_fs_t *fs,
const char *txn_name,
apr_pool_t *pool)
{
apr_pool_t *subpool = svn_pool_create(pool);
apr_hash_t *changes;
apr_hash_index_t *hi;
apr_array_header_t *changed_paths;
svn_stringbuf_t *last_recursed = NULL;
int i;
/* Fetch the changes for this transaction. */
SVN_ERR(svn_fs_fs__txn_changes_fetch(&changes, fs, txn_name, pool));
/* Make an array of the changed paths, and sort them depth-first-ily. */
changed_paths = apr_array_make(pool, apr_hash_count(changes) + 1,
sizeof(const char *));
for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
APR_ARRAY_PUSH(changed_paths, const char *) = svn__apr_hash_index_key(hi);
qsort(changed_paths->elts, changed_paths->nelts,
changed_paths->elt_size, svn_sort_compare_paths);
/* Now, traverse the array of changed paths, verify locks. Note
that if we need to do a recursive verification a path, we'll skip
over children of that path when we get to them. */
for (i = 0; i < changed_paths->nelts; i++)
{
const char *path;
svn_fs_path_change2_t *change;
svn_boolean_t recurse = TRUE;
svn_pool_clear(subpool);
path = APR_ARRAY_IDX(changed_paths, i, const char *);
/* If this path has already been verified as part of a recursive
check of one of its parents, no need to do it again. */
if (last_recursed
&& svn_dirent_is_child(last_recursed->data, path, subpool))
continue;
/* Fetch the change associated with our path. */
change = svn_hash_gets(changes, path);
/* What does it mean to succeed at lock verification for a given
path? For an existing file or directory getting modified
(text, props), it means we hold the lock on the file or
directory. For paths being added or removed, we need to hold
the locks for that path and any children of that path.
WHEW! We have no reliable way to determine the node kind
of deleted items, but fortunately we are going to do a
recursive check on deleted paths regardless of their kind. */
if (change->change_kind == svn_fs_path_change_modify)
recurse = FALSE;
SVN_ERR(svn_fs_fs__allow_locked_operation(path, fs, recurse, TRUE,
subpool));
/* If we just did a recursive check, remember the path we
checked (so children can be skipped). */
if (recurse)
{
if (! last_recursed)
last_recursed = svn_stringbuf_create(path, pool);
else
svn_stringbuf_set(last_recursed, path);
}
}
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
/* Baton used for commit_body below. */
struct commit_baton {
svn_revnum_t *new_rev_p;
svn_fs_t *fs;
svn_fs_txn_t *txn;
apr_array_header_t *reps_to_cache;
apr_hash_t *reps_hash;
apr_pool_t *reps_pool;
};
/* The work-horse for svn_fs_fs__commit, called with the FS write lock.
This implements the svn_fs_fs__with_write_lock() 'body' callback
type. BATON is a 'struct commit_baton *'. */
static svn_error_t *
commit_body(void *baton, apr_pool_t *pool)
{
struct commit_baton *cb = baton;
fs_fs_data_t *ffd = cb->fs->fsap_data;
const char *old_rev_filename, *rev_filename, *proto_filename;
const char *revprop_filename, *final_revprop;
const svn_fs_id_t *root_id, *new_root_id;
const char *start_node_id = NULL, *start_copy_id = NULL;
svn_revnum_t old_rev, new_rev;
apr_file_t *proto_file;
void *proto_file_lockcookie;
apr_off_t initial_offset, changed_path_offset;
char *buf;
apr_hash_t *txnprops;
apr_array_header_t *txnprop_list;
svn_prop_t prop;
svn_string_t date;
/* Get the current youngest revision. */
SVN_ERR(svn_fs_fs__youngest_rev(&old_rev, cb->fs, pool));
/* Check to make sure this transaction is based off the most recent
revision. */
if (cb->txn->base_rev != old_rev)
return svn_error_create(SVN_ERR_FS_TXN_OUT_OF_DATE, NULL,
_("Transaction out of date"));
/* Locks may have been added (or stolen) between the calling of
previous svn_fs.h functions and svn_fs_commit_txn(), so we need
to re-examine every changed-path in the txn and re-verify all
discovered locks. */
SVN_ERR(verify_locks(cb->fs, cb->txn->id, pool));
/* Get the next node_id and copy_id to use. */
if (ffd->format < SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT)
SVN_ERR(get_next_revision_ids(&start_node_id, &start_copy_id, cb->fs,
pool));
/* We are going to be one better than this puny old revision. */
new_rev = old_rev + 1;
/* Get a write handle on the proto revision file. */
SVN_ERR(get_writable_proto_rev(&proto_file, &proto_file_lockcookie,
cb->fs, cb->txn->id, pool));
SVN_ERR(get_file_offset(&initial_offset, proto_file, pool));
/* Write out all the node-revisions and directory contents. */
root_id = svn_fs_fs__id_txn_create("0", "0", cb->txn->id, pool);
SVN_ERR(write_final_rev(&new_root_id, proto_file, new_rev, cb->fs, root_id,
start_node_id, start_copy_id, initial_offset,
cb->reps_to_cache, cb->reps_hash, cb->reps_pool,
TRUE, pool));
/* Write the changed-path information. */
SVN_ERR(write_final_changed_path_info(&changed_path_offset, proto_file,
cb->fs, cb->txn->id, pool));
/* Write the final line. */
buf = apr_psprintf(pool, "\n%" APR_OFF_T_FMT " %" APR_OFF_T_FMT "\n",
svn_fs_fs__id_offset(new_root_id),
changed_path_offset);
SVN_ERR(svn_io_file_write_full(proto_file, buf, strlen(buf), NULL,
pool));
SVN_ERR(svn_io_file_flush_to_disk(proto_file, pool));
SVN_ERR(svn_io_file_close(proto_file, pool));
/* We don't unlock the prototype revision file immediately to avoid a
race with another caller writing to the prototype revision file
before we commit it. */
/* Remove any temporary txn props representing 'flags'. */
SVN_ERR(svn_fs_fs__txn_proplist(&txnprops, cb->txn, pool));
txnprop_list = apr_array_make(pool, 3, sizeof(svn_prop_t));
prop.value = NULL;
if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD))
{
prop.name = SVN_FS__PROP_TXN_CHECK_OOD;
APR_ARRAY_PUSH(txnprop_list, svn_prop_t) = prop;
}
if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS))
{
prop.name = SVN_FS__PROP_TXN_CHECK_LOCKS;
APR_ARRAY_PUSH(txnprop_list, svn_prop_t) = prop;
}
if (! apr_is_empty_array(txnprop_list))
SVN_ERR(svn_fs_fs__change_txn_props(cb->txn, txnprop_list, pool));
/* Create the shard for the rev and revprop file, if we're sharding and
this is the first revision of a new shard. We don't care if this
fails because the shard already existed for some reason. */
if (ffd->max_files_per_dir && new_rev % ffd->max_files_per_dir == 0)
{
/* Create the revs shard. */
{
const char *new_dir = path_rev_shard(cb->fs, new_rev, pool);
svn_error_t *err = svn_io_dir_make(new_dir, APR_OS_DEFAULT, pool);
if (err && !APR_STATUS_IS_EEXIST(err->apr_err))
return svn_error_trace(err);
svn_error_clear(err);
SVN_ERR(svn_io_copy_perms(svn_dirent_join(cb->fs->path,
PATH_REVS_DIR,
pool),
new_dir, pool));
}
/* Create the revprops shard. */
SVN_ERR_ASSERT(! is_packed_revprop(cb->fs, new_rev));
{
const char *new_dir = path_revprops_shard(cb->fs, new_rev, pool);
svn_error_t *err = svn_io_dir_make(new_dir, APR_OS_DEFAULT, pool);
if (err && !APR_STATUS_IS_EEXIST(err->apr_err))
return svn_error_trace(err);
svn_error_clear(err);
SVN_ERR(svn_io_copy_perms(svn_dirent_join(cb->fs->path,
PATH_REVPROPS_DIR,
pool),
new_dir, pool));
}
}
/* Move the finished rev file into place. */
SVN_ERR(svn_fs_fs__path_rev_absolute(&old_rev_filename,
cb->fs, old_rev, pool));
rev_filename = path_rev(cb->fs, new_rev, pool);
proto_filename = path_txn_proto_rev(cb->fs, cb->txn->id, pool);
SVN_ERR(move_into_place(proto_filename, rev_filename, old_rev_filename,
pool));
/* Now that we've moved the prototype revision file out of the way,
we can unlock it (since further attempts to write to the file
will fail as it no longer exists). We must do this so that we can
remove the transaction directory later. */
SVN_ERR(unlock_proto_rev(cb->fs, cb->txn->id, proto_file_lockcookie, pool));
/* Update commit time to ensure that svn:date revprops remain ordered. */
date.data = svn_time_to_cstring(apr_time_now(), pool);
date.len = strlen(date.data);
SVN_ERR(svn_fs_fs__change_txn_prop(cb->txn, SVN_PROP_REVISION_DATE,
&date, pool));
/* Move the revprops file into place. */
SVN_ERR_ASSERT(! is_packed_revprop(cb->fs, new_rev));
revprop_filename = path_txn_props(cb->fs, cb->txn->id, pool);
final_revprop = path_revprops(cb->fs, new_rev, pool);
SVN_ERR(move_into_place(revprop_filename, final_revprop,
old_rev_filename, pool));
/* Update the 'current' file. */
SVN_ERR(verify_as_revision_before_current_plus_plus(cb->fs, new_rev, pool));
SVN_ERR(write_final_current(cb->fs, cb->txn->id, new_rev, start_node_id,
start_copy_id, pool));
/* At this point the new revision is committed and globally visible
so let the caller know it succeeded by giving it the new revision
number, which fulfills svn_fs_commit_txn() contract. Any errors
after this point do not change the fact that a new revision was
created. */
*cb->new_rev_p = new_rev;
ffd->youngest_rev_cache = new_rev;
/* Remove this transaction directory. */
SVN_ERR(svn_fs_fs__purge_txn(cb->fs, cb->txn->id, pool));
return SVN_NO_ERROR;
}
/* Add the representations in REPS_TO_CACHE (an array of representation_t *)
* to the rep-cache database of FS. */
static svn_error_t *
write_reps_to_cache(svn_fs_t *fs,
const apr_array_header_t *reps_to_cache,
apr_pool_t *scratch_pool)
{
int i;
for (i = 0; i < reps_to_cache->nelts; i++)
{
representation_t *rep = APR_ARRAY_IDX(reps_to_cache, i, representation_t *);
/* FALSE because we don't care if another parallel commit happened to
* collide with us. (Non-parallel collisions will not be detected.) */
SVN_ERR(svn_fs_fs__set_rep_reference(fs, rep, FALSE, scratch_pool));
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__commit(svn_revnum_t *new_rev_p,
svn_fs_t *fs,
svn_fs_txn_t *txn,
apr_pool_t *pool)
{
struct commit_baton cb;
fs_fs_data_t *ffd = fs->fsap_data;
cb.new_rev_p = new_rev_p;
cb.fs = fs;
cb.txn = txn;
if (ffd->rep_sharing_allowed)
{
cb.reps_to_cache = apr_array_make(pool, 5, sizeof(representation_t *));
cb.reps_hash = apr_hash_make(pool);
cb.reps_pool = pool;
}
else
{
cb.reps_to_cache = NULL;
cb.reps_hash = NULL;
cb.reps_pool = NULL;
}
SVN_ERR(svn_fs_fs__with_write_lock(fs, commit_body, &cb, pool));
/* At this point, *NEW_REV_P has been set, so errors below won't affect
the success of the commit. (See svn_fs_commit_txn().) */
if (ffd->rep_sharing_allowed)
{
SVN_ERR(svn_fs_fs__open_rep_cache(fs, pool));
/* Write new entries to the rep-sharing database.
*
* We use an sqlite transaction to speed things up;
* see <http://www.sqlite.org/faq.html#q19>.
*/
SVN_SQLITE__WITH_TXN(
write_reps_to_cache(fs, cb.reps_to_cache, pool),
ffd->rep_cache_db);
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__reserve_copy_id(const char **copy_id_p,
svn_fs_t *fs,
const char *txn_id,
apr_pool_t *pool)
{
const char *cur_node_id, *cur_copy_id;
char *copy_id;
apr_size_t len;
/* First read in the current next-ids file. */
SVN_ERR(read_next_ids(&cur_node_id, &cur_copy_id, fs, txn_id, pool));
copy_id = apr_pcalloc(pool, strlen(cur_copy_id) + 2);
len = strlen(cur_copy_id);
svn_fs_fs__next_key(cur_copy_id, &len, copy_id);
SVN_ERR(write_next_ids(fs, txn_id, cur_node_id, copy_id, pool));
*copy_id_p = apr_pstrcat(pool, "_", cur_copy_id, (char *)NULL);
return SVN_NO_ERROR;
}
/* Write out the zeroth revision for filesystem FS. */
static svn_error_t *
write_revision_zero(svn_fs_t *fs)
{
const char *path_revision_zero = path_rev(fs, 0, fs->pool);
apr_hash_t *proplist;
svn_string_t date;
/* Write out a rev file for revision 0. */
SVN_ERR(svn_io_file_create(path_revision_zero,
"PLAIN\nEND\nENDREP\n"
"id: 0.0.r0/17\n"
"type: dir\n"
"count: 0\n"
"text: 0 0 4 4 "
"2d2977d1c96f487abe4a1e202dd03b4e\n"
"cpath: /\n"
"\n\n17 107\n", fs->pool));
SVN_ERR(svn_io_set_file_read_only(path_revision_zero, FALSE, fs->pool));
/* Set a date on revision 0. */
date.data = svn_time_to_cstring(apr_time_now(), fs->pool);
date.len = strlen(date.data);
proplist = apr_hash_make(fs->pool);
svn_hash_sets(proplist, SVN_PROP_REVISION_DATE, &date);
return set_revision_proplist(fs, 0, proplist, fs->pool);
}
svn_error_t *
svn_fs_fs__create(svn_fs_t *fs,
const char *path,
apr_pool_t *pool)
{
int format = SVN_FS_FS__FORMAT_NUMBER;
fs_fs_data_t *ffd = fs->fsap_data;
fs->path = apr_pstrdup(pool, path);
/* See if compatibility with older versions was explicitly requested. */
if (fs->config)
{
if (svn_hash_gets(fs->config, SVN_FS_CONFIG_PRE_1_4_COMPATIBLE))
format = 1;
else if (svn_hash_gets(fs->config, SVN_FS_CONFIG_PRE_1_5_COMPATIBLE))
format = 2;
else if (svn_hash_gets(fs->config, SVN_FS_CONFIG_PRE_1_6_COMPATIBLE))
format = 3;
else if (svn_hash_gets(fs->config, SVN_FS_CONFIG_PRE_1_8_COMPATIBLE))
format = 4;
}
ffd->format = format;
/* Override the default linear layout if this is a new-enough format. */
if (format >= SVN_FS_FS__MIN_LAYOUT_FORMAT_OPTION_FORMAT)
ffd->max_files_per_dir = SVN_FS_FS_DEFAULT_MAX_FILES_PER_DIR;
/* Create the revision data directories. */
if (ffd->max_files_per_dir)
SVN_ERR(svn_io_make_dir_recursively(path_rev_shard(fs, 0, pool), pool));
else
SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(path, PATH_REVS_DIR,
pool),
pool));
/* Create the revprops directory. */
if (ffd->max_files_per_dir)
SVN_ERR(svn_io_make_dir_recursively(path_revprops_shard(fs, 0, pool),
pool));
else
SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(path,
PATH_REVPROPS_DIR,
pool),
pool));
/* Create the transaction directory. */
SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(path, PATH_TXNS_DIR,
pool),
pool));
/* Create the protorevs directory. */
if (format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(path, PATH_TXN_PROTOS_DIR,
pool),
pool));
/* Create the 'current' file. */
SVN_ERR(svn_io_file_create(svn_fs_fs__path_current(fs, pool),
(format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT
? "0\n" : "0 1 1\n"),
pool));
SVN_ERR(svn_io_file_create(path_lock(fs, pool), "", pool));
SVN_ERR(svn_fs_fs__set_uuid(fs, NULL, pool));
SVN_ERR(write_revision_zero(fs));
/* Create the fsfs.conf file if supported. Older server versions would
simply ignore the file but that might result in a different behavior
than with the later releases. Also, hotcopy would ignore, i.e. not
copy, a fsfs.conf with old formats. */
if (ffd->format >= SVN_FS_FS__MIN_CONFIG_FILE)
SVN_ERR(write_config(fs, pool));
SVN_ERR(read_config(ffd, fs->path, pool));
/* Create the min unpacked rev file. */
if (ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT)
SVN_ERR(svn_io_file_create(path_min_unpacked_rev(fs, pool), "0\n", pool));
/* Create the txn-current file if the repository supports
the transaction sequence file. */
if (format >= SVN_FS_FS__MIN_TXN_CURRENT_FORMAT)
{
SVN_ERR(svn_io_file_create(path_txn_current(fs, pool),
"0\n", pool));
SVN_ERR(svn_io_file_create(path_txn_current_lock(fs, pool),
"", pool));
}
/* This filesystem is ready. Stamp it with a format number. */
SVN_ERR(write_format(path_format(fs, pool),
ffd->format, ffd->max_files_per_dir, FALSE, pool));
ffd->youngest_rev_cache = 0;
return SVN_NO_ERROR;
}
/* Part of the recovery procedure. Return the largest revision *REV in
filesystem FS. Use POOL for temporary allocation. */
static svn_error_t *
recover_get_largest_revision(svn_fs_t *fs, svn_revnum_t *rev, apr_pool_t *pool)
{
/* Discovering the largest revision in the filesystem would be an
expensive operation if we did a readdir() or searched linearly,
so we'll do a form of binary search. left is a revision that we
know exists, right a revision that we know does not exist. */
apr_pool_t *iterpool;
svn_revnum_t left, right = 1;
iterpool = svn_pool_create(pool);
/* Keep doubling right, until we find a revision that doesn't exist. */
while (1)
{
svn_error_t *err;
apr_file_t *file;
err = open_pack_or_rev_file(&file, fs, right, iterpool);
svn_pool_clear(iterpool);
if (err && err->apr_err == SVN_ERR_FS_NO_SUCH_REVISION)
{
svn_error_clear(err);
break;
}
else
SVN_ERR(err);
right <<= 1;
}
left = right >> 1;
/* We know that left exists and right doesn't. Do a normal bsearch to find
the last revision. */
while (left + 1 < right)
{
svn_revnum_t probe = left + ((right - left) / 2);
svn_error_t *err;
apr_file_t *file;
err = open_pack_or_rev_file(&file, fs, probe, iterpool);
svn_pool_clear(iterpool);
if (err && err->apr_err == SVN_ERR_FS_NO_SUCH_REVISION)
{
svn_error_clear(err);
right = probe;
}
else
{
SVN_ERR(err);
left = probe;
}
}
svn_pool_destroy(iterpool);
/* left is now the largest revision that exists. */
*rev = left;
return SVN_NO_ERROR;
}
/* A baton for reading a fixed amount from an open file. For
recover_find_max_ids() below. */
struct recover_read_from_file_baton
{
apr_file_t *file;
apr_pool_t *pool;
apr_off_t remaining;
};
/* A stream read handler used by recover_find_max_ids() below.
Read and return at most BATON->REMAINING bytes from the stream,
returning nothing after that to indicate EOF. */
static svn_error_t *
read_handler_recover(void *baton, char *buffer, apr_size_t *len)
{
struct recover_read_from_file_baton *b = baton;
svn_filesize_t bytes_to_read = *len;
if (b->remaining == 0)
{
/* Return a successful read of zero bytes to signal EOF. */
*len = 0;
return SVN_NO_ERROR;
}
if (bytes_to_read > b->remaining)
bytes_to_read = b->remaining;
b->remaining -= bytes_to_read;
return svn_io_file_read_full2(b->file, buffer, (apr_size_t) bytes_to_read,
len, NULL, b->pool);
}
/* Part of the recovery procedure. Read the directory noderev at offset
OFFSET of file REV_FILE (the revision file of revision REV of
filesystem FS), and set MAX_NODE_ID and MAX_COPY_ID to be the node-id
and copy-id of that node, if greater than the current value stored
in either. Recurse into any child directories that were modified in
this revision.
MAX_NODE_ID and MAX_COPY_ID must be arrays of at least MAX_KEY_SIZE.
Perform temporary allocation in POOL. */
static svn_error_t *
recover_find_max_ids(svn_fs_t *fs, svn_revnum_t rev,
apr_file_t *rev_file, apr_off_t offset,
char *max_node_id, char *max_copy_id,
apr_pool_t *pool)
{
apr_hash_t *headers;
char *value;
representation_t *data_rep;
struct rep_args *ra;
struct recover_read_from_file_baton baton;
svn_stream_t *stream;
apr_hash_t *entries;
apr_hash_index_t *hi;
apr_pool_t *iterpool;
SVN_ERR(svn_io_file_seek(rev_file, APR_SET, &offset, pool));
SVN_ERR(read_header_block(&headers, svn_stream_from_aprfile2(rev_file, TRUE,
pool),
pool));
/* Check that this is a directory. It should be. */
value = svn_hash_gets(headers, HEADER_TYPE);
if (value == NULL || strcmp(value, KIND_DIR) != 0)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Recovery encountered a non-directory node"));
/* Get the data location. No data location indicates an empty directory. */
value = svn_hash_gets(headers, HEADER_TEXT);
if (!value)
return SVN_NO_ERROR;
SVN_ERR(read_rep_offsets(&data_rep, value, NULL, FALSE, pool));
/* If the directory's data representation wasn't changed in this revision,
we've already scanned the directory's contents for noderevs, so we don't
need to again. This will occur if a property is changed on a directory
without changing the directory's contents. */
if (data_rep->revision != rev)
return SVN_NO_ERROR;
/* We could use get_dir_contents(), but this is much cheaper. It does
rely on directory entries being stored as PLAIN reps, though. */
offset = data_rep->offset;
SVN_ERR(svn_io_file_seek(rev_file, APR_SET, &offset, pool));
SVN_ERR(read_rep_line(&ra, rev_file, pool));
if (ra->is_delta)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Recovery encountered a deltified directory "
"representation"));
/* Now create a stream that's allowed to read only as much data as is
stored in the representation. */
baton.file = rev_file;
baton.pool = pool;
- baton.remaining = data_rep->expanded_size;
+ baton.remaining = data_rep->expanded_size
+ ? data_rep->expanded_size
+ : data_rep->size;
stream = svn_stream_create(&baton, pool);
svn_stream_set_read(stream, read_handler_recover);
/* Now read the entries from that stream. */
entries = apr_hash_make(pool);
SVN_ERR(svn_hash_read2(entries, stream, SVN_HASH_TERMINATOR, pool));
SVN_ERR(svn_stream_close(stream));
/* Now check each of the entries in our directory to find new node and
copy ids, and recurse into new subdirectories. */
iterpool = svn_pool_create(pool);
for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
{
char *str_val;
char *str;
svn_node_kind_t kind;
svn_fs_id_t *id;
const char *node_id, *copy_id;
apr_off_t child_dir_offset;
const svn_string_t *path = svn__apr_hash_index_val(hi);
svn_pool_clear(iterpool);
str_val = apr_pstrdup(iterpool, path->data);
str = svn_cstring_tokenize(" ", &str_val);
if (str == NULL)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Directory entry corrupt"));
if (strcmp(str, KIND_FILE) == 0)
kind = svn_node_file;
else if (strcmp(str, KIND_DIR) == 0)
kind = svn_node_dir;
else
{
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Directory entry corrupt"));
}
str = svn_cstring_tokenize(" ", &str_val);
if (str == NULL)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Directory entry corrupt"));
id = svn_fs_fs__id_parse(str, strlen(str), iterpool);
if (svn_fs_fs__id_rev(id) != rev)
{
/* If the node wasn't modified in this revision, we've already
checked the node and copy id. */
continue;
}
node_id = svn_fs_fs__id_node_id(id);
copy_id = svn_fs_fs__id_copy_id(id);
if (svn_fs_fs__key_compare(node_id, max_node_id) > 0)
{
SVN_ERR_ASSERT(strlen(node_id) < MAX_KEY_SIZE);
apr_cpystrn(max_node_id, node_id, MAX_KEY_SIZE);
}
if (svn_fs_fs__key_compare(copy_id, max_copy_id) > 0)
{
SVN_ERR_ASSERT(strlen(copy_id) < MAX_KEY_SIZE);
apr_cpystrn(max_copy_id, copy_id, MAX_KEY_SIZE);
}
if (kind == svn_node_file)
continue;
child_dir_offset = svn_fs_fs__id_offset(id);
SVN_ERR(recover_find_max_ids(fs, rev, rev_file, child_dir_offset,
max_node_id, max_copy_id, iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Return TRUE, if for REVISION in FS, we can find the revprop pack file.
* Use POOL for temporary allocations.
* Set *MISSING, if the reason is a missing manifest or pack file.
*/
static svn_boolean_t
packed_revprop_available(svn_boolean_t *missing,
svn_fs_t *fs,
svn_revnum_t revision,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
svn_stringbuf_t *content = NULL;
/* try to read the manifest file */
const char *folder = path_revprops_pack_shard(fs, revision, pool);
const char *manifest_path = svn_dirent_join(folder, PATH_MANIFEST, pool);
svn_error_t *err = try_stringbuf_from_file(&content,
missing,
manifest_path,
FALSE,
pool);
/* if the manifest cannot be read, consider the pack files inaccessible
* even if the file itself exists. */
if (err)
{
svn_error_clear(err);
return FALSE;
}
if (*missing)
return FALSE;
/* parse manifest content until we find the entry for REVISION.
* Revision 0 is never packed. */
revision = revision < ffd->max_files_per_dir
? revision - 1
: revision % ffd->max_files_per_dir;
while (content->data)
{
char *next = strchr(content->data, '\n');
if (next)
{
*next = 0;
++next;
}
if (revision-- == 0)
{
/* the respective pack file must exist (and be a file) */
svn_node_kind_t kind;
err = svn_io_check_path(svn_dirent_join(folder, content->data,
pool),
&kind, pool);
if (err)
{
svn_error_clear(err);
return FALSE;
}
*missing = kind == svn_node_none;
return kind == svn_node_file;
}
content->data = next;
}
return FALSE;
}
/* Baton used for recover_body below. */
struct recover_baton {
svn_fs_t *fs;
svn_cancel_func_t cancel_func;
void *cancel_baton;
};
/* The work-horse for svn_fs_fs__recover, called with the FS
write lock. This implements the svn_fs_fs__with_write_lock()
'body' callback type. BATON is a 'struct recover_baton *'. */
static svn_error_t *
recover_body(void *baton, apr_pool_t *pool)
{
struct recover_baton *b = baton;
svn_fs_t *fs = b->fs;
fs_fs_data_t *ffd = fs->fsap_data;
svn_revnum_t max_rev;
char next_node_id_buf[MAX_KEY_SIZE], next_copy_id_buf[MAX_KEY_SIZE];
char *next_node_id = NULL, *next_copy_id = NULL;
svn_revnum_t youngest_rev;
svn_node_kind_t youngest_revprops_kind;
/* Lose potentially corrupted data in temp files */
SVN_ERR(cleanup_revprop_namespace(fs));
/* We need to know the largest revision in the filesystem. */
SVN_ERR(recover_get_largest_revision(fs, &max_rev, pool));
/* Get the expected youngest revision */
SVN_ERR(get_youngest(&youngest_rev, fs->path, pool));
/* Policy note:
Since the revprops file is written after the revs file, the true
maximum available revision is the youngest one for which both are
present. That's probably the same as the max_rev we just found,
but if it's not, we could, in theory, repeatedly decrement
max_rev until we find a revision that has both a revs and
revprops file, then write db/current with that.
But we choose not to. If a repository is so corrupt that it's
missing at least one revprops file, we shouldn't assume that the
youngest revision for which both the revs and revprops files are
present is healthy. In other words, we're willing to recover
from a missing or out-of-date db/current file, because db/current
is truly redundant -- it's basically a cache so we don't have to
find max_rev each time, albeit a cache with unusual semantics,
since it also officially defines when a revision goes live. But
if we're missing more than the cache, it's time to back out and
let the admin reconstruct things by hand: correctness at that
point may depend on external things like checking a commit email
list, looking in particular working copies, etc.
This policy matches well with a typical naive backup scenario.
Say you're rsyncing your FSFS repository nightly to the same
location. Once revs and revprops are written, you've got the
maximum rev; if the backup should bomb before db/current is
written, then db/current could stay arbitrarily out-of-date, but
we can still recover. It's a small window, but we might as well
do what we can. */
/* Even if db/current were missing, it would be created with 0 by
get_youngest(), so this conditional remains valid. */
if (youngest_rev > max_rev)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Expected current rev to be <= %ld "
"but found %ld"), max_rev, youngest_rev);
/* We only need to search for maximum IDs for old FS formats which
se global ID counters. */
if (ffd->format < SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT)
{
/* Next we need to find the maximum node id and copy id in use across the
filesystem. Unfortunately, the only way we can get this information
is to scan all the noderevs of all the revisions and keep track as
we go along. */
svn_revnum_t rev;
apr_pool_t *iterpool = svn_pool_create(pool);
char max_node_id[MAX_KEY_SIZE] = "0", max_copy_id[MAX_KEY_SIZE] = "0";
apr_size_t len;
for (rev = 0; rev <= max_rev; rev++)
{
apr_file_t *rev_file;
apr_off_t root_offset;
svn_pool_clear(iterpool);
if (b->cancel_func)
SVN_ERR(b->cancel_func(b->cancel_baton));
SVN_ERR(open_pack_or_rev_file(&rev_file, fs, rev, iterpool));
SVN_ERR(get_root_changes_offset(&root_offset, NULL, rev_file, fs, rev,
iterpool));
SVN_ERR(recover_find_max_ids(fs, rev, rev_file, root_offset,
max_node_id, max_copy_id, iterpool));
SVN_ERR(svn_io_file_close(rev_file, iterpool));
}
svn_pool_destroy(iterpool);
/* Now that we finally have the maximum revision, node-id and copy-id, we
can bump the two ids to get the next of each. */
len = strlen(max_node_id);
svn_fs_fs__next_key(max_node_id, &len, next_node_id_buf);
next_node_id = next_node_id_buf;
len = strlen(max_copy_id);
svn_fs_fs__next_key(max_copy_id, &len, next_copy_id_buf);
next_copy_id = next_copy_id_buf;
}
/* Before setting current, verify that there is a revprops file
for the youngest revision. (Issue #2992) */
SVN_ERR(svn_io_check_path(path_revprops(fs, max_rev, pool),
&youngest_revprops_kind, pool));
if (youngest_revprops_kind == svn_node_none)
{
svn_boolean_t missing = TRUE;
if (!packed_revprop_available(&missing, fs, max_rev, pool))
{
if (missing)
{
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Revision %ld has a revs file but no "
"revprops file"),
max_rev);
}
else
{
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Revision %ld has a revs file but the "
"revprops file is inaccessible"),
max_rev);
}
}
}
else if (youngest_revprops_kind != svn_node_file)
{
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Revision %ld has a non-file where its "
"revprops file should be"),
max_rev);
}
/* Prune younger-than-(newfound-youngest) revisions from the rep
cache if sharing is enabled taking care not to create the cache
if it does not exist. */
if (ffd->rep_sharing_allowed)
{
svn_boolean_t rep_cache_exists;
SVN_ERR(svn_fs_fs__exists_rep_cache(&rep_cache_exists, fs, pool));
if (rep_cache_exists)
SVN_ERR(svn_fs_fs__del_rep_reference(fs, max_rev, pool));
}
/* Now store the discovered youngest revision, and the next IDs if
relevant, in a new 'current' file. */
return write_current(fs, max_rev, next_node_id, next_copy_id, pool);
}
/* This implements the fs_library_vtable_t.recover() API. */
svn_error_t *
svn_fs_fs__recover(svn_fs_t *fs,
svn_cancel_func_t cancel_func, void *cancel_baton,
apr_pool_t *pool)
{
struct recover_baton b;
/* We have no way to take out an exclusive lock in FSFS, so we're
restricted as to the types of recovery we can do. Luckily,
we just want to recreate the 'current' file, and we can do that just
by blocking other writers. */
b.fs = fs;
b.cancel_func = cancel_func;
b.cancel_baton = cancel_baton;
return svn_fs_fs__with_write_lock(fs, recover_body, &b, pool);
}
svn_error_t *
svn_fs_fs__set_uuid(svn_fs_t *fs,
const char *uuid,
apr_pool_t *pool)
{
char *my_uuid;
apr_size_t my_uuid_len;
const char *tmp_path;
const char *uuid_path = path_uuid(fs, pool);
if (! uuid)
uuid = svn_uuid_generate(pool);
/* Make sure we have a copy in FS->POOL, and append a newline. */
my_uuid = apr_pstrcat(fs->pool, uuid, "\n", (char *)NULL);
my_uuid_len = strlen(my_uuid);
SVN_ERR(svn_io_write_unique(&tmp_path,
svn_dirent_dirname(uuid_path, pool),
my_uuid, my_uuid_len,
svn_io_file_del_none, pool));
/* We use the permissions of the 'current' file, because the 'uuid'
file does not exist during repository creation. */
SVN_ERR(move_into_place(tmp_path, uuid_path,
svn_fs_fs__path_current(fs, pool), pool));
/* Remove the newline we added, and stash the UUID. */
my_uuid[my_uuid_len - 1] = '\0';
fs->uuid = my_uuid;
return SVN_NO_ERROR;
}
/** Node origin lazy cache. */
/* If directory PATH does not exist, create it and give it the same
permissions as FS_path.*/
svn_error_t *
svn_fs_fs__ensure_dir_exists(const char *path,
const char *fs_path,
apr_pool_t *pool)
{
svn_error_t *err = svn_io_dir_make(path, APR_OS_DEFAULT, pool);
if (err && APR_STATUS_IS_EEXIST(err->apr_err))
{
svn_error_clear(err);
return SVN_NO_ERROR;
}
SVN_ERR(err);
/* We successfully created a new directory. Dup the permissions
from FS->path. */
return svn_io_copy_perms(fs_path, path, pool);
}
/* Set *NODE_ORIGINS to a hash mapping 'const char *' node IDs to
'svn_string_t *' node revision IDs. Use POOL for allocations. */
static svn_error_t *
get_node_origins_from_file(svn_fs_t *fs,
apr_hash_t **node_origins,
const char *node_origins_file,
apr_pool_t *pool)
{
apr_file_t *fd;
svn_error_t *err;
svn_stream_t *stream;
*node_origins = NULL;
err = svn_io_file_open(&fd, node_origins_file,
APR_READ, APR_OS_DEFAULT, pool);
if (err && APR_STATUS_IS_ENOENT(err->apr_err))
{
svn_error_clear(err);
return SVN_NO_ERROR;
}
SVN_ERR(err);
stream = svn_stream_from_aprfile2(fd, FALSE, pool);
*node_origins = apr_hash_make(pool);
SVN_ERR(svn_hash_read2(*node_origins, stream, SVN_HASH_TERMINATOR, pool));
return svn_stream_close(stream);
}
svn_error_t *
svn_fs_fs__get_node_origin(const svn_fs_id_t **origin_id,
svn_fs_t *fs,
const char *node_id,
apr_pool_t *pool)
{
apr_hash_t *node_origins;
*origin_id = NULL;
SVN_ERR(get_node_origins_from_file(fs, &node_origins,
path_node_origin(fs, node_id, pool),
pool));
if (node_origins)
{
svn_string_t *origin_id_str =
svn_hash_gets(node_origins, node_id);
if (origin_id_str)
*origin_id = svn_fs_fs__id_parse(origin_id_str->data,
origin_id_str->len, pool);
}
return SVN_NO_ERROR;
}
/* Helper for svn_fs_fs__set_node_origin. Takes a NODE_ID/NODE_REV_ID
pair and adds it to the NODE_ORIGINS_PATH file. */
static svn_error_t *
set_node_origins_for_file(svn_fs_t *fs,
const char *node_origins_path,
const char *node_id,
svn_string_t *node_rev_id,
apr_pool_t *pool)
{
const char *path_tmp;
svn_stream_t *stream;
apr_hash_t *origins_hash;
svn_string_t *old_node_rev_id;
SVN_ERR(svn_fs_fs__ensure_dir_exists(svn_dirent_join(fs->path,
PATH_NODE_ORIGINS_DIR,
pool),
fs->path, pool));
/* Read the previously existing origins (if any), and merge our
update with it. */
SVN_ERR(get_node_origins_from_file(fs, &origins_hash,
node_origins_path, pool));
if (! origins_hash)
origins_hash = apr_hash_make(pool);
old_node_rev_id = svn_hash_gets(origins_hash, node_id);
if (old_node_rev_id && !svn_string_compare(node_rev_id, old_node_rev_id))
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Node origin for '%s' exists with a different "
"value (%s) than what we were about to store "
"(%s)"),
node_id, old_node_rev_id->data, node_rev_id->data);
svn_hash_sets(origins_hash, node_id, node_rev_id);
/* Sure, there's a race condition here. Two processes could be
trying to add different cache elements to the same file at the
same time, and the entries added by the first one to write will
be lost. But this is just a cache of reconstructible data, so
we'll accept this problem in return for not having to deal with
locking overhead. */
/* Create a temporary file, write out our hash, and close the file. */
SVN_ERR(svn_stream_open_unique(&stream, &path_tmp,
svn_dirent_dirname(node_origins_path, pool),
svn_io_file_del_none, pool, pool));
SVN_ERR(svn_hash_write2(origins_hash, stream, SVN_HASH_TERMINATOR, pool));
SVN_ERR(svn_stream_close(stream));
/* Rename the temp file as the real destination */
return svn_io_file_rename(path_tmp, node_origins_path, pool);
}
svn_error_t *
svn_fs_fs__set_node_origin(svn_fs_t *fs,
const char *node_id,
const svn_fs_id_t *node_rev_id,
apr_pool_t *pool)
{
svn_error_t *err;
const char *filename = path_node_origin(fs, node_id, pool);
err = set_node_origins_for_file(fs, filename,
node_id,
svn_fs_fs__id_unparse(node_rev_id, pool),
pool);
if (err && APR_STATUS_IS_EACCES(err->apr_err))
{
/* It's just a cache; stop trying if I can't write. */
svn_error_clear(err);
err = NULL;
}
return svn_error_trace(err);
}
svn_error_t *
svn_fs_fs__list_transactions(apr_array_header_t **names_p,
svn_fs_t *fs,
apr_pool_t *pool)
{
const char *txn_dir;
apr_hash_t *dirents;
apr_hash_index_t *hi;
apr_array_header_t *names;
apr_size_t ext_len = strlen(PATH_EXT_TXN);
names = apr_array_make(pool, 1, sizeof(const char *));
/* Get the transactions directory. */
txn_dir = svn_dirent_join(fs->path, PATH_TXNS_DIR, pool);
/* Now find a listing of this directory. */
SVN_ERR(svn_io_get_dirents3(&dirents, txn_dir, TRUE, pool, pool));
/* Loop through all the entries and return anything that ends with '.txn'. */
for (hi = apr_hash_first(pool, dirents); hi; hi = apr_hash_next(hi))
{
const char *name = svn__apr_hash_index_key(hi);
apr_ssize_t klen = svn__apr_hash_index_klen(hi);
const char *id;
/* The name must end with ".txn" to be considered a transaction. */
if ((apr_size_t) klen <= ext_len
|| (strcmp(name + klen - ext_len, PATH_EXT_TXN)) != 0)
continue;
/* Truncate the ".txn" extension and store the ID. */
id = apr_pstrndup(pool, name, strlen(name) - ext_len);
APR_ARRAY_PUSH(names, const char *) = id;
}
*names_p = names;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__open_txn(svn_fs_txn_t **txn_p,
svn_fs_t *fs,
const char *name,
apr_pool_t *pool)
{
svn_fs_txn_t *txn;
svn_node_kind_t kind;
transaction_t *local_txn;
/* First check to see if the directory exists. */
SVN_ERR(svn_io_check_path(path_txn_dir(fs, name, pool), &kind, pool));
/* Did we find it? */
if (kind != svn_node_dir)
return svn_error_createf(SVN_ERR_FS_NO_SUCH_TRANSACTION, NULL,
_("No such transaction '%s'"),
name);
txn = apr_pcalloc(pool, sizeof(*txn));
/* Read in the root node of this transaction. */
txn->id = apr_pstrdup(pool, name);
txn->fs = fs;
SVN_ERR(svn_fs_fs__get_txn(&local_txn, fs, name, pool));
txn->base_rev = svn_fs_fs__id_rev(local_txn->base_id);
txn->vtable = &txn_vtable;
*txn_p = txn;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__txn_proplist(apr_hash_t **table_p,
svn_fs_txn_t *txn,
apr_pool_t *pool)
{
apr_hash_t *proplist = apr_hash_make(pool);
SVN_ERR(get_txn_proplist(proplist, txn->fs, txn->id, pool));
*table_p = proplist;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__delete_node_revision(svn_fs_t *fs,
const svn_fs_id_t *id,
apr_pool_t *pool)
{
node_revision_t *noderev;
SVN_ERR(svn_fs_fs__get_node_revision(&noderev, fs, id, pool));
/* Delete any mutable property representation. */
if (noderev->prop_rep && noderev->prop_rep->txn_id)
SVN_ERR(svn_io_remove_file2(path_txn_node_props(fs, id, pool), FALSE,
pool));
/* Delete any mutable data representation. */
if (noderev->data_rep && noderev->data_rep->txn_id
&& noderev->kind == svn_node_dir)
{
fs_fs_data_t *ffd = fs->fsap_data;
SVN_ERR(svn_io_remove_file2(path_txn_node_children(fs, id, pool), FALSE,
pool));
/* remove the corresponding entry from the cache, if such exists */
if (ffd->txn_dir_cache)
{
const char *key = svn_fs_fs__id_unparse(id, pool)->data;
SVN_ERR(svn_cache__set(ffd->txn_dir_cache, key, NULL, pool));
}
}
return svn_io_remove_file2(path_txn_node_rev(fs, id, pool), FALSE, pool);
}
/*** Revisions ***/
svn_error_t *
svn_fs_fs__revision_prop(svn_string_t **value_p,
svn_fs_t *fs,
svn_revnum_t rev,
const char *propname,
apr_pool_t *pool)
{
apr_hash_t *table;
SVN_ERR(svn_fs__check_fs(fs, TRUE));
SVN_ERR(svn_fs_fs__revision_proplist(&table, fs, rev, pool));
*value_p = svn_hash_gets(table, propname);
return SVN_NO_ERROR;
}
/* Baton used for change_rev_prop_body below. */
struct change_rev_prop_baton {
svn_fs_t *fs;
svn_revnum_t rev;
const char *name;
const svn_string_t *const *old_value_p;
const svn_string_t *value;
};
/* The work-horse for svn_fs_fs__change_rev_prop, called with the FS
write lock. This implements the svn_fs_fs__with_write_lock()
'body' callback type. BATON is a 'struct change_rev_prop_baton *'. */
static svn_error_t *
change_rev_prop_body(void *baton, apr_pool_t *pool)
{
struct change_rev_prop_baton *cb = baton;
apr_hash_t *table;
SVN_ERR(svn_fs_fs__revision_proplist(&table, cb->fs, cb->rev, pool));
if (cb->old_value_p)
{
const svn_string_t *wanted_value = *cb->old_value_p;
const svn_string_t *present_value = svn_hash_gets(table, cb->name);
if ((!wanted_value != !present_value)
|| (wanted_value && present_value
&& !svn_string_compare(wanted_value, present_value)))
{
/* What we expected isn't what we found. */
return svn_error_createf(SVN_ERR_FS_PROP_BASEVALUE_MISMATCH, NULL,
_("revprop '%s' has unexpected value in "
"filesystem"),
cb->name);
}
/* Fall through. */
}
svn_hash_sets(table, cb->name, cb->value);
return set_revision_proplist(cb->fs, cb->rev, table, pool);
}
svn_error_t *
svn_fs_fs__change_rev_prop(svn_fs_t *fs,
svn_revnum_t rev,
const char *name,
const svn_string_t *const *old_value_p,
const svn_string_t *value,
apr_pool_t *pool)
{
struct change_rev_prop_baton cb;
SVN_ERR(svn_fs__check_fs(fs, TRUE));
cb.fs = fs;
cb.rev = rev;
cb.name = name;
cb.old_value_p = old_value_p;
cb.value = value;
return svn_fs_fs__with_write_lock(fs, change_rev_prop_body, &cb, pool);
}
/*** Transactions ***/
svn_error_t *
svn_fs_fs__get_txn_ids(const svn_fs_id_t **root_id_p,
const svn_fs_id_t **base_root_id_p,
svn_fs_t *fs,
const char *txn_name,
apr_pool_t *pool)
{
transaction_t *txn;
SVN_ERR(svn_fs_fs__get_txn(&txn, fs, txn_name, pool));
*root_id_p = txn->root_id;
*base_root_id_p = txn->base_id;
return SVN_NO_ERROR;
}
/* Generic transaction operations. */
svn_error_t *
svn_fs_fs__txn_prop(svn_string_t **value_p,
svn_fs_txn_t *txn,
const char *propname,
apr_pool_t *pool)
{
apr_hash_t *table;
svn_fs_t *fs = txn->fs;
SVN_ERR(svn_fs__check_fs(fs, TRUE));
SVN_ERR(svn_fs_fs__txn_proplist(&table, txn, pool));
*value_p = svn_hash_gets(table, propname);
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__begin_txn(svn_fs_txn_t **txn_p,
svn_fs_t *fs,
svn_revnum_t rev,
apr_uint32_t flags,
apr_pool_t *pool)
{
svn_string_t date;
svn_prop_t prop;
apr_array_header_t *props = apr_array_make(pool, 3, sizeof(svn_prop_t));
SVN_ERR(svn_fs__check_fs(fs, TRUE));
SVN_ERR(svn_fs_fs__create_txn(txn_p, fs, rev, pool));
/* Put a datestamp on the newly created txn, so we always know
exactly how old it is. (This will help sysadmins identify
long-abandoned txns that may need to be manually removed.) When
a txn is promoted to a revision, this property will be
automatically overwritten with a revision datestamp. */
date.data = svn_time_to_cstring(apr_time_now(), pool);
date.len = strlen(date.data);
prop.name = SVN_PROP_REVISION_DATE;
prop.value = &date;
APR_ARRAY_PUSH(props, svn_prop_t) = prop;
/* Set temporary txn props that represent the requested 'flags'
behaviors. */
if (flags & SVN_FS_TXN_CHECK_OOD)
{
prop.name = SVN_FS__PROP_TXN_CHECK_OOD;
prop.value = svn_string_create("true", pool);
APR_ARRAY_PUSH(props, svn_prop_t) = prop;
}
if (flags & SVN_FS_TXN_CHECK_LOCKS)
{
prop.name = SVN_FS__PROP_TXN_CHECK_LOCKS;
prop.value = svn_string_create("true", pool);
APR_ARRAY_PUSH(props, svn_prop_t) = prop;
}
return svn_fs_fs__change_txn_props(*txn_p, props, pool);
}
/****** Packing FSFS shards *********/
/* Write a file FILENAME in directory FS_PATH, containing a single line
* with the number REVNUM in ASCII decimal. Move the file into place
* atomically, overwriting any existing file.
*
* Similar to write_current(). */
static svn_error_t *
write_revnum_file(const char *fs_path,
const char *filename,
svn_revnum_t revnum,
apr_pool_t *scratch_pool)
{
const char *final_path, *tmp_path;
svn_stream_t *tmp_stream;
final_path = svn_dirent_join(fs_path, filename, scratch_pool);
SVN_ERR(svn_stream_open_unique(&tmp_stream, &tmp_path, fs_path,
svn_io_file_del_none,
scratch_pool, scratch_pool));
SVN_ERR(svn_stream_printf(tmp_stream, scratch_pool, "%ld\n", revnum));
SVN_ERR(svn_stream_close(tmp_stream));
SVN_ERR(move_into_place(tmp_path, final_path, final_path, scratch_pool));
return SVN_NO_ERROR;
}
/* Pack the revision SHARD containing exactly MAX_FILES_PER_DIR revisions
* from SHARD_PATH into the PACK_FILE_DIR, using POOL for allocations.
* CANCEL_FUNC and CANCEL_BATON are what you think they are.
*
* If for some reason we detect a partial packing already performed, we
* remove the pack file and start again.
*/
static svn_error_t *
pack_rev_shard(const char *pack_file_dir,
const char *shard_path,
apr_int64_t shard,
int max_files_per_dir,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
const char *pack_file_path, *manifest_file_path;
svn_stream_t *pack_stream, *manifest_stream;
svn_revnum_t start_rev, end_rev, rev;
apr_off_t next_offset;
apr_pool_t *iterpool;
/* Some useful paths. */
pack_file_path = svn_dirent_join(pack_file_dir, PATH_PACKED, pool);
manifest_file_path = svn_dirent_join(pack_file_dir, PATH_MANIFEST, pool);
/* Remove any existing pack file for this shard, since it is incomplete. */
SVN_ERR(svn_io_remove_dir2(pack_file_dir, TRUE, cancel_func, cancel_baton,
pool));
/* Create the new directory and pack and manifest files. */
SVN_ERR(svn_io_dir_make(pack_file_dir, APR_OS_DEFAULT, pool));
SVN_ERR(svn_stream_open_writable(&pack_stream, pack_file_path, pool,
pool));
SVN_ERR(svn_stream_open_writable(&manifest_stream, manifest_file_path,
pool, pool));
start_rev = (svn_revnum_t) (shard * max_files_per_dir);
end_rev = (svn_revnum_t) ((shard + 1) * (max_files_per_dir) - 1);
next_offset = 0;
iterpool = svn_pool_create(pool);
/* Iterate over the revisions in this shard, squashing them together. */
for (rev = start_rev; rev <= end_rev; rev++)
{
svn_stream_t *rev_stream;
apr_finfo_t finfo;
const char *path;
svn_pool_clear(iterpool);
/* Get the size of the file. */
path = svn_dirent_join(shard_path, apr_psprintf(iterpool, "%ld", rev),
iterpool);
SVN_ERR(svn_io_stat(&finfo, path, APR_FINFO_SIZE, iterpool));
/* Update the manifest. */
SVN_ERR(svn_stream_printf(manifest_stream, iterpool, "%" APR_OFF_T_FMT
"\n", next_offset));
next_offset += finfo.size;
/* Copy all the bits from the rev file to the end of the pack file. */
SVN_ERR(svn_stream_open_readonly(&rev_stream, path, iterpool, iterpool));
SVN_ERR(svn_stream_copy3(rev_stream, svn_stream_disown(pack_stream,
iterpool),
cancel_func, cancel_baton, iterpool));
}
SVN_ERR(svn_stream_close(manifest_stream));
SVN_ERR(svn_stream_close(pack_stream));
SVN_ERR(svn_io_copy_perms(shard_path, pack_file_dir, iterpool));
SVN_ERR(svn_io_set_file_read_only(pack_file_path, FALSE, iterpool));
SVN_ERR(svn_io_set_file_read_only(manifest_file_path, FALSE, iterpool));
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Copy revprop files for revisions [START_REV, END_REV) from SHARD_PATH
* to the pack file at PACK_FILE_NAME in PACK_FILE_DIR.
*
* The file sizes have already been determined and written to SIZES.
* Please note that this function will be executed while the filesystem
* has been locked and that revprops files will therefore not be modified
* while the pack is in progress.
*
* COMPRESSION_LEVEL defines how well the resulting pack file shall be
* compressed or whether is shall be compressed at all. TOTAL_SIZE is
* a hint on which initial buffer size we should use to hold the pack file
* content.
*
* CANCEL_FUNC and CANCEL_BATON are used as usual. Temporary allocations
* are done in SCRATCH_POOL.
*/
static svn_error_t *
copy_revprops(const char *pack_file_dir,
const char *pack_filename,
const char *shard_path,
svn_revnum_t start_rev,
svn_revnum_t end_rev,
apr_array_header_t *sizes,
apr_size_t total_size,
int compression_level,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
svn_stream_t *pack_stream;
apr_file_t *pack_file;
svn_revnum_t rev;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
svn_stream_t *stream;
/* create empty data buffer and a write stream on top of it */
svn_stringbuf_t *uncompressed
= svn_stringbuf_create_ensure(total_size, scratch_pool);
svn_stringbuf_t *compressed
= svn_stringbuf_create_empty(scratch_pool);
pack_stream = svn_stream_from_stringbuf(uncompressed, scratch_pool);
/* write the pack file header */
SVN_ERR(serialize_revprops_header(pack_stream, start_rev, sizes, 0,
sizes->nelts, iterpool));
/* Some useful paths. */
SVN_ERR(svn_io_file_open(&pack_file, svn_dirent_join(pack_file_dir,
pack_filename,
scratch_pool),
APR_WRITE | APR_CREATE, APR_OS_DEFAULT,
scratch_pool));
/* Iterate over the revisions in this shard, squashing them together. */
for (rev = start_rev; rev <= end_rev; rev++)
{
const char *path;
svn_pool_clear(iterpool);
/* Construct the file name. */
path = svn_dirent_join(shard_path, apr_psprintf(iterpool, "%ld", rev),
iterpool);
/* Copy all the bits from the non-packed revprop file to the end of
* the pack file. */
SVN_ERR(svn_stream_open_readonly(&stream, path, iterpool, iterpool));
SVN_ERR(svn_stream_copy3(stream, pack_stream,
cancel_func, cancel_baton, iterpool));
}
/* flush stream buffers to content buffer */
SVN_ERR(svn_stream_close(pack_stream));
/* compress the content (or just store it for COMPRESSION_LEVEL 0) */
SVN_ERR(svn__compress(svn_stringbuf__morph_into_string(uncompressed),
compressed, compression_level));
/* write the pack file content to disk */
stream = svn_stream_from_aprfile2(pack_file, FALSE, scratch_pool);
SVN_ERR(svn_stream_write(stream, compressed->data, &compressed->len));
SVN_ERR(svn_stream_close(stream));
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* For the revprop SHARD at SHARD_PATH with exactly MAX_FILES_PER_DIR
* revprop files in it, create a packed shared at PACK_FILE_DIR.
*
* COMPRESSION_LEVEL defines how well the resulting pack file shall be
* compressed or whether is shall be compressed at all. Individual pack
* file containing more than one revision will be limited to a size of
* MAX_PACK_SIZE bytes before compression.
*
* CANCEL_FUNC and CANCEL_BATON are used in the usual way. Temporary
* allocations are done in SCRATCH_POOL.
*/
static svn_error_t *
pack_revprops_shard(const char *pack_file_dir,
const char *shard_path,
apr_int64_t shard,
int max_files_per_dir,
apr_off_t max_pack_size,
int compression_level,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
const char *manifest_file_path, *pack_filename = NULL;
svn_stream_t *manifest_stream;
svn_revnum_t start_rev, end_rev, rev;
apr_off_t total_size;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
apr_array_header_t *sizes;
/* Some useful paths. */
manifest_file_path = svn_dirent_join(pack_file_dir, PATH_MANIFEST,
scratch_pool);
/* Remove any existing pack file for this shard, since it is incomplete. */
SVN_ERR(svn_io_remove_dir2(pack_file_dir, TRUE, cancel_func, cancel_baton,
scratch_pool));
/* Create the new directory and manifest file stream. */
SVN_ERR(svn_io_dir_make(pack_file_dir, APR_OS_DEFAULT, scratch_pool));
SVN_ERR(svn_stream_open_writable(&manifest_stream, manifest_file_path,
scratch_pool, scratch_pool));
/* revisions to handle. Special case: revision 0 */
start_rev = (svn_revnum_t) (shard * max_files_per_dir);
end_rev = (svn_revnum_t) ((shard + 1) * (max_files_per_dir) - 1);
if (start_rev == 0)
++start_rev;
/* initialize the revprop size info */
sizes = apr_array_make(scratch_pool, max_files_per_dir, sizeof(apr_off_t));
total_size = 2 * SVN_INT64_BUFFER_SIZE;
/* Iterate over the revisions in this shard, determine their size and
* squashing them together into pack files. */
for (rev = start_rev; rev <= end_rev; rev++)
{
apr_finfo_t finfo;
const char *path;
svn_pool_clear(iterpool);
/* Get the size of the file. */
path = svn_dirent_join(shard_path, apr_psprintf(iterpool, "%ld", rev),
iterpool);
SVN_ERR(svn_io_stat(&finfo, path, APR_FINFO_SIZE, iterpool));
/* if we already have started a pack file and this revprop cannot be
* appended to it, write the previous pack file. */
if (sizes->nelts != 0 &&
total_size + SVN_INT64_BUFFER_SIZE + finfo.size > max_pack_size)
{
SVN_ERR(copy_revprops(pack_file_dir, pack_filename, shard_path,
start_rev, rev-1, sizes, (apr_size_t)total_size,
compression_level, cancel_func, cancel_baton,
iterpool));
/* next pack file starts empty again */
apr_array_clear(sizes);
total_size = 2 * SVN_INT64_BUFFER_SIZE;
start_rev = rev;
}
/* Update the manifest. Allocate a file name for the current pack
* file if it is a new one */
if (sizes->nelts == 0)
pack_filename = apr_psprintf(scratch_pool, "%ld.0", rev);
SVN_ERR(svn_stream_printf(manifest_stream, iterpool, "%s\n",
pack_filename));
/* add to list of files to put into the current pack file */
APR_ARRAY_PUSH(sizes, apr_off_t) = finfo.size;
total_size += SVN_INT64_BUFFER_SIZE + finfo.size;
}
/* write the last pack file */
if (sizes->nelts != 0)
SVN_ERR(copy_revprops(pack_file_dir, pack_filename, shard_path,
start_rev, rev-1, sizes, (apr_size_t)total_size,
compression_level, cancel_func, cancel_baton,
iterpool));
/* flush the manifest file and update permissions */
SVN_ERR(svn_stream_close(manifest_stream));
SVN_ERR(svn_io_copy_perms(shard_path, pack_file_dir, iterpool));
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Delete the non-packed revprop SHARD at SHARD_PATH with exactly
* MAX_FILES_PER_DIR revprop files in it. If this is shard 0, keep the
* revprop file for revision 0.
*
* CANCEL_FUNC and CANCEL_BATON are used in the usual way. Temporary
* allocations are done in SCRATCH_POOL.
*/
static svn_error_t *
delete_revprops_shard(const char *shard_path,
apr_int64_t shard,
int max_files_per_dir,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
if (shard == 0)
{
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
int i;
/* delete all files except the one for revision 0 */
for (i = 1; i < max_files_per_dir; ++i)
{
const char *path = svn_dirent_join(shard_path,
apr_psprintf(iterpool, "%d", i),
iterpool);
if (cancel_func)
SVN_ERR((*cancel_func)(cancel_baton));
SVN_ERR(svn_io_remove_file2(path, TRUE, iterpool));
svn_pool_clear(iterpool);
}
svn_pool_destroy(iterpool);
}
else
SVN_ERR(svn_io_remove_dir2(shard_path, TRUE,
cancel_func, cancel_baton, scratch_pool));
return SVN_NO_ERROR;
}
/* In the file system at FS_PATH, pack the SHARD in REVS_DIR and
* REVPROPS_DIR containing exactly MAX_FILES_PER_DIR revisions, using POOL
* for allocations. REVPROPS_DIR will be NULL if revprop packing is not
* supported. COMPRESSION_LEVEL and MAX_PACK_SIZE will be ignored in that
* case.
*
* CANCEL_FUNC and CANCEL_BATON are what you think they are; similarly
* NOTIFY_FUNC and NOTIFY_BATON.
*
* If for some reason we detect a partial packing already performed, we
* remove the pack file and start again.
*/
static svn_error_t *
pack_shard(const char *revs_dir,
const char *revsprops_dir,
const char *fs_path,
apr_int64_t shard,
int max_files_per_dir,
apr_off_t max_pack_size,
int compression_level,
svn_fs_pack_notify_t notify_func,
void *notify_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
const char *rev_shard_path, *rev_pack_file_dir;
const char *revprops_shard_path, *revprops_pack_file_dir;
/* Notify caller we're starting to pack this shard. */
if (notify_func)
SVN_ERR(notify_func(notify_baton, shard, svn_fs_pack_notify_start,
pool));
/* Some useful paths. */
rev_pack_file_dir = svn_dirent_join(revs_dir,
apr_psprintf(pool,
"%" APR_INT64_T_FMT PATH_EXT_PACKED_SHARD,
shard),
pool);
rev_shard_path = svn_dirent_join(revs_dir,
apr_psprintf(pool, "%" APR_INT64_T_FMT, shard),
pool);
/* pack the revision content */
SVN_ERR(pack_rev_shard(rev_pack_file_dir, rev_shard_path,
shard, max_files_per_dir,
cancel_func, cancel_baton, pool));
/* if enabled, pack the revprops in an equivalent way */
if (revsprops_dir)
{
revprops_pack_file_dir = svn_dirent_join(revsprops_dir,
apr_psprintf(pool,
"%" APR_INT64_T_FMT PATH_EXT_PACKED_SHARD,
shard),
pool);
revprops_shard_path = svn_dirent_join(revsprops_dir,
apr_psprintf(pool, "%" APR_INT64_T_FMT, shard),
pool);
SVN_ERR(pack_revprops_shard(revprops_pack_file_dir, revprops_shard_path,
shard, max_files_per_dir,
(int)(0.9 * max_pack_size),
compression_level,
cancel_func, cancel_baton, pool));
}
/* Update the min-unpacked-rev file to reflect our newly packed shard.
* (This doesn't update ffd->min_unpacked_rev. That will be updated by
* update_min_unpacked_rev() when necessary.) */
SVN_ERR(write_revnum_file(fs_path, PATH_MIN_UNPACKED_REV,
(svn_revnum_t)((shard + 1) * max_files_per_dir),
pool));
/* Finally, remove the existing shard directories. */
SVN_ERR(svn_io_remove_dir2(rev_shard_path, TRUE,
cancel_func, cancel_baton, pool));
if (revsprops_dir)
SVN_ERR(delete_revprops_shard(revprops_shard_path,
shard, max_files_per_dir,
cancel_func, cancel_baton, pool));
/* Notify caller we're starting to pack this shard. */
if (notify_func)
SVN_ERR(notify_func(notify_baton, shard, svn_fs_pack_notify_end,
pool));
return SVN_NO_ERROR;
}
struct pack_baton
{
svn_fs_t *fs;
svn_fs_pack_notify_t notify_func;
void *notify_baton;
svn_cancel_func_t cancel_func;
void *cancel_baton;
};
/* The work-horse for svn_fs_fs__pack, called with the FS write lock.
This implements the svn_fs_fs__with_write_lock() 'body' callback
type. BATON is a 'struct pack_baton *'.
WARNING: if you add a call to this function, please note:
The code currently assumes that any piece of code running with
the write-lock set can rely on the ffd->min_unpacked_rev and
ffd->min_unpacked_revprop caches to be up-to-date (and, by
extension, on not having to use a retry when calling
svn_fs_fs__path_rev_absolute() and friends). If you add a call
to this function, consider whether you have to call
update_min_unpacked_rev().
See this thread: http://thread.gmane.org/1291206765.3782.3309.camel@edith
*/
static svn_error_t *
pack_body(void *baton,
apr_pool_t *pool)
{
struct pack_baton *pb = baton;
fs_fs_data_t ffd = {0};
apr_int64_t completed_shards;
apr_int64_t i;
svn_revnum_t youngest;
apr_pool_t *iterpool;
const char *rev_data_path;
const char *revprops_data_path = NULL;
/* read repository settings */
SVN_ERR(read_format(&ffd.format, &ffd.max_files_per_dir,
path_format(pb->fs, pool), pool));
SVN_ERR(check_format(ffd.format));
SVN_ERR(read_config(&ffd, pb->fs->path, pool));
/* If the repository isn't a new enough format, we don't support packing.
Return a friendly error to that effect. */
if (ffd.format < SVN_FS_FS__MIN_PACKED_FORMAT)
return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("FSFS format (%d) too old to pack; please upgrade the filesystem."),
ffd.format);
/* If we aren't using sharding, we can't do any packing, so quit. */
if (!ffd.max_files_per_dir)
return SVN_NO_ERROR;
SVN_ERR(read_min_unpacked_rev(&ffd.min_unpacked_rev,
path_min_unpacked_rev(pb->fs, pool),
pool));
SVN_ERR(get_youngest(&youngest, pb->fs->path, pool));
completed_shards = (youngest + 1) / ffd.max_files_per_dir;
/* See if we've already completed all possible shards thus far. */
if (ffd.min_unpacked_rev == (completed_shards * ffd.max_files_per_dir))
return SVN_NO_ERROR;
rev_data_path = svn_dirent_join(pb->fs->path, PATH_REVS_DIR, pool);
if (ffd.format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT)
revprops_data_path = svn_dirent_join(pb->fs->path, PATH_REVPROPS_DIR,
pool);
iterpool = svn_pool_create(pool);
for (i = ffd.min_unpacked_rev / ffd.max_files_per_dir;
i < completed_shards;
i++)
{
svn_pool_clear(iterpool);
if (pb->cancel_func)
SVN_ERR(pb->cancel_func(pb->cancel_baton));
SVN_ERR(pack_shard(rev_data_path, revprops_data_path,
pb->fs->path, i, ffd.max_files_per_dir,
ffd.revprop_pack_size,
ffd.compress_packed_revprops
? SVN_DELTA_COMPRESSION_LEVEL_DEFAULT
: SVN_DELTA_COMPRESSION_LEVEL_NONE,
pb->notify_func, pb->notify_baton,
pb->cancel_func, pb->cancel_baton, iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__pack(svn_fs_t *fs,
svn_fs_pack_notify_t notify_func,
void *notify_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
struct pack_baton pb = { 0 };
pb.fs = fs;
pb.notify_func = notify_func;
pb.notify_baton = notify_baton;
pb.cancel_func = cancel_func;
pb.cancel_baton = cancel_baton;
return svn_fs_fs__with_write_lock(fs, pack_body, &pb, pool);
}
/** Verifying. **/
/* Baton type expected by verify_walker(). The purpose is to reuse open
* rev / pack file handles between calls. Its contents need to be cleaned
* periodically to limit resource usage.
*/
typedef struct verify_walker_baton_t
{
/* number of calls to verify_walker() since the last clean */
int iteration_count;
/* number of files opened since the last clean */
int file_count;
/* progress notification callback to invoke periodically (may be NULL) */
svn_fs_progress_notify_func_t notify_func;
/* baton to use with NOTIFY_FUNC */
void *notify_baton;
/* remember the last revision for which we called notify_func */
svn_revnum_t last_notified_revision;
/* current file handle (or NULL) */
apr_file_t *file_hint;
/* corresponding revision (or SVN_INVALID_REVNUM) */
svn_revnum_t rev_hint;
/* pool to use for the file handles etc. */
apr_pool_t *pool;
} verify_walker_baton_t;
/* Used by svn_fs_fs__verify().
Implements svn_fs_fs__walk_rep_reference().walker. */
static svn_error_t *
verify_walker(representation_t *rep,
void *baton,
svn_fs_t *fs,
apr_pool_t *scratch_pool)
{
struct rep_state *rs;
struct rep_args *rep_args;
if (baton)
{
verify_walker_baton_t *walker_baton = baton;
apr_file_t * previous_file;
/* notify and free resources periodically */
if ( walker_baton->iteration_count > 1000
|| walker_baton->file_count > 16)
{
if ( walker_baton->notify_func
&& rep->revision != walker_baton->last_notified_revision)
{
walker_baton->notify_func(rep->revision,
walker_baton->notify_baton,
scratch_pool);
walker_baton->last_notified_revision = rep->revision;
}
svn_pool_clear(walker_baton->pool);
walker_baton->iteration_count = 0;
walker_baton->file_count = 0;
walker_baton->file_hint = NULL;
walker_baton->rev_hint = SVN_INVALID_REVNUM;
}
/* access the repo data */
previous_file = walker_baton->file_hint;
SVN_ERR(create_rep_state(&rs, &rep_args, &walker_baton->file_hint,
&walker_baton->rev_hint, rep, fs,
walker_baton->pool));
/* update resource usage counters */
walker_baton->iteration_count++;
if (previous_file != walker_baton->file_hint)
walker_baton->file_count++;
}
else
{
/* ### Should this be using read_rep_line() directly? */
SVN_ERR(create_rep_state(&rs, &rep_args, NULL, NULL, rep, fs,
scratch_pool));
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__verify(svn_fs_t *fs,
svn_revnum_t start,
svn_revnum_t end,
svn_fs_progress_notify_func_t notify_func,
void *notify_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
svn_boolean_t exists;
svn_revnum_t youngest = ffd->youngest_rev_cache; /* cache is current */
if (ffd->format < SVN_FS_FS__MIN_REP_SHARING_FORMAT)
return SVN_NO_ERROR;
/* Input validation. */
if (! SVN_IS_VALID_REVNUM(start))
start = 0;
if (! SVN_IS_VALID_REVNUM(end))
end = youngest;
SVN_ERR(ensure_revision_exists(fs, start, pool));
SVN_ERR(ensure_revision_exists(fs, end, pool));
/* rep-cache verification. */
SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, fs, pool));
if (exists)
{
/* provide a baton to allow the reuse of open file handles between
iterations (saves 2/3 of OS level file operations). */
verify_walker_baton_t *baton = apr_pcalloc(pool, sizeof(*baton));
baton->rev_hint = SVN_INVALID_REVNUM;
baton->pool = svn_pool_create(pool);
baton->last_notified_revision = SVN_INVALID_REVNUM;
baton->notify_func = notify_func;
baton->notify_baton = notify_baton;
/* tell the user that we are now ready to do *something* */
if (notify_func)
notify_func(SVN_INVALID_REVNUM, notify_baton, baton->pool);
/* Do not attempt to walk the rep-cache database if its file does
not exist, since doing so would create it --- which may confuse
the administrator. Don't take any lock. */
SVN_ERR(svn_fs_fs__walk_rep_reference(fs, start, end,
verify_walker, baton,
cancel_func, cancel_baton,
pool));
/* walker resource cleanup */
svn_pool_destroy(baton->pool);
}
return SVN_NO_ERROR;
}
/** Hotcopy. **/
/* Like svn_io_dir_file_copy(), but doesn't copy files that exist at
* the destination and do not differ in terms of kind, size, and mtime. */
static svn_error_t *
hotcopy_io_dir_file_copy(const char *src_path,
const char *dst_path,
const char *file,
apr_pool_t *scratch_pool)
{
const svn_io_dirent2_t *src_dirent;
const svn_io_dirent2_t *dst_dirent;
const char *src_target;
const char *dst_target;
/* Does the destination already exist? If not, we must copy it. */
dst_target = svn_dirent_join(dst_path, file, scratch_pool);
SVN_ERR(svn_io_stat_dirent2(&dst_dirent, dst_target, FALSE, TRUE,
scratch_pool, scratch_pool));
if (dst_dirent->kind != svn_node_none)
{
/* If the destination's stat information indicates that the file
* is equal to the source, don't bother copying the file again. */
src_target = svn_dirent_join(src_path, file, scratch_pool);
SVN_ERR(svn_io_stat_dirent2(&src_dirent, src_target, FALSE, FALSE,
scratch_pool, scratch_pool));
if (src_dirent->kind == dst_dirent->kind &&
src_dirent->special == dst_dirent->special &&
src_dirent->filesize == dst_dirent->filesize &&
src_dirent->mtime <= dst_dirent->mtime)
return SVN_NO_ERROR;
}
return svn_error_trace(svn_io_dir_file_copy(src_path, dst_path, file,
scratch_pool));
}
/* Set *NAME_P to the UTF-8 representation of directory entry NAME.
* NAME is in the internal encoding used by APR; PARENT is in
* UTF-8 and in internal (not local) style.
*
* Use PARENT only for generating an error string if the conversion
* fails because NAME could not be represented in UTF-8. In that
* case, return a two-level error in which the outer error's message
* mentions PARENT, but the inner error's message does not mention
* NAME (except possibly in hex) since NAME may not be printable.
* Such a compound error at least allows the user to go looking in the
* right directory for the problem.
*
* If there is any other error, just return that error directly.
*
* If there is any error, the effect on *NAME_P is undefined.
*
* *NAME_P and NAME may refer to the same storage.
*/
static svn_error_t *
entry_name_to_utf8(const char **name_p,
const char *name,
const char *parent,
apr_pool_t *pool)
{
svn_error_t *err = svn_path_cstring_to_utf8(name_p, name, pool);
if (err && err->apr_err == APR_EINVAL)
{
return svn_error_createf(err->apr_err, err,
_("Error converting entry "
"in directory '%s' to UTF-8"),
svn_dirent_local_style(parent, pool));
}
return err;
}
/* Like svn_io_copy_dir_recursively() but doesn't copy regular files that
* exist in the destination and do not differ from the source in terms of
* kind, size, and mtime. */
static svn_error_t *
hotcopy_io_copy_dir_recursively(const char *src,
const char *dst_parent,
const char *dst_basename,
svn_boolean_t copy_perms,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
svn_node_kind_t kind;
apr_status_t status;
const char *dst_path;
apr_dir_t *this_dir;
apr_finfo_t this_entry;
apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME;
/* Make a subpool for recursion */
apr_pool_t *subpool = svn_pool_create(pool);
/* The 'dst_path' is simply dst_parent/dst_basename */
dst_path = svn_dirent_join(dst_parent, dst_basename, pool);
/* Sanity checks: SRC and DST_PARENT are directories, and
DST_BASENAME doesn't already exist in DST_PARENT. */
SVN_ERR(svn_io_check_path(src, &kind, subpool));
if (kind != svn_node_dir)
return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
_("Source '%s' is not a directory"),
svn_dirent_local_style(src, pool));
SVN_ERR(svn_io_check_path(dst_parent, &kind, subpool));
if (kind != svn_node_dir)
return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
_("Destination '%s' is not a directory"),
svn_dirent_local_style(dst_parent, pool));
SVN_ERR(svn_io_check_path(dst_path, &kind, subpool));
/* Create the new directory. */
/* ### TODO: copy permissions (needs apr_file_attrs_get()) */
SVN_ERR(svn_io_make_dir_recursively(dst_path, pool));
/* Loop over the dirents in SRC. ('.' and '..' are auto-excluded) */
SVN_ERR(svn_io_dir_open(&this_dir, src, subpool));
for (status = apr_dir_read(&this_entry, flags, this_dir);
status == APR_SUCCESS;
status = apr_dir_read(&this_entry, flags, this_dir))
{
if ((this_entry.name[0] == '.')
&& ((this_entry.name[1] == '\0')
|| ((this_entry.name[1] == '.')
&& (this_entry.name[2] == '\0'))))
{
continue;
}
else
{
const char *entryname_utf8;
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
SVN_ERR(entry_name_to_utf8(&entryname_utf8, this_entry.name,
src, subpool));
if (this_entry.filetype == APR_REG) /* regular file */
{
SVN_ERR(hotcopy_io_dir_file_copy(src, dst_path, entryname_utf8,
subpool));
}
else if (this_entry.filetype == APR_LNK) /* symlink */
{
const char *src_target = svn_dirent_join(src, entryname_utf8,
subpool);
const char *dst_target = svn_dirent_join(dst_path,
entryname_utf8,
subpool);
SVN_ERR(svn_io_copy_link(src_target, dst_target,
subpool));
}
else if (this_entry.filetype == APR_DIR) /* recurse */
{
const char *src_target;
/* Prevent infinite recursion by filtering off our
newly created destination path. */
if (strcmp(src, dst_parent) == 0
&& strcmp(entryname_utf8, dst_basename) == 0)
continue;
src_target = svn_dirent_join(src, entryname_utf8, subpool);
SVN_ERR(hotcopy_io_copy_dir_recursively(src_target,
dst_path,
entryname_utf8,
copy_perms,
cancel_func,
cancel_baton,
subpool));
}
/* ### support other APR node types someday?? */
}
}
if (! (APR_STATUS_IS_ENOENT(status)))
return svn_error_wrap_apr(status, _("Can't read directory '%s'"),
svn_dirent_local_style(src, pool));
status = apr_dir_close(this_dir);
if (status)
return svn_error_wrap_apr(status, _("Error closing directory '%s'"),
svn_dirent_local_style(src, pool));
/* Free any memory used by recursion */
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
/* Copy an un-packed revision or revprop file for revision REV from SRC_SUBDIR
* to DST_SUBDIR. Assume a sharding layout based on MAX_FILES_PER_DIR.
* Use SCRATCH_POOL for temporary allocations. */
static svn_error_t *
hotcopy_copy_shard_file(const char *src_subdir,
const char *dst_subdir,
svn_revnum_t rev,
int max_files_per_dir,
apr_pool_t *scratch_pool)
{
const char *src_subdir_shard = src_subdir,
*dst_subdir_shard = dst_subdir;
if (max_files_per_dir)
{
const char *shard = apr_psprintf(scratch_pool, "%ld",
rev / max_files_per_dir);
src_subdir_shard = svn_dirent_join(src_subdir, shard, scratch_pool);
dst_subdir_shard = svn_dirent_join(dst_subdir, shard, scratch_pool);
if (rev % max_files_per_dir == 0)
{
SVN_ERR(svn_io_make_dir_recursively(dst_subdir_shard, scratch_pool));
SVN_ERR(svn_io_copy_perms(dst_subdir, dst_subdir_shard,
scratch_pool));
}
}
SVN_ERR(hotcopy_io_dir_file_copy(src_subdir_shard, dst_subdir_shard,
apr_psprintf(scratch_pool, "%ld", rev),
scratch_pool));
return SVN_NO_ERROR;
}
/* Copy a packed shard containing revision REV, and which contains
* MAX_FILES_PER_DIR revisions, from SRC_FS to DST_FS.
* Update *DST_MIN_UNPACKED_REV in case the shard is new in DST_FS.
* Do not re-copy data which already exists in DST_FS.
* Use SCRATCH_POOL for temporary allocations. */
static svn_error_t *
hotcopy_copy_packed_shard(svn_revnum_t *dst_min_unpacked_rev,
svn_fs_t *src_fs,
svn_fs_t *dst_fs,
svn_revnum_t rev,
int max_files_per_dir,
apr_pool_t *scratch_pool)
{
const char *src_subdir;
const char *dst_subdir;
const char *packed_shard;
const char *src_subdir_packed_shard;
svn_revnum_t revprop_rev;
apr_pool_t *iterpool;
fs_fs_data_t *src_ffd = src_fs->fsap_data;
/* Copy the packed shard. */
src_subdir = svn_dirent_join(src_fs->path, PATH_REVS_DIR, scratch_pool);
dst_subdir = svn_dirent_join(dst_fs->path, PATH_REVS_DIR, scratch_pool);
packed_shard = apr_psprintf(scratch_pool, "%ld" PATH_EXT_PACKED_SHARD,
rev / max_files_per_dir);
src_subdir_packed_shard = svn_dirent_join(src_subdir, packed_shard,
scratch_pool);
SVN_ERR(hotcopy_io_copy_dir_recursively(src_subdir_packed_shard,
dst_subdir, packed_shard,
TRUE /* copy_perms */,
NULL /* cancel_func */, NULL,
scratch_pool));
/* Copy revprops belonging to revisions in this pack. */
src_subdir = svn_dirent_join(src_fs->path, PATH_REVPROPS_DIR, scratch_pool);
dst_subdir = svn_dirent_join(dst_fs->path, PATH_REVPROPS_DIR, scratch_pool);
if ( src_ffd->format < SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT
|| src_ffd->min_unpacked_rev < rev + max_files_per_dir)
{
/* copy unpacked revprops rev by rev */
iterpool = svn_pool_create(scratch_pool);
for (revprop_rev = rev;
revprop_rev < rev + max_files_per_dir;
revprop_rev++)
{
svn_pool_clear(iterpool);
SVN_ERR(hotcopy_copy_shard_file(src_subdir, dst_subdir,
revprop_rev, max_files_per_dir,
iterpool));
}
svn_pool_destroy(iterpool);
}
else
{
/* revprop for revision 0 will never be packed */
if (rev == 0)
SVN_ERR(hotcopy_copy_shard_file(src_subdir, dst_subdir,
0, max_files_per_dir,
scratch_pool));
/* packed revprops folder */
packed_shard = apr_psprintf(scratch_pool, "%ld" PATH_EXT_PACKED_SHARD,
rev / max_files_per_dir);
src_subdir_packed_shard = svn_dirent_join(src_subdir, packed_shard,
scratch_pool);
SVN_ERR(hotcopy_io_copy_dir_recursively(src_subdir_packed_shard,
dst_subdir, packed_shard,
TRUE /* copy_perms */,
NULL /* cancel_func */, NULL,
scratch_pool));
}
/* If necessary, update the min-unpacked rev file in the hotcopy. */
if (*dst_min_unpacked_rev < rev + max_files_per_dir)
{
*dst_min_unpacked_rev = rev + max_files_per_dir;
SVN_ERR(write_revnum_file(dst_fs->path, PATH_MIN_UNPACKED_REV,
*dst_min_unpacked_rev,
scratch_pool));
}
return SVN_NO_ERROR;
}
/* If NEW_YOUNGEST is younger than *DST_YOUNGEST, update the 'current'
* file in DST_FS and set *DST_YOUNGEST to NEW_YOUNGEST.
* Use SCRATCH_POOL for temporary allocations. */
static svn_error_t *
hotcopy_update_current(svn_revnum_t *dst_youngest,
svn_fs_t *dst_fs,
svn_revnum_t new_youngest,
apr_pool_t *scratch_pool)
{
char next_node_id[MAX_KEY_SIZE] = "0";
char next_copy_id[MAX_KEY_SIZE] = "0";
fs_fs_data_t *dst_ffd = dst_fs->fsap_data;
if (*dst_youngest >= new_youngest)
return SVN_NO_ERROR;
/* If necessary, get new current next_node and next_copy IDs. */
if (dst_ffd->format < SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT)
{
apr_off_t root_offset;
apr_file_t *rev_file;
+ char max_node_id[MAX_KEY_SIZE] = "0";
+ char max_copy_id[MAX_KEY_SIZE] = "0";
+ apr_size_t len;
if (dst_ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT)
SVN_ERR(update_min_unpacked_rev(dst_fs, scratch_pool));
SVN_ERR(open_pack_or_rev_file(&rev_file, dst_fs, new_youngest,
scratch_pool));
SVN_ERR(get_root_changes_offset(&root_offset, NULL, rev_file,
dst_fs, new_youngest, scratch_pool));
SVN_ERR(recover_find_max_ids(dst_fs, new_youngest, rev_file,
- root_offset, next_node_id, next_copy_id,
+ root_offset, max_node_id, max_copy_id,
scratch_pool));
SVN_ERR(svn_io_file_close(rev_file, scratch_pool));
+
+ /* We store the _next_ ids. */
+ len = strlen(max_node_id);
+ svn_fs_fs__next_key(max_node_id, &len, next_node_id);
+ len = strlen(max_copy_id);
+ svn_fs_fs__next_key(max_copy_id, &len, next_copy_id);
}
/* Update 'current'. */
SVN_ERR(write_current(dst_fs, new_youngest, next_node_id, next_copy_id,
scratch_pool));
*dst_youngest = new_youngest;
return SVN_NO_ERROR;
}
/* Remove revision or revprop files between START_REV (inclusive) and
* END_REV (non-inclusive) from folder DST_SUBDIR in DST_FS. Assume
* sharding as per MAX_FILES_PER_DIR.
* Use SCRATCH_POOL for temporary allocations. */
static svn_error_t *
hotcopy_remove_files(svn_fs_t *dst_fs,
const char *dst_subdir,
svn_revnum_t start_rev,
svn_revnum_t end_rev,
int max_files_per_dir,
apr_pool_t *scratch_pool)
{
const char *shard;
const char *dst_subdir_shard;
svn_revnum_t rev;
apr_pool_t *iterpool;
/* Pre-compute paths for initial shard. */
shard = apr_psprintf(scratch_pool, "%ld", start_rev / max_files_per_dir);
dst_subdir_shard = svn_dirent_join(dst_subdir, shard, scratch_pool);
iterpool = svn_pool_create(scratch_pool);
for (rev = start_rev; rev < end_rev; rev++)
{
const char *path;
svn_pool_clear(iterpool);
/* If necessary, update paths for shard. */
if (rev != start_rev && rev % max_files_per_dir == 0)
{
shard = apr_psprintf(iterpool, "%ld", rev / max_files_per_dir);
dst_subdir_shard = svn_dirent_join(dst_subdir, shard, scratch_pool);
}
/* remove files for REV */
path = svn_dirent_join(dst_subdir_shard,
apr_psprintf(iterpool, "%ld", rev),
iterpool);
/* Make the rev file writable and remove it. */
SVN_ERR(svn_io_set_file_read_write(path, TRUE, iterpool));
SVN_ERR(svn_io_remove_file2(path, TRUE, iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Remove revisions between START_REV (inclusive) and END_REV (non-inclusive)
* from DST_FS. Assume sharding as per MAX_FILES_PER_DIR.
* Use SCRATCH_POOL for temporary allocations. */
static svn_error_t *
hotcopy_remove_rev_files(svn_fs_t *dst_fs,
svn_revnum_t start_rev,
svn_revnum_t end_rev,
int max_files_per_dir,
apr_pool_t *scratch_pool)
{
SVN_ERR_ASSERT(start_rev <= end_rev);
SVN_ERR(hotcopy_remove_files(dst_fs,
svn_dirent_join(dst_fs->path,
PATH_REVS_DIR,
scratch_pool),
start_rev, end_rev,
max_files_per_dir, scratch_pool));
return SVN_NO_ERROR;
}
/* Remove revision properties between START_REV (inclusive) and END_REV
* (non-inclusive) from DST_FS. Assume sharding as per MAX_FILES_PER_DIR.
* Use SCRATCH_POOL for temporary allocations. Revision 0 revprops will
* not be deleted. */
static svn_error_t *
hotcopy_remove_revprop_files(svn_fs_t *dst_fs,
svn_revnum_t start_rev,
svn_revnum_t end_rev,
int max_files_per_dir,
apr_pool_t *scratch_pool)
{
SVN_ERR_ASSERT(start_rev <= end_rev);
/* don't delete rev 0 props */
SVN_ERR(hotcopy_remove_files(dst_fs,
svn_dirent_join(dst_fs->path,
PATH_REVPROPS_DIR,
scratch_pool),
start_rev ? start_rev : 1, end_rev,
max_files_per_dir, scratch_pool));
return SVN_NO_ERROR;
}
/* Verify that DST_FS is a suitable destination for an incremental
* hotcopy from SRC_FS. */
static svn_error_t *
hotcopy_incremental_check_preconditions(svn_fs_t *src_fs,
svn_fs_t *dst_fs,
apr_pool_t *pool)
{
fs_fs_data_t *src_ffd = src_fs->fsap_data;
fs_fs_data_t *dst_ffd = dst_fs->fsap_data;
/* We only support incremental hotcopy between the same format. */
if (src_ffd->format != dst_ffd->format)
return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("The FSFS format (%d) of the hotcopy source does not match the "
"FSFS format (%d) of the hotcopy destination; please upgrade "
"both repositories to the same format"),
src_ffd->format, dst_ffd->format);
/* Make sure the UUID of source and destination match up.
* We don't want to copy over a different repository. */
if (strcmp(src_fs->uuid, dst_fs->uuid) != 0)
return svn_error_create(SVN_ERR_RA_UUID_MISMATCH, NULL,
_("The UUID of the hotcopy source does "
"not match the UUID of the hotcopy "
"destination"));
/* Also require same shard size. */
if (src_ffd->max_files_per_dir != dst_ffd->max_files_per_dir)
return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("The sharding layout configuration "
"of the hotcopy source does not match "
"the sharding layout configuration of "
"the hotcopy destination"));
return SVN_NO_ERROR;
}
/* Remove folder PATH. Ignore errors due to the sub-tree not being empty.
* CANCEL_FUNC and CANCEL_BATON do the usual thing.
* Use POOL for temporary allocations.
*/
static svn_error_t *
remove_folder(const char *path,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
svn_error_t *err = svn_io_remove_dir2(path, TRUE,
cancel_func, cancel_baton, pool);
if (err && APR_STATUS_IS_ENOTEMPTY(err->apr_err))
{
svn_error_clear(err);
err = SVN_NO_ERROR;
}
return svn_error_trace(err);
}
/* Baton for hotcopy_body(). */
struct hotcopy_body_baton {
svn_fs_t *src_fs;
svn_fs_t *dst_fs;
svn_boolean_t incremental;
svn_cancel_func_t cancel_func;
void *cancel_baton;
} hotcopy_body_baton;
/* Perform a hotcopy, either normal or incremental.
*
* Normal hotcopy assumes that the destination exists as an empty
* directory. It behaves like an incremental hotcopy except that
* none of the copied files already exist in the destination.
*
* An incremental hotcopy copies only changed or new files to the destination,
* and removes files from the destination no longer present in the source.
* While the incremental hotcopy is running, readers should still be able
* to access the destintation repository without error and should not see
* revisions currently in progress of being copied. Readers are able to see
* new fully copied revisions even if the entire incremental hotcopy procedure
* has not yet completed.
*
* Writers are blocked out completely during the entire incremental hotcopy
* process to ensure consistency. This function assumes that the repository
* write-lock is held.
*/
static svn_error_t *
hotcopy_body(void *baton, apr_pool_t *pool)
{
struct hotcopy_body_baton *hbb = baton;
svn_fs_t *src_fs = hbb->src_fs;
fs_fs_data_t *src_ffd = src_fs->fsap_data;
svn_fs_t *dst_fs = hbb->dst_fs;
fs_fs_data_t *dst_ffd = dst_fs->fsap_data;
int max_files_per_dir = src_ffd->max_files_per_dir;
svn_boolean_t incremental = hbb->incremental;
svn_cancel_func_t cancel_func = hbb->cancel_func;
void* cancel_baton = hbb->cancel_baton;
svn_revnum_t src_youngest;
svn_revnum_t dst_youngest;
svn_revnum_t rev;
svn_revnum_t src_min_unpacked_rev;
svn_revnum_t dst_min_unpacked_rev;
const char *src_subdir;
const char *dst_subdir;
const char *revprop_src_subdir;
const char *revprop_dst_subdir;
apr_pool_t *iterpool;
svn_node_kind_t kind;
/* Try to copy the config.
*
* ### We try copying the config file before doing anything else,
* ### because higher layers will abort the hotcopy if we throw
* ### an error from this function, and that renders the hotcopy
* ### unusable anyway. */
if (src_ffd->format >= SVN_FS_FS__MIN_CONFIG_FILE)
{
svn_error_t *err;
err = svn_io_dir_file_copy(src_fs->path, dst_fs->path, PATH_CONFIG,
pool);
if (err)
{
if (APR_STATUS_IS_ENOENT(err->apr_err))
{
/* 1.6.0 to 1.6.11 did not copy the configuration file during
* hotcopy. So if we're hotcopying a repository which has been
* created as a hotcopy itself, it's possible that fsfs.conf
* does not exist. Ask the user to re-create it.
*
* ### It would be nice to make this a non-fatal error,
* ### but this function does not get an svn_fs_t object
* ### so we have no way of just printing a warning via
* ### the fs->warning() callback. */
const char *msg;
const char *src_abspath;
const char *dst_abspath;
const char *config_relpath;
svn_error_t *err2;
config_relpath = svn_dirent_join(src_fs->path, PATH_CONFIG, pool);
err2 = svn_dirent_get_absolute(&src_abspath, src_fs->path, pool);
if (err2)
return svn_error_trace(svn_error_compose_create(err, err2));
err2 = svn_dirent_get_absolute(&dst_abspath, dst_fs->path, pool);
if (err2)
return svn_error_trace(svn_error_compose_create(err, err2));
/* ### hack: strip off the 'db/' directory from paths so
* ### they make sense to the user */
src_abspath = svn_dirent_dirname(src_abspath, pool);
dst_abspath = svn_dirent_dirname(dst_abspath, pool);
msg = apr_psprintf(pool,
_("Failed to create hotcopy at '%s'. "
"The file '%s' is missing from the source "
"repository. Please create this file, for "
"instance by running 'svnadmin upgrade %s'"),
dst_abspath, config_relpath, src_abspath);
return svn_error_quick_wrap(err, msg);
}
else
return svn_error_trace(err);
}
}
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
/* Find the youngest revision in the source and destination.
* We only support hotcopies from sources with an equal or greater amount
* of revisions than the destination.
* This also catches the case where users accidentally swap the
* source and destination arguments. */
SVN_ERR(get_youngest(&src_youngest, src_fs->path, pool));
if (incremental)
{
SVN_ERR(get_youngest(&dst_youngest, dst_fs->path, pool));
if (src_youngest < dst_youngest)
return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("The hotcopy destination already contains more revisions "
"(%lu) than the hotcopy source contains (%lu); are source "
"and destination swapped?"),
dst_youngest, src_youngest);
}
else
dst_youngest = 0;
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
/* Copy the min unpacked rev, and read its value. */
if (src_ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT)
{
const char *min_unpacked_rev_path;
min_unpacked_rev_path = svn_dirent_join(src_fs->path,
PATH_MIN_UNPACKED_REV,
pool);
SVN_ERR(read_min_unpacked_rev(&src_min_unpacked_rev,
min_unpacked_rev_path,
pool));
min_unpacked_rev_path = svn_dirent_join(dst_fs->path,
PATH_MIN_UNPACKED_REV,
pool);
SVN_ERR(read_min_unpacked_rev(&dst_min_unpacked_rev,
min_unpacked_rev_path,
pool));
/* We only support packs coming from the hotcopy source.
* The destination should not be packed independently from
* the source. This also catches the case where users accidentally
* swap the source and destination arguments. */
if (src_min_unpacked_rev < dst_min_unpacked_rev)
return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("The hotcopy destination already contains "
"more packed revisions (%lu) than the "
"hotcopy source contains (%lu)"),
dst_min_unpacked_rev - 1,
src_min_unpacked_rev - 1);
SVN_ERR(svn_io_dir_file_copy(src_fs->path, dst_fs->path,
PATH_MIN_UNPACKED_REV, pool));
}
else
{
src_min_unpacked_rev = 0;
dst_min_unpacked_rev = 0;
}
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
/*
* Copy the necessary rev files.
*/
src_subdir = svn_dirent_join(src_fs->path, PATH_REVS_DIR, pool);
dst_subdir = svn_dirent_join(dst_fs->path, PATH_REVS_DIR, pool);
SVN_ERR(svn_io_make_dir_recursively(dst_subdir, pool));
iterpool = svn_pool_create(pool);
/* First, copy packed shards. */
for (rev = 0; rev < src_min_unpacked_rev; rev += max_files_per_dir)
{
svn_pool_clear(iterpool);
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
/* Copy the packed shard. */
SVN_ERR(hotcopy_copy_packed_shard(&dst_min_unpacked_rev,
src_fs, dst_fs,
rev, max_files_per_dir,
iterpool));
/* If necessary, update 'current' to the most recent packed rev,
* so readers can see new revisions which arrived in this pack. */
SVN_ERR(hotcopy_update_current(&dst_youngest, dst_fs,
rev + max_files_per_dir - 1,
iterpool));
/* Remove revision files which are now packed. */
if (incremental)
{
SVN_ERR(hotcopy_remove_rev_files(dst_fs, rev,
rev + max_files_per_dir,
max_files_per_dir, iterpool));
if (dst_ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT)
SVN_ERR(hotcopy_remove_revprop_files(dst_fs, rev,
rev + max_files_per_dir,
max_files_per_dir,
iterpool));
}
/* Now that all revisions have moved into the pack, the original
* rev dir can be removed. */
SVN_ERR(remove_folder(path_rev_shard(dst_fs, rev, iterpool),
cancel_func, cancel_baton, iterpool));
if (rev > 0 && dst_ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT)
SVN_ERR(remove_folder(path_revprops_shard(dst_fs, rev, iterpool),
cancel_func, cancel_baton, iterpool));
}
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
/* Now, copy pairs of non-packed revisions and revprop files.
* If necessary, update 'current' after copying all files from a shard. */
SVN_ERR_ASSERT(rev == src_min_unpacked_rev);
SVN_ERR_ASSERT(src_min_unpacked_rev == dst_min_unpacked_rev);
revprop_src_subdir = svn_dirent_join(src_fs->path, PATH_REVPROPS_DIR, pool);
revprop_dst_subdir = svn_dirent_join(dst_fs->path, PATH_REVPROPS_DIR, pool);
SVN_ERR(svn_io_make_dir_recursively(revprop_dst_subdir, pool));
for (; rev <= src_youngest; rev++)
{
svn_error_t *err;
svn_pool_clear(iterpool);
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
/* Copy the rev file. */
err = hotcopy_copy_shard_file(src_subdir, dst_subdir,
rev, max_files_per_dir,
iterpool);
if (err)
{
if (APR_STATUS_IS_ENOENT(err->apr_err) &&
src_ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT)
{
svn_error_clear(err);
/* The source rev file does not exist. This can happen if the
* source repository is being packed concurrently with this
* hotcopy operation.
*
* If the new revision is now packed, and the youngest revision
* we're interested in is not inside this pack, try to copy the
* pack instead.
*
* If the youngest revision ended up being packed, don't try
* to be smart and work around this. Just abort the hotcopy. */
SVN_ERR(update_min_unpacked_rev(src_fs, pool));
if (is_packed_rev(src_fs, rev))
{
if (is_packed_rev(src_fs, src_youngest))
return svn_error_createf(
SVN_ERR_FS_NO_SUCH_REVISION, NULL,
_("The assumed HEAD revision (%lu) of the "
"hotcopy source has been packed while the "
"hotcopy was in progress; please restart "
"the hotcopy operation"),
src_youngest);
SVN_ERR(hotcopy_copy_packed_shard(&dst_min_unpacked_rev,
src_fs, dst_fs,
rev, max_files_per_dir,
iterpool));
rev = dst_min_unpacked_rev;
continue;
}
else
return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
_("Revision %lu disappeared from the "
"hotcopy source while hotcopy was "
"in progress"), rev);
}
else
return svn_error_trace(err);
}
/* Copy the revprop file. */
SVN_ERR(hotcopy_copy_shard_file(revprop_src_subdir,
revprop_dst_subdir,
rev, max_files_per_dir,
iterpool));
/* After completing a full shard, update 'current'. */
if (max_files_per_dir && rev % max_files_per_dir == 0)
SVN_ERR(hotcopy_update_current(&dst_youngest, dst_fs, rev, iterpool));
}
svn_pool_destroy(iterpool);
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
/* We assume that all revisions were copied now, i.e. we didn't exit the
* above loop early. 'rev' was last incremented during exit of the loop. */
SVN_ERR_ASSERT(rev == src_youngest + 1);
/* All revisions were copied. Update 'current'. */
SVN_ERR(hotcopy_update_current(&dst_youngest, dst_fs, src_youngest, pool));
/* Replace the locks tree.
* This is racy in case readers are currently trying to list locks in
* the destination. However, we need to get rid of stale locks.
* This is the simplest way of doing this, so we accept this small race. */
dst_subdir = svn_dirent_join(dst_fs->path, PATH_LOCKS_DIR, pool);
SVN_ERR(svn_io_remove_dir2(dst_subdir, TRUE, cancel_func, cancel_baton,
pool));
src_subdir = svn_dirent_join(src_fs->path, PATH_LOCKS_DIR, pool);
SVN_ERR(svn_io_check_path(src_subdir, &kind, pool));
if (kind == svn_node_dir)
SVN_ERR(svn_io_copy_dir_recursively(src_subdir, dst_fs->path,
PATH_LOCKS_DIR, TRUE,
cancel_func, cancel_baton, pool));
/* Now copy the node-origins cache tree. */
src_subdir = svn_dirent_join(src_fs->path, PATH_NODE_ORIGINS_DIR, pool);
SVN_ERR(svn_io_check_path(src_subdir, &kind, pool));
if (kind == svn_node_dir)
SVN_ERR(hotcopy_io_copy_dir_recursively(src_subdir, dst_fs->path,
PATH_NODE_ORIGINS_DIR, TRUE,
cancel_func, cancel_baton, pool));
/*
* NB: Data copied below is only read by writers, not readers.
* Writers are still locked out at this point.
*/
if (dst_ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT)
{
/* Copy the rep cache and then remove entries for revisions
* younger than the destination's youngest revision. */
src_subdir = svn_dirent_join(src_fs->path, REP_CACHE_DB_NAME, pool);
dst_subdir = svn_dirent_join(dst_fs->path, REP_CACHE_DB_NAME, pool);
SVN_ERR(svn_io_check_path(src_subdir, &kind, pool));
if (kind == svn_node_file)
{
SVN_ERR(svn_sqlite__hotcopy(src_subdir, dst_subdir, pool));
SVN_ERR(svn_fs_fs__del_rep_reference(dst_fs, dst_youngest, pool));
}
}
/* Copy the txn-current file. */
if (dst_ffd->format >= SVN_FS_FS__MIN_TXN_CURRENT_FORMAT)
SVN_ERR(svn_io_dir_file_copy(src_fs->path, dst_fs->path,
PATH_TXN_CURRENT, pool));
/* If a revprop generation file exists in the source filesystem,
* reset it to zero (since this is on a different path, it will not
* overlap with data already in cache). Also, clean up stale files
* used for the named atomics implementation. */
SVN_ERR(svn_io_check_path(path_revprop_generation(src_fs, pool),
&kind, pool));
if (kind == svn_node_file)
SVN_ERR(write_revprop_generation_file(dst_fs, 0, pool));
SVN_ERR(cleanup_revprop_namespace(dst_fs));
/* Hotcopied FS is complete. Stamp it with a format file. */
SVN_ERR(write_format(svn_dirent_join(dst_fs->path, PATH_FORMAT, pool),
dst_ffd->format, max_files_per_dir, TRUE, pool));
return SVN_NO_ERROR;
}
/* Set up shared data between SRC_FS and DST_FS. */
static void
hotcopy_setup_shared_fs_data(svn_fs_t *src_fs, svn_fs_t *dst_fs)
{
fs_fs_data_t *src_ffd = src_fs->fsap_data;
fs_fs_data_t *dst_ffd = dst_fs->fsap_data;
/* The common pool and mutexes are shared between src and dst filesystems.
* During hotcopy we only grab the mutexes for the destination, so there
* is no risk of dead-lock. We don't write to the src filesystem. Shared
* data for the src_fs has already been initialised in fs_hotcopy(). */
dst_ffd->shared = src_ffd->shared;
}
/* Create an empty filesystem at DST_FS at DST_PATH with the same
* configuration as SRC_FS (uuid, format, and other parameters).
* After creation DST_FS has no revisions, not even revision zero. */
static svn_error_t *
hotcopy_create_empty_dest(svn_fs_t *src_fs,
svn_fs_t *dst_fs,
const char *dst_path,
apr_pool_t *pool)
{
fs_fs_data_t *src_ffd = src_fs->fsap_data;
fs_fs_data_t *dst_ffd = dst_fs->fsap_data;
dst_fs->path = apr_pstrdup(pool, dst_path);
dst_ffd->max_files_per_dir = src_ffd->max_files_per_dir;
dst_ffd->config = src_ffd->config;
dst_ffd->format = src_ffd->format;
/* Create the revision data directories. */
if (dst_ffd->max_files_per_dir)
SVN_ERR(svn_io_make_dir_recursively(path_rev_shard(dst_fs, 0, pool),
pool));
else
SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(dst_path,
PATH_REVS_DIR, pool),
pool));
/* Create the revprops directory. */
if (src_ffd->max_files_per_dir)
SVN_ERR(svn_io_make_dir_recursively(path_revprops_shard(dst_fs, 0, pool),
pool));
else
SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(dst_path,
PATH_REVPROPS_DIR,
pool),
pool));
/* Create the transaction directory. */
SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(dst_path, PATH_TXNS_DIR,
pool),
pool));
/* Create the protorevs directory. */
if (dst_ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(dst_path,
PATH_TXN_PROTOS_DIR,
pool),
pool));
/* Create the 'current' file. */
SVN_ERR(svn_io_file_create(svn_fs_fs__path_current(dst_fs, pool),
(dst_ffd->format >=
SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT
? "0\n" : "0 1 1\n"),
pool));
/* Create lock file and UUID. */
SVN_ERR(svn_io_file_create(path_lock(dst_fs, pool), "", pool));
SVN_ERR(svn_fs_fs__set_uuid(dst_fs, src_fs->uuid, pool));
/* Create the min unpacked rev file. */
if (dst_ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT)
SVN_ERR(svn_io_file_create(path_min_unpacked_rev(dst_fs, pool),
"0\n", pool));
/* Create the txn-current file if the repository supports
the transaction sequence file. */
if (dst_ffd->format >= SVN_FS_FS__MIN_TXN_CURRENT_FORMAT)
{
SVN_ERR(svn_io_file_create(path_txn_current(dst_fs, pool),
"0\n", pool));
SVN_ERR(svn_io_file_create(path_txn_current_lock(dst_fs, pool),
"", pool));
}
dst_ffd->youngest_rev_cache = 0;
hotcopy_setup_shared_fs_data(src_fs, dst_fs);
SVN_ERR(svn_fs_fs__initialize_caches(dst_fs, pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__hotcopy(svn_fs_t *src_fs,
svn_fs_t *dst_fs,
const char *src_path,
const char *dst_path,
svn_boolean_t incremental,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
struct hotcopy_body_baton hbb;
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
SVN_ERR(svn_fs_fs__open(src_fs, src_path, pool));
if (incremental)
{
const char *dst_format_abspath;
svn_node_kind_t dst_format_kind;
/* Check destination format to be sure we know how to incrementally
* hotcopy to the destination FS. */
dst_format_abspath = svn_dirent_join(dst_path, PATH_FORMAT, pool);
SVN_ERR(svn_io_check_path(dst_format_abspath, &dst_format_kind, pool));
if (dst_format_kind == svn_node_none)
{
/* Destination doesn't exist yet. Perform a normal hotcopy to a
* empty destination using the same configuration as the source. */
SVN_ERR(hotcopy_create_empty_dest(src_fs, dst_fs, dst_path, pool));
}
else
{
/* Check the existing repository. */
SVN_ERR(svn_fs_fs__open(dst_fs, dst_path, pool));
SVN_ERR(hotcopy_incremental_check_preconditions(src_fs, dst_fs,
pool));
hotcopy_setup_shared_fs_data(src_fs, dst_fs);
SVN_ERR(svn_fs_fs__initialize_caches(dst_fs, pool));
}
}
else
{
/* Start out with an empty destination using the same configuration
* as the source. */
SVN_ERR(hotcopy_create_empty_dest(src_fs, dst_fs, dst_path, pool));
}
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
hbb.src_fs = src_fs;
hbb.dst_fs = dst_fs;
hbb.incremental = incremental;
hbb.cancel_func = cancel_func;
hbb.cancel_baton = cancel_baton;
SVN_ERR(svn_fs_fs__with_write_lock(dst_fs, hotcopy_body, &hbb, pool));
return SVN_NO_ERROR;
}
Index: vendor/subversion/dist/subversion/libsvn_fs_fs/rep-cache-db.h
===================================================================
--- vendor/subversion/dist/subversion/libsvn_fs_fs/rep-cache-db.h (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_fs_fs/rep-cache-db.h (revision 286501)
@@ -1,83 +1,83 @@
-/* This file is automatically generated from rep-cache-db.sql and .dist_sandbox/subversion-1.8.10/subversion/libsvn_fs_fs/token-map.h.
+/* This file is automatically generated from rep-cache-db.sql and .dist_sandbox/subversion-1.8.14/subversion/libsvn_fs_fs/token-map.h.
* Do not edit this file -- edit the source and rerun gen-make.py */
#define STMT_CREATE_SCHEMA 0
#define STMT_0_INFO {"STMT_CREATE_SCHEMA", NULL}
#define STMT_0 \
"CREATE TABLE rep_cache ( " \
" hash TEXT NOT NULL PRIMARY KEY, " \
" revision INTEGER NOT NULL, " \
" offset INTEGER NOT NULL, " \
" size INTEGER NOT NULL, " \
" expanded_size INTEGER NOT NULL " \
" ); " \
"PRAGMA USER_VERSION = 1; " \
""
#define STMT_GET_REP 1
#define STMT_1_INFO {"STMT_GET_REP", NULL}
#define STMT_1 \
"SELECT revision, offset, size, expanded_size " \
"FROM rep_cache " \
"WHERE hash = ?1 " \
""
#define STMT_SET_REP 2
#define STMT_2_INFO {"STMT_SET_REP", NULL}
#define STMT_2 \
"INSERT OR FAIL INTO rep_cache (hash, revision, offset, size, expanded_size) " \
"VALUES (?1, ?2, ?3, ?4, ?5) " \
""
#define STMT_GET_REPS_FOR_RANGE 3
#define STMT_3_INFO {"STMT_GET_REPS_FOR_RANGE", NULL}
#define STMT_3 \
"SELECT hash, revision, offset, size, expanded_size " \
"FROM rep_cache " \
"WHERE revision >= ?1 AND revision <= ?2 " \
""
#define STMT_GET_MAX_REV 4
#define STMT_4_INFO {"STMT_GET_MAX_REV", NULL}
#define STMT_4 \
"SELECT MAX(revision) " \
"FROM rep_cache " \
""
#define STMT_DEL_REPS_YOUNGER_THAN_REV 5
#define STMT_5_INFO {"STMT_DEL_REPS_YOUNGER_THAN_REV", NULL}
#define STMT_5 \
"DELETE FROM rep_cache " \
"WHERE revision > ?1 " \
""
#define STMT_LOCK_REP 6
#define STMT_6_INFO {"STMT_LOCK_REP", NULL}
#define STMT_6 \
"BEGIN TRANSACTION; " \
"INSERT INTO rep_cache VALUES ('dummy', 0, 0, 0, 0) " \
""
#define REP_CACHE_DB_SQL_DECLARE_STATEMENTS(varname) \
static const char * const varname[] = { \
STMT_0, \
STMT_1, \
STMT_2, \
STMT_3, \
STMT_4, \
STMT_5, \
STMT_6, \
NULL \
}
#define REP_CACHE_DB_SQL_DECLARE_STATEMENT_INFO(varname) \
static const char * const varname[][2] = { \
STMT_0_INFO, \
STMT_1_INFO, \
STMT_2_INFO, \
STMT_3_INFO, \
STMT_4_INFO, \
STMT_5_INFO, \
STMT_6_INFO, \
{NULL, NULL} \
}
Index: vendor/subversion/dist/subversion/libsvn_fs_fs/tree.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_fs_fs/tree.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_fs_fs/tree.c (revision 286501)
@@ -1,4447 +1,4324 @@
/* tree.c : tree-like filesystem, built on DAG filesystem
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
/* The job of this layer is to take a filesystem with lots of node
sharing going on --- the real DAG filesystem as it appears in the
database --- and make it look and act like an ordinary tree
filesystem, with no sharing.
We do just-in-time cloning: you can walk from some unfinished
transaction's root down into directories and files shared with
committed revisions; as soon as you try to change something, the
appropriate nodes get cloned (and parent directory entries updated)
invisibly, behind your back. Any other references you have to
nodes that have been cloned by other changes, even made by other
processes, are automatically updated to point to the right clones. */
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <apr_pools.h>
#include <apr_hash.h>
#include "svn_hash.h"
#include "svn_private_config.h"
#include "svn_pools.h"
#include "svn_error.h"
#include "svn_path.h"
#include "svn_mergeinfo.h"
#include "svn_fs.h"
#include "svn_props.h"
#include "fs.h"
#include "key-gen.h"
#include "dag.h"
#include "lock.h"
#include "tree.h"
#include "fs_fs.h"
#include "id.h"
#include "temp_serializer.h"
#include "private/svn_mergeinfo_private.h"
#include "private/svn_subr_private.h"
#include "private/svn_fs_util.h"
#include "private/svn_fspath.h"
#include "../libsvn_fs/fs-loader.h"
/* ### I believe this constant will become internal to reps-strings.c.
### see the comment in window_consumer() for more information. */
/* ### the comment also seems to need tweaking: the log file stuff
### is no longer an issue... */
/* Data written to the filesystem through the svn_fs_apply_textdelta()
interface is cached in memory until the end of the data stream, or
until a size trigger is hit. Define that trigger here (in bytes).
Setting the value to 0 will result in no filesystem buffering at
all. The value only really matters when dealing with file contents
bigger than the value itself. Above that point, large values here
allow the filesystem to buffer more data in memory before flushing
to the database, which increases memory usage but greatly decreases
the amount of disk access (and log-file generation) in database.
Smaller values will limit your overall memory consumption, but can
drastically hurt throughput by necessitating more write operations
to the database (which also generates more log-files). */
#define WRITE_BUFFER_SIZE 512000
/* The root structures.
Why do they contain different data? Well, transactions are mutable
enough that it isn't safe to cache the DAG node for the root
directory or the hash of copyfrom data: somebody else might modify
them concurrently on disk! (Why is the DAG node cache safer than
the root DAG node? When cloning transaction DAG nodes in and out
of the cache, all of the possibly-mutable data from the
node_revision_t inside the dag_node_t is dropped.) Additionally,
revisions are immutable enough that their DAG node cache can be
kept in the FS object and shared among multiple revision root
objects.
*/
typedef struct fs_rev_root_data_t
{
/* A dag node for the revision's root directory. */
dag_node_t *root_dir;
/* Cache structure for mapping const char * PATH to const char
*COPYFROM_STRING, so that paths_changed can remember all the
copyfrom information in the changes file.
COPYFROM_STRING has the format "REV PATH", or is the empty string if
the path was added without history. */
apr_hash_t *copyfrom_cache;
} fs_rev_root_data_t;
typedef struct fs_txn_root_data_t
{
const char *txn_id;
/* Cache of txn DAG nodes (without their nested noderevs, because
* it's mutable). Same keys/values as ffd->rev_node_cache. */
svn_cache__t *txn_node_cache;
} fs_txn_root_data_t;
/* Declared here to resolve the circular dependencies. */
static svn_error_t * get_dag(dag_node_t **dag_node_p,
svn_fs_root_t *root,
const char *path,
- svn_boolean_t needs_lock_cache,
apr_pool_t *pool);
static svn_fs_root_t *make_revision_root(svn_fs_t *fs, svn_revnum_t rev,
dag_node_t *root_dir,
apr_pool_t *pool);
static svn_error_t *make_txn_root(svn_fs_root_t **root_p,
svn_fs_t *fs, const char *txn,
svn_revnum_t base_rev, apr_uint32_t flags,
apr_pool_t *pool);
/*** Node Caching ***/
/* 1st level cache */
/* An entry in the first-level cache. REVISION and PATH form the key that
will ultimately be matched.
*/
typedef struct cache_entry_t
{
/* hash value derived from PATH, REVISION.
Used to short-circuit failed lookups. */
apr_uint32_t hash_value;
/* revision to which the NODE belongs */
svn_revnum_t revision;
/* path of the NODE */
char *path;
/* cached value of strlen(PATH). */
apr_size_t path_len;
/* the node allocated in the cache's pool. NULL for empty entries. */
dag_node_t *node;
} cache_entry_t;
/* Number of entries in the cache. Keep this low to keep pressure on the
CPU caches low as well. A binary value is most efficient. If we walk
a directory tree, we want enough entries to store nodes for all files
without overwriting the nodes for the parent folder. That way, there
will be no unnecessary misses (except for a few random ones caused by
hash collision).
The actual number of instances may be higher but entries that got
overwritten are no longer visible.
*/
enum { BUCKET_COUNT = 256 };
-/* Each pool that has received a DAG node, will hold at least on lock on
- our cache to ensure that the node remains valid despite being allocated
- in the cache's pool. This is the structure to represent the lock.
- */
-typedef struct cache_lock_t
-{
- /* pool holding the lock */
- apr_pool_t *pool;
-
- /* cache being locked */
- fs_fs_dag_cache_t *cache;
-
- /* next lock. NULL at EOL */
- struct cache_lock_t *next;
-
- /* previous lock. NULL at list head. Only then this==cache->first_lock */
- struct cache_lock_t *prev;
-} cache_lock_t;
-
/* The actual cache structure. All nodes will be allocated in POOL.
When the number of INSERTIONS (i.e. objects created form that pool)
exceeds a certain threshold, the pool will be cleared and the cache
with it.
-
- To ensure that nodes returned from this structure remain valid, the
- cache will get locked for the lifetime of the _receiving_ pools (i.e.
- those in which we would allocate the node if there was no cache.).
- The cache will only be cleared FIRST_LOCK is 0.
*/
struct fs_fs_dag_cache_t
{
/* fixed number of (possibly empty) cache entries */
cache_entry_t buckets[BUCKET_COUNT];
/* pool used for all node allocation */
apr_pool_t *pool;
/* number of entries created from POOL since the last cleanup */
apr_size_t insertions;
/* Property lookups etc. have a very high locality (75% re-hit).
Thus, remember the last hit location for optimistic lookup. */
apr_size_t last_hit;
-
- /* List of receiving pools that are still alive. */
- cache_lock_t *first_lock;
};
-/* Cleanup function to be called when a receiving pool gets cleared.
- Unlocks the cache once.
- */
-static apr_status_t
-unlock_cache(void *baton_void)
-{
- cache_lock_t *lock = baton_void;
-
- /* remove lock from chain. Update the head */
- if (lock->next)
- lock->next->prev = lock->prev;
- if (lock->prev)
- lock->prev->next = lock->next;
- else
- lock->cache->first_lock = lock->next;
-
- return APR_SUCCESS;
-}
-
-/* Cleanup function to be called when the cache itself gets destroyed.
- In that case, we must unregister all unlock requests.
- */
-static apr_status_t
-unregister_locks(void *baton_void)
-{
- fs_fs_dag_cache_t *cache = baton_void;
- cache_lock_t *lock;
-
- for (lock = cache->first_lock; lock; lock = lock->next)
- apr_pool_cleanup_kill(lock->pool,
- lock,
- unlock_cache);
-
- return APR_SUCCESS;
-}
-
fs_fs_dag_cache_t*
svn_fs_fs__create_dag_cache(apr_pool_t *pool)
{
fs_fs_dag_cache_t *result = apr_pcalloc(pool, sizeof(*result));
result->pool = svn_pool_create(pool);
- apr_pool_cleanup_register(pool,
- result,
- unregister_locks,
- apr_pool_cleanup_null);
-
return result;
}
-/* Prevent the entries in CACHE from being destroyed, for as long as the
- POOL lives.
- */
-static void
-lock_cache(fs_fs_dag_cache_t* cache, apr_pool_t *pool)
-{
- /* we only need to lock / unlock once per pool. Since we will often ask
- for multiple nodes with the same pool, we can reduce the overhead.
- However, if e.g. pools are being used in an alternating pattern,
- we may lock the cache more than once for the same pool (and register
- just as many cleanup actions).
- */
- cache_lock_t *lock = cache->first_lock;
-
- /* try to find an existing lock for POOL.
- But limit the time spent on chasing pointers. */
- int limiter = 8;
- while (lock && --limiter)
- if (lock->pool == pool)
- return;
-
- /* create a new lock and put it at the beginning of the lock chain */
- lock = apr_palloc(pool, sizeof(*lock));
- lock->cache = cache;
- lock->pool = pool;
- lock->next = cache->first_lock;
- lock->prev = NULL;
-
- if (cache->first_lock)
- cache->first_lock->prev = lock;
- cache->first_lock = lock;
-
- /* instruct POOL to remove the look upon cleanup */
- apr_pool_cleanup_register(pool,
- lock,
- unlock_cache,
- apr_pool_cleanup_null);
-}
-
/* Clears the CACHE at regular intervals (destroying all cached nodes)
*/
static void
auto_clear_dag_cache(fs_fs_dag_cache_t* cache)
{
- if (cache->first_lock == NULL && cache->insertions > BUCKET_COUNT)
+ if (cache->insertions > BUCKET_COUNT)
{
svn_pool_clear(cache->pool);
memset(cache->buckets, 0, sizeof(cache->buckets));
cache->insertions = 0;
}
}
/* For the given REVISION and PATH, return the respective entry in CACHE.
If the entry is empty, its NODE member will be NULL and the caller
may then set it to the corresponding DAG node allocated in CACHE->POOL.
*/
static cache_entry_t *
cache_lookup( fs_fs_dag_cache_t *cache
, svn_revnum_t revision
, const char *path)
{
apr_size_t i, bucket_index;
apr_size_t path_len = strlen(path);
apr_uint32_t hash_value = (apr_uint32_t)revision;
#if SVN_UNALIGNED_ACCESS_IS_OK
/* "randomizing" / distributing factor used in our hash function */
const apr_uint32_t factor = 0xd1f3da69;
#endif
/* optimistic lookup: hit the same bucket again? */
cache_entry_t *result = &cache->buckets[cache->last_hit];
if ( (result->revision == revision)
&& (result->path_len == path_len)
&& !memcmp(result->path, path, path_len))
{
return result;
}
/* need to do a full lookup. Calculate the hash value
(HASH_VALUE has been initialized to REVISION). */
i = 0;
#if SVN_UNALIGNED_ACCESS_IS_OK
/* We relax the dependency chain between iterations by processing
two chunks from the input per hash_value self-multiplication.
The HASH_VALUE update latency is now 1 MUL latency + 1 ADD latency
per 2 chunks instead of 1 chunk.
*/
for (; i + 8 <= path_len; i += 8)
hash_value = hash_value * factor * factor
+ ( *(const apr_uint32_t*)(path + i) * factor
+ *(const apr_uint32_t*)(path + i + 4));
#endif
for (; i < path_len; ++i)
/* Help GCC to minimize the HASH_VALUE update latency by splitting the
MUL 33 of the naive implementation: h = h * 33 + path[i]. This
shortens the dependency chain from 1 shift + 2 ADDs to 1 shift + 1 ADD.
*/
hash_value = hash_value * 32 + (hash_value + (unsigned char)path[i]);
bucket_index = hash_value + (hash_value >> 16);
bucket_index = (bucket_index + (bucket_index >> 8)) % BUCKET_COUNT;
/* access the corresponding bucket and remember its location */
result = &cache->buckets[bucket_index];
cache->last_hit = bucket_index;
/* if it is *NOT* a match, clear the bucket, expect the caller to fill
in the node and count it as an insertion */
if ( (result->hash_value != hash_value)
|| (result->revision != revision)
|| (result->path_len != path_len)
|| memcmp(result->path, path, path_len))
{
result->hash_value = hash_value;
result->revision = revision;
if (result->path_len < path_len)
result->path = apr_palloc(cache->pool, path_len + 1);
result->path_len = path_len;
memcpy(result->path, path, path_len + 1);
result->node = NULL;
cache->insertions++;
}
return result;
}
/* 2nd level cache */
/* Find and return the DAG node cache for ROOT and the key that
should be used for PATH. */
static void
locate_cache(svn_cache__t **cache,
const char **key,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
if (root->is_txn_root)
{
fs_txn_root_data_t *frd = root->fsap_data;
if (cache) *cache = frd->txn_node_cache;
if (key && path) *key = path;
}
else
{
fs_fs_data_t *ffd = root->fs->fsap_data;
if (cache) *cache = ffd->rev_node_cache;
if (key && path) *key
= svn_fs_fs__combine_number_and_string(root->rev, path, pool);
}
}
-/* Return NODE for PATH from ROOT's node cache, or NULL if the node
- isn't cached; read it from the FS. *NODE remains valid until either
- POOL or the FS gets cleared or destroyed (whichever comes first).
-
- Since locking can be expensive and POOL may be long-living, for
- nodes that will not need to survive the next call to this function,
- set NEEDS_LOCK_CACHE to FALSE. */
+/* Return NODE_P for PATH from ROOT's node cache, or NULL if the node
+ isn't cached; read it from the FS. *NODE_P is allocated in POOL. */
static svn_error_t *
dag_node_cache_get(dag_node_t **node_p,
svn_fs_root_t *root,
const char *path,
- svn_boolean_t needs_lock_cache,
apr_pool_t *pool)
{
svn_boolean_t found;
dag_node_t *node = NULL;
svn_cache__t *cache;
const char *key;
SVN_ERR_ASSERT(*path == '/');
if (!root->is_txn_root)
{
/* immutable DAG node. use the global caches for it */
fs_fs_data_t *ffd = root->fs->fsap_data;
cache_entry_t *bucket;
auto_clear_dag_cache(ffd->dag_node_cache);
bucket = cache_lookup(ffd->dag_node_cache, root->rev, path);
if (bucket->node == NULL)
{
locate_cache(&cache, &key, root, path, pool);
- SVN_ERR(svn_cache__get((void **)&node, &found, cache, key,
- ffd->dag_node_cache->pool));
+ SVN_ERR(svn_cache__get((void **)&node, &found, cache, key, pool));
if (found && node)
{
/* Patch up the FS, since this might have come from an old FS
* object. */
svn_fs_fs__dag_set_fs(node, root->fs);
- bucket->node = node;
+
+ /* Retain the DAG node in L1 cache. */
+ bucket->node = svn_fs_fs__dag_dup(node,
+ ffd->dag_node_cache->pool);
}
}
else
{
- node = bucket->node;
+ /* Copy the node from L1 cache into the passed-in POOL. */
+ node = svn_fs_fs__dag_dup(bucket->node, pool);
}
-
- /* if we found a node, make sure it remains valid at least as long
- as it would when allocated in POOL. */
- if (node && needs_lock_cache)
- lock_cache(ffd->dag_node_cache, pool);
}
else
{
/* DAG is mutable / may become invalid. Use the TXN-local cache */
locate_cache(&cache, &key, root, path, pool);
SVN_ERR(svn_cache__get((void **) &node, &found, cache, key, pool));
if (found && node)
{
/* Patch up the FS, since this might have come from an old FS
* object. */
svn_fs_fs__dag_set_fs(node, root->fs);
}
}
*node_p = node;
return SVN_NO_ERROR;
}
/* Add the NODE for PATH to ROOT's node cache. */
static svn_error_t *
dag_node_cache_set(svn_fs_root_t *root,
const char *path,
dag_node_t *node,
apr_pool_t *pool)
{
svn_cache__t *cache;
const char *key;
SVN_ERR_ASSERT(*path == '/');
/* Do *not* attempt to dup and put the node into L1.
* dup() is twice as expensive as an L2 lookup (which will set also L1).
*/
locate_cache(&cache, &key, root, path, pool);
return svn_cache__set(cache, key, node, pool);
}
/* Baton for find_descendents_in_cache. */
struct fdic_baton {
const char *path;
apr_array_header_t *list;
apr_pool_t *pool;
};
/* If the given item is a descendent of BATON->PATH, push
* it onto BATON->LIST (copying into BATON->POOL). Implements
* the svn_iter_apr_hash_cb_t prototype. */
static svn_error_t *
find_descendents_in_cache(void *baton,
const void *key,
apr_ssize_t klen,
void *val,
apr_pool_t *pool)
{
struct fdic_baton *b = baton;
const char *item_path = key;
if (svn_fspath__skip_ancestor(b->path, item_path))
APR_ARRAY_PUSH(b->list, const char *) = apr_pstrdup(b->pool, item_path);
return SVN_NO_ERROR;
}
/* Invalidate cache entries for PATH and any of its children. This
should *only* be called on a transaction root! */
static svn_error_t *
dag_node_cache_invalidate(svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
struct fdic_baton b;
svn_cache__t *cache;
apr_pool_t *iterpool;
int i;
b.path = path;
b.pool = svn_pool_create(pool);
b.list = apr_array_make(b.pool, 1, sizeof(const char *));
SVN_ERR_ASSERT(root->is_txn_root);
locate_cache(&cache, NULL, root, NULL, b.pool);
SVN_ERR(svn_cache__iter(NULL, cache, find_descendents_in_cache,
&b, b.pool));
iterpool = svn_pool_create(b.pool);
for (i = 0; i < b.list->nelts; i++)
{
const char *descendent = APR_ARRAY_IDX(b.list, i, const char *);
svn_pool_clear(iterpool);
SVN_ERR(svn_cache__set(cache, descendent, NULL, iterpool));
}
svn_pool_destroy(iterpool);
svn_pool_destroy(b.pool);
return SVN_NO_ERROR;
}
/* Creating transaction and revision root nodes. */
svn_error_t *
svn_fs_fs__txn_root(svn_fs_root_t **root_p,
svn_fs_txn_t *txn,
apr_pool_t *pool)
{
apr_uint32_t flags = 0;
apr_hash_t *txnprops;
/* Look for the temporary txn props representing 'flags'. */
SVN_ERR(svn_fs_fs__txn_proplist(&txnprops, txn, pool));
if (txnprops)
{
if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD))
flags |= SVN_FS_TXN_CHECK_OOD;
if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS))
flags |= SVN_FS_TXN_CHECK_LOCKS;
}
return make_txn_root(root_p, txn->fs, txn->id, txn->base_rev, flags, pool);
}
svn_error_t *
svn_fs_fs__revision_root(svn_fs_root_t **root_p,
svn_fs_t *fs,
svn_revnum_t rev,
apr_pool_t *pool)
{
dag_node_t *root_dir;
SVN_ERR(svn_fs__check_fs(fs, TRUE));
SVN_ERR(svn_fs_fs__dag_revision_root(&root_dir, fs, rev, pool));
*root_p = make_revision_root(fs, rev, root_dir, pool);
return SVN_NO_ERROR;
}
/* Getting dag nodes for roots. */
/* Set *NODE_P to a freshly opened dag node referring to the root
directory of ROOT, allocating from POOL. */
static svn_error_t *
root_node(dag_node_t **node_p,
svn_fs_root_t *root,
apr_pool_t *pool)
{
if (root->is_txn_root)
{
/* It's a transaction root. Open a fresh copy. */
return svn_fs_fs__dag_txn_root(node_p, root->fs, root->txn, pool);
}
else
{
/* It's a revision root, so we already have its root directory
opened. */
fs_rev_root_data_t *frd = root->fsap_data;
*node_p = svn_fs_fs__dag_dup(frd->root_dir, pool);
return SVN_NO_ERROR;
}
}
/* Set *NODE_P to a mutable root directory for ROOT, cloning if
necessary, allocating in POOL. ROOT must be a transaction root.
Use ERROR_PATH in error messages. */
static svn_error_t *
mutable_root_node(dag_node_t **node_p,
svn_fs_root_t *root,
const char *error_path,
apr_pool_t *pool)
{
if (root->is_txn_root)
return svn_fs_fs__dag_clone_root(node_p, root->fs, root->txn, pool);
else
/* If it's not a transaction root, we can't change its contents. */
return SVN_FS__ERR_NOT_MUTABLE(root->fs, root->rev, error_path);
}
/* Traversing directory paths. */
typedef enum copy_id_inherit_t
{
copy_id_inherit_unknown = 0,
copy_id_inherit_self,
copy_id_inherit_parent,
copy_id_inherit_new
} copy_id_inherit_t;
/* A linked list representing the path from a node up to a root
directory. We use this for cloning, and for operations that need
to deal with both a node and its parent directory. For example, a
`delete' operation needs to know that the node actually exists, but
also needs to change the parent directory. */
typedef struct parent_path_t
{
/* A node along the path. This could be the final node, one of its
parents, or the root. Every parent path ends with an element for
the root directory. */
dag_node_t *node;
/* The name NODE has in its parent directory. This is zero for the
root directory, which (obviously) has no name in its parent. */
char *entry;
/* The parent of NODE, or zero if NODE is the root directory. */
struct parent_path_t *parent;
/* The copy ID inheritance style. */
copy_id_inherit_t copy_inherit;
/* If copy ID inheritance style is copy_id_inherit_new, this is the
path which should be implicitly copied; otherwise, this is NULL. */
const char *copy_src_path;
} parent_path_t;
/* Return a text string describing the absolute path of parent_path
PARENT_PATH. It will be allocated in POOL. */
static const char *
parent_path_path(parent_path_t *parent_path,
apr_pool_t *pool)
{
const char *path_so_far = "/";
if (parent_path->parent)
path_so_far = parent_path_path(parent_path->parent, pool);
return parent_path->entry
? svn_fspath__join(path_so_far, parent_path->entry, pool)
: path_so_far;
}
/* Return the FS path for the parent path chain object CHILD relative
to its ANCESTOR in the same chain, allocated in POOL. */
static const char *
parent_path_relpath(parent_path_t *child,
parent_path_t *ancestor,
apr_pool_t *pool)
{
const char *path_so_far = "";
parent_path_t *this_node = child;
while (this_node != ancestor)
{
assert(this_node != NULL);
path_so_far = svn_relpath_join(this_node->entry, path_so_far, pool);
this_node = this_node->parent;
}
return path_so_far;
}
/* Choose a copy ID inheritance method *INHERIT_P to be used in the
event that immutable node CHILD in FS needs to be made mutable. If
the inheritance method is copy_id_inherit_new, also return a
*COPY_SRC_PATH on which to base the new copy ID (else return NULL
for that path). CHILD must have a parent (it cannot be the root
node). TXN_ID is the transaction in which these items might be
mutable. Allocations are taken from POOL. */
static svn_error_t *
get_copy_inheritance(copy_id_inherit_t *inherit_p,
const char **copy_src_path,
svn_fs_t *fs,
parent_path_t *child,
const char *txn_id,
apr_pool_t *pool)
{
const svn_fs_id_t *child_id, *parent_id, *copyroot_id;
const char *child_copy_id, *parent_copy_id;
const char *id_path = NULL;
svn_fs_root_t *copyroot_root;
dag_node_t *copyroot_node;
svn_revnum_t copyroot_rev;
const char *copyroot_path;
SVN_ERR_ASSERT(child && child->parent && txn_id);
/* Initialize some convenience variables. */
child_id = svn_fs_fs__dag_get_id(child->node);
parent_id = svn_fs_fs__dag_get_id(child->parent->node);
child_copy_id = svn_fs_fs__id_copy_id(child_id);
parent_copy_id = svn_fs_fs__id_copy_id(parent_id);
/* If this child is already mutable, we have nothing to do. */
if (svn_fs_fs__id_txn_id(child_id))
{
*inherit_p = copy_id_inherit_self;
*copy_src_path = NULL;
return SVN_NO_ERROR;
}
/* From this point on, we'll assume that the child will just take
its copy ID from its parent. */
*inherit_p = copy_id_inherit_parent;
*copy_src_path = NULL;
/* Special case: if the child's copy ID is '0', use the parent's
copy ID. */
if (strcmp(child_copy_id, "0") == 0)
return SVN_NO_ERROR;
/* Compare the copy IDs of the child and its parent. If they are
the same, then the child is already on the same branch as the
parent, and should use the same mutability copy ID that the
parent will use. */
if (svn_fs_fs__key_compare(child_copy_id, parent_copy_id) == 0)
return SVN_NO_ERROR;
/* If the child is on the same branch that the parent is on, the
child should just use the same copy ID that the parent would use.
Else, the child needs to generate a new copy ID to use should it
need to be made mutable. We will claim that child is on the same
branch as its parent if the child itself is not a branch point,
or if it is a branch point that we are accessing via its original
copy destination path. */
SVN_ERR(svn_fs_fs__dag_get_copyroot(&copyroot_rev, &copyroot_path,
child->node));
SVN_ERR(svn_fs_fs__revision_root(&copyroot_root, fs, copyroot_rev, pool));
- SVN_ERR(get_dag(&copyroot_node, copyroot_root, copyroot_path, FALSE, pool));
+ SVN_ERR(get_dag(&copyroot_node, copyroot_root, copyroot_path, pool));
copyroot_id = svn_fs_fs__dag_get_id(copyroot_node);
if (svn_fs_fs__id_compare(copyroot_id, child_id) == -1)
return SVN_NO_ERROR;
/* Determine if we are looking at the child via its original path or
as a subtree item of a copied tree. */
id_path = svn_fs_fs__dag_get_created_path(child->node);
if (strcmp(id_path, parent_path_path(child, pool)) == 0)
{
*inherit_p = copy_id_inherit_self;
return SVN_NO_ERROR;
}
/* We are pretty sure that the child node is an unedited nested
branched node. When it needs to be made mutable, it should claim
a new copy ID. */
*inherit_p = copy_id_inherit_new;
*copy_src_path = id_path;
return SVN_NO_ERROR;
}
/* Allocate a new parent_path_t node from POOL, referring to NODE,
ENTRY, PARENT, and COPY_ID. */
static parent_path_t *
make_parent_path(dag_node_t *node,
char *entry,
parent_path_t *parent,
apr_pool_t *pool)
{
parent_path_t *parent_path = apr_pcalloc(pool, sizeof(*parent_path));
parent_path->node = node;
parent_path->entry = entry;
parent_path->parent = parent;
parent_path->copy_inherit = copy_id_inherit_unknown;
parent_path->copy_src_path = NULL;
return parent_path;
}
/* Flags for open_path. */
typedef enum open_path_flags_t {
/* The last component of the PATH need not exist. (All parent
directories must exist, as usual.) If the last component doesn't
exist, simply leave the `node' member of the bottom parent_path
component zero. */
open_path_last_optional = 1,
/* When this flag is set, don't bother to lookup the DAG node in
our caches because we already tried this. Ignoring this flag
has no functional impact. */
open_path_uncached = 2,
/* The caller does not care about the parent node chain but only
the final DAG node. */
open_path_node_only = 4
} open_path_flags_t;
/* Open the node identified by PATH in ROOT, allocating in POOL. Set
*PARENT_PATH_P to a path from the node up to ROOT. The resulting
**PARENT_PATH_P value is guaranteed to contain at least one
*element, for the root directory. PATH must be in canonical form.
If resulting *PARENT_PATH_P will eventually be made mutable and
modified, or if copy ID inheritance information is otherwise
needed, TXN_ID should be the ID of the mutability transaction. If
TXN_ID is NULL, no copy ID inheritance information will be
calculated for the *PARENT_PATH_P chain.
If FLAGS & open_path_last_optional is zero, return the error
SVN_ERR_FS_NOT_FOUND if the node PATH refers to does not exist. If
non-zero, require all the parent directories to exist as normal,
but if the final path component doesn't exist, simply return a path
whose bottom `node' member is zero. This option is useful for
callers that create new nodes --- we find the parent directory for
them, and tell them whether the entry exists already.
The remaining bits in FLAGS are hints that allow this function
to take shortcuts based on knowledge that the caller provides,
such as the caller is not actually being interested in PARENT_PATH_P,
but only in (*PARENT_PATH_P)->NODE.
NOTE: Public interfaces which only *read* from the filesystem
should not call this function directly, but should instead use
get_dag().
*/
static svn_error_t *
open_path(parent_path_t **parent_path_p,
svn_fs_root_t *root,
const char *path,
int flags,
const char *txn_id,
apr_pool_t *pool)
{
svn_fs_t *fs = root->fs;
dag_node_t *here = NULL; /* The directory we're currently looking at. */
parent_path_t *parent_path; /* The path from HERE up to the root. */
const char *rest; /* The portion of PATH we haven't traversed yet. */
/* ensure a canonical path representation */
const char *path_so_far = "/";
apr_pool_t *iterpool = svn_pool_create(pool);
/* callers often traverse the tree in some path-based order. That means
a sibling of PATH has been presently accessed. Try to start the lookup
directly at the parent node, if the caller did not requested the full
parent chain. */
const char *directory;
assert(svn_fs__is_canonical_abspath(path));
if (flags & open_path_node_only)
{
directory = svn_dirent_dirname(path, pool);
if (directory[1] != 0) /* root nodes are covered anyway */
- SVN_ERR(dag_node_cache_get(&here, root, directory, TRUE, pool));
+ SVN_ERR(dag_node_cache_get(&here, root, directory, pool));
}
/* did the shortcut work? */
if (here)
{
path_so_far = directory;
rest = path + strlen(directory) + 1;
}
else
{
/* Make a parent_path item for the root node, using its own current
copy id. */
SVN_ERR(root_node(&here, root, pool));
rest = path + 1; /* skip the leading '/', it saves in iteration */
}
parent_path = make_parent_path(here, 0, 0, pool);
parent_path->copy_inherit = copy_id_inherit_self;
/* Whenever we are at the top of this loop:
- HERE is our current directory,
- ID is the node revision ID of HERE,
- REST is the path we're going to find in HERE, and
- PARENT_PATH includes HERE and all its parents. */
for (;;)
{
const char *next;
char *entry;
dag_node_t *child;
svn_pool_clear(iterpool);
/* Parse out the next entry from the path. */
entry = svn_fs__next_entry_name(&next, rest, pool);
/* Calculate the path traversed thus far. */
path_so_far = svn_fspath__join(path_so_far, entry, pool);
if (*entry == '\0')
{
/* Given the behavior of svn_fs__next_entry_name(), this
happens when the path either starts or ends with a slash.
In either case, we stay put: the current directory stays
the same, and we add nothing to the parent path. */
child = here;
}
else
{
copy_id_inherit_t inherit;
const char *copy_path = NULL;
svn_error_t *err = SVN_NO_ERROR;
dag_node_t *cached_node = NULL;
/* If we found a directory entry, follow it. First, we
check our node cache, and, failing that, we hit the DAG
layer. Don't bother to contact the cache for the last
element if we already know the lookup to fail for the
complete path. */
if (next || !(flags & open_path_uncached))
- SVN_ERR(dag_node_cache_get(&cached_node, root, path_so_far,
- TRUE, pool));
+ SVN_ERR(dag_node_cache_get(&cached_node, root, path_so_far, pool));
+
if (cached_node)
child = cached_node;
else
err = svn_fs_fs__dag_open(&child, here, entry, pool, iterpool);
/* "file not found" requires special handling. */
if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
{
/* If this was the last path component, and the caller
said it was optional, then don't return an error;
just put a NULL node pointer in the path. */
svn_error_clear(err);
if ((flags & open_path_last_optional)
&& (! next || *next == '\0'))
{
parent_path = make_parent_path(NULL, entry, parent_path,
pool);
break;
}
else
{
/* Build a better error message than svn_fs_fs__dag_open
can provide, giving the root and full path name. */
return SVN_FS__NOT_FOUND(root, path);
}
}
/* Other errors we return normally. */
SVN_ERR(err);
if (flags & open_path_node_only)
{
/* Shortcut: the caller only wan'ts the final DAG node. */
parent_path->node = child;
}
else
{
/* Now, make a parent_path item for CHILD. */
parent_path = make_parent_path(child, entry, parent_path, pool);
if (txn_id)
{
SVN_ERR(get_copy_inheritance(&inherit, &copy_path, fs,
parent_path, txn_id, iterpool));
parent_path->copy_inherit = inherit;
parent_path->copy_src_path = apr_pstrdup(pool, copy_path);
}
}
/* Cache the node we found (if it wasn't already cached). */
if (! cached_node)
SVN_ERR(dag_node_cache_set(root, path_so_far, child, iterpool));
}
/* Are we finished traversing the path? */
if (! next)
break;
/* The path isn't finished yet; we'd better be in a directory. */
if (svn_fs_fs__dag_node_kind(child) != svn_node_dir)
SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far),
apr_psprintf(iterpool, _("Failure opening '%s'"), path));
rest = next;
here = child;
}
svn_pool_destroy(iterpool);
*parent_path_p = parent_path;
return SVN_NO_ERROR;
}
/* Make the node referred to by PARENT_PATH mutable, if it isn't
already, allocating from POOL. ROOT must be the root from which
PARENT_PATH descends. Clone any parent directories as needed.
Adjust the dag nodes in PARENT_PATH to refer to the clones. Use
ERROR_PATH in error messages. */
static svn_error_t *
make_path_mutable(svn_fs_root_t *root,
parent_path_t *parent_path,
const char *error_path,
apr_pool_t *pool)
{
dag_node_t *clone;
const char *txn_id = root->txn;
/* Is the node mutable already? */
if (svn_fs_fs__dag_check_mutable(parent_path->node))
return SVN_NO_ERROR;
/* Are we trying to clone the root, or somebody's child node? */
if (parent_path->parent)
{
const svn_fs_id_t *parent_id, *child_id, *copyroot_id;
const char *copy_id = NULL;
copy_id_inherit_t inherit = parent_path->copy_inherit;
const char *clone_path, *copyroot_path;
svn_revnum_t copyroot_rev;
svn_boolean_t is_parent_copyroot = FALSE;
svn_fs_root_t *copyroot_root;
dag_node_t *copyroot_node;
/* We're trying to clone somebody's child. Make sure our parent
is mutable. */
SVN_ERR(make_path_mutable(root, parent_path->parent,
error_path, pool));
switch (inherit)
{
case copy_id_inherit_parent:
parent_id = svn_fs_fs__dag_get_id(parent_path->parent->node);
copy_id = svn_fs_fs__id_copy_id(parent_id);
break;
case copy_id_inherit_new:
SVN_ERR(svn_fs_fs__reserve_copy_id(&copy_id, root->fs, txn_id,
pool));
break;
case copy_id_inherit_self:
copy_id = NULL;
break;
case copy_id_inherit_unknown:
default:
SVN_ERR_MALFUNCTION(); /* uh-oh -- somebody didn't calculate copy-ID
inheritance data. */
}
/* Determine what copyroot our new child node should use. */
SVN_ERR(svn_fs_fs__dag_get_copyroot(&copyroot_rev, &copyroot_path,
parent_path->node));
SVN_ERR(svn_fs_fs__revision_root(&copyroot_root, root->fs,
copyroot_rev, pool));
- SVN_ERR(get_dag(&copyroot_node, copyroot_root, copyroot_path,
- FALSE, pool));
+ SVN_ERR(get_dag(&copyroot_node, copyroot_root, copyroot_path, pool));
child_id = svn_fs_fs__dag_get_id(parent_path->node);
copyroot_id = svn_fs_fs__dag_get_id(copyroot_node);
if (strcmp(svn_fs_fs__id_node_id(child_id),
svn_fs_fs__id_node_id(copyroot_id)) != 0)
is_parent_copyroot = TRUE;
/* Now make this node mutable. */
clone_path = parent_path_path(parent_path->parent, pool);
SVN_ERR(svn_fs_fs__dag_clone_child(&clone,
parent_path->parent->node,
clone_path,
parent_path->entry,
copy_id, txn_id,
is_parent_copyroot,
pool));
/* Update the path cache. */
SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool),
clone, pool));
}
else
{
/* We're trying to clone the root directory. */
SVN_ERR(mutable_root_node(&clone, root, error_path, pool));
}
/* Update the PARENT_PATH link to refer to the clone. */
parent_path->node = clone;
return SVN_NO_ERROR;
}
/* Open the node identified by PATH in ROOT. Set DAG_NODE_P to the
node we find, allocated in POOL. Return the error
- SVN_ERR_FS_NOT_FOUND if this node doesn't exist.
-
- Since locking can be expensive and POOL may be long-living, for
- nodes that will not need to survive the next call to this function,
- set NEEDS_LOCK_CACHE to FALSE. */
+ SVN_ERR_FS_NOT_FOUND if this node doesn't exist. */
static svn_error_t *
get_dag(dag_node_t **dag_node_p,
svn_fs_root_t *root,
const char *path,
- svn_boolean_t needs_lock_cache,
apr_pool_t *pool)
{
parent_path_t *parent_path;
dag_node_t *node = NULL;
/* First we look for the DAG in our cache
(if the path may be canonical). */
if (*path == '/')
- SVN_ERR(dag_node_cache_get(&node, root, path, needs_lock_cache, pool));
+ SVN_ERR(dag_node_cache_get(&node, root, path, pool));
if (! node)
{
/* Canonicalize the input PATH. */
if (! svn_fs__is_canonical_abspath(path))
{
path = svn_fs__canonicalize_abspath(path, pool);
/* Try again with the corrected path. */
- SVN_ERR(dag_node_cache_get(&node, root, path, needs_lock_cache,
- pool));
+ SVN_ERR(dag_node_cache_get(&node, root, path, pool));
}
if (! node)
{
/* Call open_path with no flags, as we want this to return an
* error if the node for which we are searching doesn't exist. */
SVN_ERR(open_path(&parent_path, root, path,
open_path_uncached | open_path_node_only,
NULL, pool));
node = parent_path->node;
/* No need to cache our find -- open_path() will do that for us. */
}
}
*dag_node_p = node;
return SVN_NO_ERROR;
}
/* Populating the `changes' table. */
/* Add a change to the changes table in FS, keyed on transaction id
TXN_ID, and indicated that a change of kind CHANGE_KIND occurred on
PATH (whose node revision id is--or was, in the case of a
deletion--NODEREV_ID), and optionally that TEXT_MODs or PROP_MODs
occurred. If the change resulted from a copy, COPYFROM_REV and
COPYFROM_PATH specify under which revision and path the node was
copied from. If this was not part of a copy, COPYFROM_REV should
be SVN_INVALID_REVNUM. Do all this as part of POOL. */
static svn_error_t *
add_change(svn_fs_t *fs,
const char *txn_id,
const char *path,
const svn_fs_id_t *noderev_id,
svn_fs_path_change_kind_t change_kind,
svn_boolean_t text_mod,
svn_boolean_t prop_mod,
svn_node_kind_t node_kind,
svn_revnum_t copyfrom_rev,
const char *copyfrom_path,
apr_pool_t *pool)
{
return svn_fs_fs__add_change(fs, txn_id,
svn_fs__canonicalize_abspath(path, pool),
noderev_id, change_kind, text_mod, prop_mod,
node_kind, copyfrom_rev, copyfrom_path,
pool);
}
/* Generic node operations. */
/* Get the id of a node referenced by path PATH in ROOT. Return the
id in *ID_P allocated in POOL. */
svn_error_t *
svn_fs_fs__node_id(const svn_fs_id_t **id_p,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
if ((! root->is_txn_root)
&& (path[0] == '\0' || ((path[0] == '/') && (path[1] == '\0'))))
{
/* Optimize the case where we don't need any db access at all.
The root directory ("" or "/") node is stored in the
svn_fs_root_t object, and never changes when it's a revision
root, so we can just reach in and grab it directly. */
fs_rev_root_data_t *frd = root->fsap_data;
*id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(frd->root_dir), pool);
}
else
{
dag_node_t *node;
- SVN_ERR(get_dag(&node, root, path, FALSE, pool));
+ SVN_ERR(get_dag(&node, root, path, pool));
*id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(node), pool);
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__node_created_rev(svn_revnum_t *revision,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
dag_node_t *node;
- SVN_ERR(get_dag(&node, root, path, FALSE, pool));
+ SVN_ERR(get_dag(&node, root, path, pool));
return svn_fs_fs__dag_get_revision(revision, node, pool);
}
/* Set *CREATED_PATH to the path at which PATH under ROOT was created.
Return a string allocated in POOL. */
static svn_error_t *
fs_node_created_path(const char **created_path,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
dag_node_t *node;
- SVN_ERR(get_dag(&node, root, path, TRUE, pool));
+ SVN_ERR(get_dag(&node, root, path, pool));
*created_path = svn_fs_fs__dag_get_created_path(node);
return SVN_NO_ERROR;
}
/* Set *KIND_P to the type of node located at PATH under ROOT.
Perform temporary allocations in POOL. */
static svn_error_t *
node_kind(svn_node_kind_t *kind_p,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
const svn_fs_id_t *node_id;
dag_node_t *node;
/* Get the node id. */
SVN_ERR(svn_fs_fs__node_id(&node_id, root, path, pool));
/* Use the node id to get the real kind. */
SVN_ERR(svn_fs_fs__dag_get_node(&node, root->fs, node_id, pool));
*kind_p = svn_fs_fs__dag_node_kind(node);
return SVN_NO_ERROR;
}
/* Set *KIND_P to the type of node present at PATH under ROOT. If
PATH does not exist under ROOT, set *KIND_P to svn_node_none. Use
POOL for temporary allocation. */
svn_error_t *
svn_fs_fs__check_path(svn_node_kind_t *kind_p,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
svn_error_t *err = node_kind(kind_p, root, path, pool);
if (err &&
((err->apr_err == SVN_ERR_FS_NOT_FOUND)
|| (err->apr_err == SVN_ERR_FS_NOT_DIRECTORY)))
{
svn_error_clear(err);
err = SVN_NO_ERROR;
*kind_p = svn_node_none;
}
return svn_error_trace(err);
}
/* Set *VALUE_P to the value of the property named PROPNAME of PATH in
ROOT. If the node has no property by that name, set *VALUE_P to
zero. Allocate the result in POOL. */
static svn_error_t *
fs_node_prop(svn_string_t **value_p,
svn_fs_root_t *root,
const char *path,
const char *propname,
apr_pool_t *pool)
{
dag_node_t *node;
apr_hash_t *proplist;
- SVN_ERR(get_dag(&node, root, path, FALSE, pool));
+ SVN_ERR(get_dag(&node, root, path, pool));
SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, node, pool));
*value_p = NULL;
if (proplist)
*value_p = svn_hash_gets(proplist, propname);
return SVN_NO_ERROR;
}
/* Set *TABLE_P to the entire property list of PATH under ROOT, as an
APR hash table allocated in POOL. The resulting property table
maps property names to pointers to svn_string_t objects containing
the property value. */
static svn_error_t *
fs_node_proplist(apr_hash_t **table_p,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
apr_hash_t *table;
dag_node_t *node;
- SVN_ERR(get_dag(&node, root, path, FALSE, pool));
+ SVN_ERR(get_dag(&node, root, path, pool));
SVN_ERR(svn_fs_fs__dag_get_proplist(&table, node, pool));
*table_p = table ? table : apr_hash_make(pool);
return SVN_NO_ERROR;
}
static svn_error_t *
increment_mergeinfo_up_tree(parent_path_t *pp,
apr_int64_t increment,
apr_pool_t *pool)
{
for (; pp; pp = pp->parent)
SVN_ERR(svn_fs_fs__dag_increment_mergeinfo_count(pp->node,
increment,
pool));
return SVN_NO_ERROR;
}
/* Change, add, or delete a node's property value. The affected node
is PATH under ROOT, the property value to modify is NAME, and VALUE
points to either a string value to set the new contents to, or NULL
if the property should be deleted. Perform temporary allocations
in POOL. */
static svn_error_t *
fs_change_node_prop(svn_fs_root_t *root,
const char *path,
const char *name,
const svn_string_t *value,
apr_pool_t *pool)
{
parent_path_t *parent_path;
apr_hash_t *proplist;
const char *txn_id;
if (! root->is_txn_root)
return SVN_FS__NOT_TXN(root);
txn_id = root->txn;
path = svn_fs__canonicalize_abspath(path, pool);
SVN_ERR(open_path(&parent_path, root, path, 0, txn_id, pool));
/* Check (non-recursively) to see if path is locked; if so, check
that we can use it. */
if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, FALSE, FALSE,
pool));
SVN_ERR(make_path_mutable(root, parent_path, path, pool));
SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, parent_path->node, pool));
/* If there's no proplist, but we're just deleting a property, exit now. */
if ((! proplist) && (! value))
return SVN_NO_ERROR;
/* Now, if there's no proplist, we know we need to make one. */
if (! proplist)
proplist = apr_hash_make(pool);
if (svn_fs_fs__fs_supports_mergeinfo(root->fs)
&& strcmp (name, SVN_PROP_MERGEINFO) == 0)
{
apr_int64_t increment = 0;
svn_boolean_t had_mergeinfo;
SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&had_mergeinfo, parent_path->node));
if (value && !had_mergeinfo)
increment = 1;
else if (!value && had_mergeinfo)
increment = -1;
if (increment != 0)
{
SVN_ERR(increment_mergeinfo_up_tree(parent_path, increment, pool));
SVN_ERR(svn_fs_fs__dag_set_has_mergeinfo(parent_path->node,
(value != NULL), pool));
}
}
/* Set the property. */
svn_hash_sets(proplist, name, value);
/* Overwrite the node's proplist. */
SVN_ERR(svn_fs_fs__dag_set_proplist(parent_path->node, proplist,
pool));
/* Make a record of this modification in the changes table. */
return add_change(root->fs, txn_id, path,
svn_fs_fs__dag_get_id(parent_path->node),
svn_fs_path_change_modify, FALSE, TRUE,
svn_fs_fs__dag_node_kind(parent_path->node),
SVN_INVALID_REVNUM, NULL, pool);
}
/* Determine if the properties of two path/root combinations are
different. Set *CHANGED_P to TRUE if the properties at PATH1 under
ROOT1 differ from those at PATH2 under ROOT2, or FALSE otherwise.
Both roots must be in the same filesystem. */
static svn_error_t *
fs_props_changed(svn_boolean_t *changed_p,
svn_fs_root_t *root1,
const char *path1,
svn_fs_root_t *root2,
const char *path2,
apr_pool_t *pool)
{
dag_node_t *node1, *node2;
/* Check that roots are in the same fs. */
if (root1->fs != root2->fs)
return svn_error_create
(SVN_ERR_FS_GENERAL, NULL,
_("Cannot compare property value between two different filesystems"));
- SVN_ERR(get_dag(&node1, root1, path1, TRUE, pool));
- SVN_ERR(get_dag(&node2, root2, path2, TRUE, pool));
+ SVN_ERR(get_dag(&node1, root1, path1, pool));
+ SVN_ERR(get_dag(&node2, root2, path2, pool));
return svn_fs_fs__dag_things_different(changed_p, NULL,
node1, node2);
}
/* Merges and commits. */
/* Set *NODE to the root node of ROOT. */
static svn_error_t *
get_root(dag_node_t **node, svn_fs_root_t *root, apr_pool_t *pool)
{
- return get_dag(node, root, "/", TRUE, pool);
+ return get_dag(node, root, "/", pool);
}
/* Set the contents of CONFLICT_PATH to PATH, and return an
SVN_ERR_FS_CONFLICT error that indicates that there was a conflict
at PATH. Perform all allocations in POOL (except the allocation of
CONFLICT_PATH, which should be handled outside this function). */
static svn_error_t *
conflict_err(svn_stringbuf_t *conflict_path,
const char *path)
{
svn_stringbuf_set(conflict_path, path);
return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL,
_("Conflict at '%s'"), path);
}
/* Merge changes between ANCESTOR and SOURCE into TARGET. ANCESTOR
* and TARGET must be distinct node revisions. TARGET_PATH should
* correspond to TARGET's full path in its filesystem, and is used for
* reporting conflict location.
*
* SOURCE, TARGET, and ANCESTOR are generally directories; this
* function recursively merges the directories' contents. If any are
* files, this function simply returns an error whenever SOURCE,
* TARGET, and ANCESTOR are all distinct node revisions.
*
* If there are differences between ANCESTOR and SOURCE that conflict
* with changes between ANCESTOR and TARGET, this function returns an
* SVN_ERR_FS_CONFLICT error, and updates CONFLICT_P to the name of the
* conflicting node in TARGET, with TARGET_PATH prepended as a path.
*
* If there are no conflicting differences, CONFLICT_P is updated to
* the empty string.
*
* CONFLICT_P must point to a valid svn_stringbuf_t.
*
* Do any necessary temporary allocation in POOL.
*/
static svn_error_t *
merge(svn_stringbuf_t *conflict_p,
const char *target_path,
dag_node_t *target,
dag_node_t *source,
dag_node_t *ancestor,
const char *txn_id,
apr_int64_t *mergeinfo_increment_out,
apr_pool_t *pool)
{
const svn_fs_id_t *source_id, *target_id, *ancestor_id;
apr_hash_t *s_entries, *t_entries, *a_entries;
apr_hash_index_t *hi;
svn_fs_t *fs;
apr_pool_t *iterpool;
apr_int64_t mergeinfo_increment = 0;
svn_boolean_t fs_supports_mergeinfo;
/* Make sure everyone comes from the same filesystem. */
fs = svn_fs_fs__dag_get_fs(ancestor);
if ((fs != svn_fs_fs__dag_get_fs(source))
|| (fs != svn_fs_fs__dag_get_fs(target)))
{
return svn_error_create
(SVN_ERR_FS_CORRUPT, NULL,
_("Bad merge; ancestor, source, and target not all in same fs"));
}
/* We have the same fs, now check it. */
SVN_ERR(svn_fs__check_fs(fs, TRUE));
source_id = svn_fs_fs__dag_get_id(source);
target_id = svn_fs_fs__dag_get_id(target);
ancestor_id = svn_fs_fs__dag_get_id(ancestor);
/* It's improper to call this function with ancestor == target. */
if (svn_fs_fs__id_eq(ancestor_id, target_id))
{
svn_string_t *id_str = svn_fs_fs__id_unparse(target_id, pool);
return svn_error_createf
(SVN_ERR_FS_GENERAL, NULL,
_("Bad merge; target '%s' has id '%s', same as ancestor"),
target_path, id_str->data);
}
svn_stringbuf_setempty(conflict_p);
/* Base cases:
* Either no change made in source, or same change as made in target.
* Both mean nothing to merge here.
*/
if (svn_fs_fs__id_eq(ancestor_id, source_id)
|| (svn_fs_fs__id_eq(source_id, target_id)))
return SVN_NO_ERROR;
/* Else proceed, knowing all three are distinct node revisions.
*
* How to merge from this point:
*
* if (not all 3 are directories)
* {
* early exit with conflict;
* }
*
* // Property changes may only be made to up-to-date
* // directories, because once the client commits the prop
* // change, it bumps the directory's revision, and therefore
* // must be able to depend on there being no other changes to
* // that directory in the repository.
* if (target's property list differs from ancestor's)
* conflict;
*
* For each entry NAME in the directory ANCESTOR:
*
* Let ANCESTOR-ENTRY, SOURCE-ENTRY, and TARGET-ENTRY be the IDs of
* the name within ANCESTOR, SOURCE, and TARGET respectively.
* (Possibly null if NAME does not exist in SOURCE or TARGET.)
*
* If ANCESTOR-ENTRY == SOURCE-ENTRY, then:
* No changes were made to this entry while the transaction was in
* progress, so do nothing to the target.
*
* Else if ANCESTOR-ENTRY == TARGET-ENTRY, then:
* A change was made to this entry while the transaction was in
* process, but the transaction did not touch this entry. Replace
* TARGET-ENTRY with SOURCE-ENTRY.
*
* Else:
* Changes were made to this entry both within the transaction and
* to the repository while the transaction was in progress. They
* must be merged or declared to be in conflict.
*
* If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
* double delete; flag a conflict.
*
* If any of the three entries is of type file, declare a conflict.
*
* If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
* modification of ANCESTOR-ENTRY (determine by comparing the
* node-id fields), declare a conflict. A replacement is
* incompatible with a modification or other replacement--even
* an identical replacement.
*
* Direct modifications were made to the directory ANCESTOR-ENTRY
* in both SOURCE and TARGET. Recursively merge these
* modifications.
*
* For each leftover entry NAME in the directory SOURCE:
*
* If NAME exists in TARGET, declare a conflict. Even if SOURCE and
* TARGET are adding exactly the same thing, two additions are not
* auto-mergeable with each other.
*
* Add NAME to TARGET with the entry from SOURCE.
*
* Now that we are done merging the changes from SOURCE into the
* directory TARGET, update TARGET's predecessor to be SOURCE.
*/
if ((svn_fs_fs__dag_node_kind(source) != svn_node_dir)
|| (svn_fs_fs__dag_node_kind(target) != svn_node_dir)
|| (svn_fs_fs__dag_node_kind(ancestor) != svn_node_dir))
{
return conflict_err(conflict_p, target_path);
}
/* Possible early merge failure: if target and ancestor have
different property lists, then the merge should fail.
Propchanges can *only* be committed on an up-to-date directory.
### TODO: see issue #418 about the inelegance of this.
Another possible, similar, early merge failure: if source and
ancestor have different property lists (meaning someone else
changed directory properties while our commit transaction was
happening), the merge should fail. See issue #2751.
*/
{
node_revision_t *tgt_nr, *anc_nr, *src_nr;
/* Get node revisions for our id's. */
SVN_ERR(svn_fs_fs__get_node_revision(&tgt_nr, fs, target_id, pool));
SVN_ERR(svn_fs_fs__get_node_revision(&anc_nr, fs, ancestor_id, pool));
SVN_ERR(svn_fs_fs__get_node_revision(&src_nr, fs, source_id, pool));
/* Now compare the prop-keys of the skels. Note that just because
the keys are different -doesn't- mean the proplists have
different contents. But merge() isn't concerned with contents;
it doesn't do a brute-force comparison on textual contents, so
it won't do that here either. Checking to see if the propkey
atoms are `equal' is enough. */
if (! svn_fs_fs__noderev_same_rep_key(tgt_nr->prop_rep, anc_nr->prop_rep))
return conflict_err(conflict_p, target_path);
if (! svn_fs_fs__noderev_same_rep_key(src_nr->prop_rep, anc_nr->prop_rep))
return conflict_err(conflict_p, target_path);
}
/* ### todo: it would be more efficient to simply check for a NULL
entries hash where necessary below than to allocate an empty hash
here, but another day, another day... */
SVN_ERR(svn_fs_fs__dag_dir_entries(&s_entries, source, pool));
SVN_ERR(svn_fs_fs__dag_dir_entries(&t_entries, target, pool));
SVN_ERR(svn_fs_fs__dag_dir_entries(&a_entries, ancestor, pool));
fs_supports_mergeinfo = svn_fs_fs__fs_supports_mergeinfo(fs);
/* for each entry E in a_entries... */
iterpool = svn_pool_create(pool);
for (hi = apr_hash_first(pool, a_entries);
hi;
hi = apr_hash_next(hi))
{
svn_fs_dirent_t *s_entry, *t_entry, *a_entry;
const char *name;
apr_ssize_t klen;
svn_pool_clear(iterpool);
name = svn__apr_hash_index_key(hi);
klen = svn__apr_hash_index_klen(hi);
a_entry = svn__apr_hash_index_val(hi);
s_entry = apr_hash_get(s_entries, name, klen);
t_entry = apr_hash_get(t_entries, name, klen);
/* No changes were made to this entry while the transaction was
in progress, so do nothing to the target. */
if (s_entry && svn_fs_fs__id_eq(a_entry->id, s_entry->id))
goto end;
/* A change was made to this entry while the transaction was in
process, but the transaction did not touch this entry. */
else if (t_entry && svn_fs_fs__id_eq(a_entry->id, t_entry->id))
{
dag_node_t *t_ent_node;
SVN_ERR(svn_fs_fs__dag_get_node(&t_ent_node, fs,
t_entry->id, iterpool));
if (fs_supports_mergeinfo)
{
apr_int64_t mergeinfo_start;
SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_start,
t_ent_node));
mergeinfo_increment -= mergeinfo_start;
}
if (s_entry)
{
dag_node_t *s_ent_node;
SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
s_entry->id, iterpool));
if (fs_supports_mergeinfo)
{
apr_int64_t mergeinfo_end;
SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_end,
s_ent_node));
mergeinfo_increment += mergeinfo_end;
}
SVN_ERR(svn_fs_fs__dag_set_entry(target, name,
s_entry->id,
s_entry->kind,
txn_id,
iterpool));
}
else
{
SVN_ERR(svn_fs_fs__dag_delete(target, name, txn_id, iterpool));
}
}
/* Changes were made to this entry both within the transaction
and to the repository while the transaction was in progress.
They must be merged or declared to be in conflict. */
else
{
dag_node_t *s_ent_node, *t_ent_node, *a_ent_node;
const char *new_tpath;
apr_int64_t sub_mergeinfo_increment;
/* If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
double delete; if one of them is null, that's a delete versus
a modification. In any of these cases, flag a conflict. */
if (s_entry == NULL || t_entry == NULL)
return conflict_err(conflict_p,
svn_fspath__join(target_path,
a_entry->name,
iterpool));
/* If any of the three entries is of type file, flag a conflict. */
if (s_entry->kind == svn_node_file
|| t_entry->kind == svn_node_file
|| a_entry->kind == svn_node_file)
return conflict_err(conflict_p,
svn_fspath__join(target_path,
a_entry->name,
iterpool));
/* If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
modification of ANCESTOR-ENTRY, declare a conflict. */
if (strcmp(svn_fs_fs__id_node_id(s_entry->id),
svn_fs_fs__id_node_id(a_entry->id)) != 0
|| strcmp(svn_fs_fs__id_copy_id(s_entry->id),
svn_fs_fs__id_copy_id(a_entry->id)) != 0
|| strcmp(svn_fs_fs__id_node_id(t_entry->id),
svn_fs_fs__id_node_id(a_entry->id)) != 0
|| strcmp(svn_fs_fs__id_copy_id(t_entry->id),
svn_fs_fs__id_copy_id(a_entry->id)) != 0)
return conflict_err(conflict_p,
svn_fspath__join(target_path,
a_entry->name,
iterpool));
/* Direct modifications were made to the directory
ANCESTOR-ENTRY in both SOURCE and TARGET. Recursively
merge these modifications. */
SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
s_entry->id, iterpool));
SVN_ERR(svn_fs_fs__dag_get_node(&t_ent_node, fs,
t_entry->id, iterpool));
SVN_ERR(svn_fs_fs__dag_get_node(&a_ent_node, fs,
a_entry->id, iterpool));
new_tpath = svn_fspath__join(target_path, t_entry->name, iterpool);
SVN_ERR(merge(conflict_p, new_tpath,
t_ent_node, s_ent_node, a_ent_node,
txn_id,
&sub_mergeinfo_increment,
iterpool));
if (fs_supports_mergeinfo)
mergeinfo_increment += sub_mergeinfo_increment;
}
/* We've taken care of any possible implications E could have.
Remove it from source_entries, so it's easy later to loop
over all the source entries that didn't exist in
ancestor_entries. */
end:
apr_hash_set(s_entries, name, klen, NULL);
}
/* For each entry E in source but not in ancestor */
for (hi = apr_hash_first(pool, s_entries);
hi;
hi = apr_hash_next(hi))
{
svn_fs_dirent_t *s_entry, *t_entry;
const char *name = svn__apr_hash_index_key(hi);
apr_ssize_t klen = svn__apr_hash_index_klen(hi);
dag_node_t *s_ent_node;
svn_pool_clear(iterpool);
s_entry = svn__apr_hash_index_val(hi);
t_entry = apr_hash_get(t_entries, name, klen);
/* If NAME exists in TARGET, declare a conflict. */
if (t_entry)
return conflict_err(conflict_p,
svn_fspath__join(target_path,
t_entry->name,
iterpool));
SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
s_entry->id, iterpool));
if (fs_supports_mergeinfo)
{
apr_int64_t mergeinfo_s;
SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_s,
s_ent_node));
mergeinfo_increment += mergeinfo_s;
}
SVN_ERR(svn_fs_fs__dag_set_entry
(target, s_entry->name, s_entry->id, s_entry->kind,
txn_id, iterpool));
}
svn_pool_destroy(iterpool);
SVN_ERR(svn_fs_fs__dag_update_ancestry(target, source, pool));
if (fs_supports_mergeinfo)
SVN_ERR(svn_fs_fs__dag_increment_mergeinfo_count(target,
mergeinfo_increment,
pool));
if (mergeinfo_increment_out)
*mergeinfo_increment_out = mergeinfo_increment;
return SVN_NO_ERROR;
}
/* Merge changes between an ancestor and SOURCE_NODE into
TXN. The ancestor is either ANCESTOR_NODE, or if
that is null, TXN's base node.
If the merge is successful, TXN's base will become
SOURCE_NODE, and its root node will have a new ID, a
successor of SOURCE_NODE.
If a conflict results, update *CONFLICT to the path in the txn that
conflicted; see the CONFLICT_P parameter of merge() for details. */
static svn_error_t *
merge_changes(dag_node_t *ancestor_node,
dag_node_t *source_node,
svn_fs_txn_t *txn,
svn_stringbuf_t *conflict,
apr_pool_t *pool)
{
dag_node_t *txn_root_node;
svn_fs_t *fs = txn->fs;
const char *txn_id = txn->id;
SVN_ERR(svn_fs_fs__dag_txn_root(&txn_root_node, fs, txn_id, pool));
if (ancestor_node == NULL)
{
SVN_ERR(svn_fs_fs__dag_txn_base_root(&ancestor_node, fs,
txn_id, pool));
}
if (svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(ancestor_node),
svn_fs_fs__dag_get_id(txn_root_node)))
{
/* If no changes have been made in TXN since its current base,
then it can't conflict with any changes since that base.
The caller isn't supposed to call us in that case. */
SVN_ERR_MALFUNCTION();
}
else
SVN_ERR(merge(conflict, "/", txn_root_node,
source_node, ancestor_node, txn_id, NULL, pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__commit_txn(const char **conflict_p,
svn_revnum_t *new_rev,
svn_fs_txn_t *txn,
apr_pool_t *pool)
{
/* How do commits work in Subversion?
*
* When you're ready to commit, here's what you have:
*
* 1. A transaction, with a mutable tree hanging off it.
* 2. A base revision, against which TXN_TREE was made.
* 3. A latest revision, which may be newer than the base rev.
*
* The problem is that if latest != base, then one can't simply
* attach the txn root as the root of the new revision, because that
* would lose all the changes between base and latest. It is also
* not acceptable to insist that base == latest; in a busy
* repository, commits happen too fast to insist that everyone keep
* their entire tree up-to-date at all times. Non-overlapping
* changes should not interfere with each other.
*
* The solution is to merge the changes between base and latest into
* the txn tree [see the function merge()]. The txn tree is the
* only one of the three trees that is mutable, so it has to be the
* one to adjust.
*
* You might have to adjust it more than once, if a new latest
* revision gets committed while you were merging in the previous
* one. For example:
*
* 1. Jane starts txn T, based at revision 6.
* 2. Someone commits (or already committed) revision 7.
* 3. Jane's starts merging the changes between 6 and 7 into T.
* 4. Meanwhile, someone commits revision 8.
* 5. Jane finishes the 6-->7 merge. T could now be committed
* against a latest revision of 7, if only that were still the
* latest. Unfortunately, 8 is now the latest, so...
* 6. Jane starts merging the changes between 7 and 8 into T.
* 7. Meanwhile, no one commits any new revisions. Whew.
* 8. Jane commits T, creating revision 9, whose tree is exactly
* T's tree, except immutable now.
*
* Lather, rinse, repeat.
*/
svn_error_t *err = SVN_NO_ERROR;
svn_stringbuf_t *conflict = svn_stringbuf_create_empty(pool);
svn_fs_t *fs = txn->fs;
/* Limit memory usage when the repository has a high commit rate and
needs to run the following while loop multiple times. The memory
growth without an iteration pool is very noticeable when the
transaction modifies a node that has 20,000 sibling nodes. */
apr_pool_t *iterpool = svn_pool_create(pool);
/* Initialize output params. */
*new_rev = SVN_INVALID_REVNUM;
if (conflict_p)
*conflict_p = NULL;
while (1729)
{
svn_revnum_t youngish_rev;
svn_fs_root_t *youngish_root;
dag_node_t *youngish_root_node;
svn_pool_clear(iterpool);
/* Get the *current* youngest revision. We call it "youngish"
because new revisions might get committed after we've
obtained it. */
SVN_ERR(svn_fs_fs__youngest_rev(&youngish_rev, fs, iterpool));
SVN_ERR(svn_fs_fs__revision_root(&youngish_root, fs, youngish_rev,
iterpool));
/* Get the dag node for the youngest revision. Later we'll use
it as the SOURCE argument to a merge, and if the merge
succeeds, this youngest root node will become the new base
root for the svn txn that was the target of the merge (but
note that the youngest rev may have changed by then -- that's
why we're careful to get this root in its own bdb txn
here). */
SVN_ERR(get_root(&youngish_root_node, youngish_root, iterpool));
/* Try to merge. If the merge succeeds, the base root node of
TARGET's txn will become the same as youngish_root_node, so
any future merges will only be between that node and whatever
the root node of the youngest rev is by then. */
err = merge_changes(NULL, youngish_root_node, txn, conflict, iterpool);
if (err)
{
if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
*conflict_p = conflict->data;
goto cleanup;
}
txn->base_rev = youngish_rev;
/* Try to commit. */
err = svn_fs_fs__commit(new_rev, fs, txn, iterpool);
if (err && (err->apr_err == SVN_ERR_FS_TXN_OUT_OF_DATE))
{
/* Did someone else finish committing a new revision while we
were in mid-merge or mid-commit? If so, we'll need to
loop again to merge the new changes in, then try to
commit again. Or if that's not what happened, then just
return the error. */
svn_revnum_t youngest_rev;
SVN_ERR(svn_fs_fs__youngest_rev(&youngest_rev, fs, iterpool));
if (youngest_rev == youngish_rev)
goto cleanup;
else
svn_error_clear(err);
}
else if (err)
{
goto cleanup;
}
else
{
err = SVN_NO_ERROR;
goto cleanup;
}
}
cleanup:
svn_fs_fs__reset_txn_caches(fs);
svn_pool_destroy(iterpool);
return svn_error_trace(err);
}
/* Merge changes between two nodes into a third node. Given nodes
SOURCE_PATH under SOURCE_ROOT, TARGET_PATH under TARGET_ROOT and
ANCESTOR_PATH under ANCESTOR_ROOT, modify target to contain all the
changes between the ancestor and source. If there are conflicts,
return SVN_ERR_FS_CONFLICT and set *CONFLICT_P to a textual
description of the offending changes. Perform any temporary
allocations in POOL. */
static svn_error_t *
fs_merge(const char **conflict_p,
svn_fs_root_t *source_root,
const char *source_path,
svn_fs_root_t *target_root,
const char *target_path,
svn_fs_root_t *ancestor_root,
const char *ancestor_path,
apr_pool_t *pool)
{
dag_node_t *source, *ancestor;
svn_fs_txn_t *txn;
svn_error_t *err;
svn_stringbuf_t *conflict = svn_stringbuf_create_empty(pool);
if (! target_root->is_txn_root)
return SVN_FS__NOT_TXN(target_root);
/* Paranoia. */
if ((source_root->fs != ancestor_root->fs)
|| (target_root->fs != ancestor_root->fs))
{
return svn_error_create
(SVN_ERR_FS_CORRUPT, NULL,
_("Bad merge; ancestor, source, and target not all in same fs"));
}
/* ### kff todo: is there any compelling reason to get the nodes in
one db transaction? Right now we don't; txn_body_get_root() gets
one node at a time. This will probably need to change:
Jim Blandy <jimb@zwingli.cygnus.com> writes:
> svn_fs_merge needs to be a single transaction, to protect it against
> people deleting parents of nodes it's working on, etc.
*/
/* Get the ancestor node. */
SVN_ERR(get_root(&ancestor, ancestor_root, pool));
/* Get the source node. */
SVN_ERR(get_root(&source, source_root, pool));
/* Open a txn for the txn root into which we're merging. */
SVN_ERR(svn_fs_fs__open_txn(&txn, ancestor_root->fs, target_root->txn,
pool));
/* Merge changes between ANCESTOR and SOURCE into TXN. */
err = merge_changes(ancestor, source, txn, conflict, pool);
if (err)
{
if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
*conflict_p = conflict->data;
return svn_error_trace(err);
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__deltify(svn_fs_t *fs,
svn_revnum_t revision,
apr_pool_t *pool)
{
/* Deltify is a no-op for fs_fs. */
return SVN_NO_ERROR;
}
/* Directories. */
/* Set *TABLE_P to a newly allocated APR hash table containing the
entries of the directory at PATH in ROOT. The keys of the table
are entry names, as byte strings, excluding the final null
character; the table's values are pointers to svn_fs_dirent_t
structures. Allocate the table and its contents in POOL. */
static svn_error_t *
fs_dir_entries(apr_hash_t **table_p,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
dag_node_t *node;
/* Get the entries for this path in the caller's pool. */
- SVN_ERR(get_dag(&node, root, path, FALSE, pool));
+ SVN_ERR(get_dag(&node, root, path, pool));
return svn_fs_fs__dag_dir_entries(table_p, node, pool);
}
/* Raise an error if PATH contains a newline because FSFS cannot handle
* such paths. See issue #4340. */
static svn_error_t *
check_newline(const char *path, apr_pool_t *pool)
{
char *c = strchr(path, '\n');
if (c)
return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
_("Invalid control character '0x%02x' in path '%s'"),
(unsigned char)*c, svn_path_illegal_path_escape(path, pool));
return SVN_NO_ERROR;
}
/* Create a new directory named PATH in ROOT. The new directory has
no entries, and no properties. ROOT must be the root of a
transaction, not a revision. Do any necessary temporary allocation
in POOL. */
static svn_error_t *
fs_make_dir(svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
parent_path_t *parent_path;
dag_node_t *sub_dir;
const char *txn_id = root->txn;
SVN_ERR(check_newline(path, pool));
path = svn_fs__canonicalize_abspath(path, pool);
SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
txn_id, pool));
/* Check (recursively) to see if some lock is 'reserving' a path at
that location, or even some child-path; if so, check that we can
use it. */
if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, TRUE, FALSE,
pool));
/* If there's already a sub-directory by that name, complain. This
also catches the case of trying to make a subdirectory named `/'. */
if (parent_path->node)
return SVN_FS__ALREADY_EXISTS(root, path);
/* Create the subdirectory. */
SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool));
SVN_ERR(svn_fs_fs__dag_make_dir(&sub_dir,
parent_path->parent->node,
parent_path_path(parent_path->parent,
pool),
parent_path->entry,
txn_id,
pool));
/* Add this directory to the path cache. */
SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool),
sub_dir, pool));
/* Make a record of this modification in the changes table. */
return add_change(root->fs, txn_id, path, svn_fs_fs__dag_get_id(sub_dir),
svn_fs_path_change_add, FALSE, FALSE, svn_node_dir,
SVN_INVALID_REVNUM, NULL, pool);
}
/* Delete the node at PATH under ROOT. ROOT must be a transaction
root. Perform temporary allocations in POOL. */
static svn_error_t *
fs_delete_node(svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
parent_path_t *parent_path;
const char *txn_id = root->txn;
apr_int64_t mergeinfo_count = 0;
svn_node_kind_t kind;
if (! root->is_txn_root)
return SVN_FS__NOT_TXN(root);
path = svn_fs__canonicalize_abspath(path, pool);
SVN_ERR(open_path(&parent_path, root, path, 0, txn_id, pool));
kind = svn_fs_fs__dag_node_kind(parent_path->node);
/* We can't remove the root of the filesystem. */
if (! parent_path->parent)
return svn_error_create(SVN_ERR_FS_ROOT_DIR, NULL,
_("The root directory cannot be deleted"));
/* Check to see if path (or any child thereof) is locked; if so,
check that we can use the existing lock(s). */
if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, TRUE, FALSE,
pool));
/* Make the parent directory mutable, and do the deletion. */
SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool));
if (svn_fs_fs__fs_supports_mergeinfo(root->fs))
SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_count,
parent_path->node));
SVN_ERR(svn_fs_fs__dag_delete(parent_path->parent->node,
parent_path->entry,
txn_id, pool));
/* Remove this node and any children from the path cache. */
SVN_ERR(dag_node_cache_invalidate(root, parent_path_path(parent_path, pool),
pool));
/* Update mergeinfo counts for parents */
if (mergeinfo_count > 0)
SVN_ERR(increment_mergeinfo_up_tree(parent_path->parent,
-mergeinfo_count,
pool));
/* Make a record of this modification in the changes table. */
return add_change(root->fs, txn_id, path,
svn_fs_fs__dag_get_id(parent_path->node),
svn_fs_path_change_delete, FALSE, FALSE, kind,
SVN_INVALID_REVNUM, NULL, pool);
}
/* Set *SAME_P to TRUE if FS1 and FS2 have the same UUID, else set to FALSE.
Use POOL for temporary allocation only.
Note: this code is duplicated between libsvn_fs_fs and libsvn_fs_base. */
static svn_error_t *
fs_same_p(svn_boolean_t *same_p,
svn_fs_t *fs1,
svn_fs_t *fs2,
apr_pool_t *pool)
{
*same_p = ! strcmp(fs1->uuid, fs2->uuid);
return SVN_NO_ERROR;
}
/* Copy the node at FROM_PATH under FROM_ROOT to TO_PATH under
TO_ROOT. If PRESERVE_HISTORY is set, then the copy is recorded in
the copies table. Perform temporary allocations in POOL. */
static svn_error_t *
copy_helper(svn_fs_root_t *from_root,
const char *from_path,
svn_fs_root_t *to_root,
const char *to_path,
svn_boolean_t preserve_history,
apr_pool_t *pool)
{
dag_node_t *from_node;
parent_path_t *to_parent_path;
const char *txn_id = to_root->txn;
svn_boolean_t same_p;
/* Use an error check, not an assert, because even the caller cannot
guarantee that a filesystem's UUID has not changed "on the fly". */
SVN_ERR(fs_same_p(&same_p, from_root->fs, to_root->fs, pool));
if (! same_p)
return svn_error_createf
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Cannot copy between two different filesystems ('%s' and '%s')"),
from_root->fs->path, to_root->fs->path);
if (from_root->is_txn_root)
return svn_error_create
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Copy from mutable tree not currently supported"));
/* Get the NODE for FROM_PATH in FROM_ROOT.*/
- SVN_ERR(get_dag(&from_node, from_root, from_path, TRUE, pool));
+ SVN_ERR(get_dag(&from_node, from_root, from_path, pool));
/* Build up the parent path from TO_PATH in TO_ROOT. If the last
component does not exist, it's not that big a deal. We'll just
make one there. */
SVN_ERR(open_path(&to_parent_path, to_root, to_path,
open_path_last_optional, txn_id, pool));
/* Check to see if path (or any child thereof) is locked; if so,
check that we can use the existing lock(s). */
if (to_root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
SVN_ERR(svn_fs_fs__allow_locked_operation(to_path, to_root->fs,
TRUE, FALSE, pool));
/* If the destination node already exists as the same node as the
source (in other words, this operation would result in nothing
happening at all), just do nothing an return successfully,
proud that you saved yourself from a tiresome task. */
if (to_parent_path->node &&
svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(from_node),
svn_fs_fs__dag_get_id(to_parent_path->node)))
return SVN_NO_ERROR;
if (! from_root->is_txn_root)
{
svn_fs_path_change_kind_t kind;
dag_node_t *new_node;
const char *from_canonpath;
apr_int64_t mergeinfo_start;
apr_int64_t mergeinfo_end;
/* If TO_PATH already existed prior to the copy, note that this
operation is a replacement, not an addition. */
if (to_parent_path->node)
{
kind = svn_fs_path_change_replace;
if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs))
SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_start,
to_parent_path->node));
}
else
{
kind = svn_fs_path_change_add;
mergeinfo_start = 0;
}
if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs))
SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_end,
from_node));
/* Make sure the target node's parents are mutable. */
SVN_ERR(make_path_mutable(to_root, to_parent_path->parent,
to_path, pool));
/* Canonicalize the copyfrom path. */
from_canonpath = svn_fs__canonicalize_abspath(from_path, pool);
SVN_ERR(svn_fs_fs__dag_copy(to_parent_path->parent->node,
to_parent_path->entry,
from_node,
preserve_history,
from_root->rev,
from_canonpath,
txn_id, pool));
if (kind == svn_fs_path_change_replace)
SVN_ERR(dag_node_cache_invalidate(to_root,
parent_path_path(to_parent_path,
pool), pool));
if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs)
&& mergeinfo_start != mergeinfo_end)
SVN_ERR(increment_mergeinfo_up_tree(to_parent_path->parent,
mergeinfo_end - mergeinfo_start,
pool));
/* Make a record of this modification in the changes table. */
- SVN_ERR(get_dag(&new_node, to_root, to_path, TRUE, pool));
+ SVN_ERR(get_dag(&new_node, to_root, to_path, pool));
SVN_ERR(add_change(to_root->fs, txn_id, to_path,
svn_fs_fs__dag_get_id(new_node), kind, FALSE, FALSE,
svn_fs_fs__dag_node_kind(from_node),
from_root->rev, from_canonpath, pool));
}
else
{
/* See IZ Issue #436 */
/* Copying from transaction roots not currently available.
### cmpilato todo someday: make this not so. :-) Note that
when copying from mutable trees, you have to make sure that
you aren't creating a cyclic graph filesystem, and a simple
referencing operation won't cut it. Currently, we should not
be able to reach this clause, and the interface reports that
this only works from immutable trees anyway, but JimB has
stated that this requirement need not be necessary in the
future. */
SVN_ERR_MALFUNCTION();
}
return SVN_NO_ERROR;
}
/* Create a copy of FROM_PATH in FROM_ROOT named TO_PATH in TO_ROOT.
If FROM_PATH is a directory, copy it recursively. Temporary
allocations are from POOL.*/
static svn_error_t *
fs_copy(svn_fs_root_t *from_root,
const char *from_path,
svn_fs_root_t *to_root,
const char *to_path,
apr_pool_t *pool)
{
SVN_ERR(check_newline(to_path, pool));
return svn_error_trace(copy_helper(from_root,
svn_fs__canonicalize_abspath(from_path,
pool),
to_root,
svn_fs__canonicalize_abspath(to_path,
pool),
TRUE, pool));
}
/* Create a copy of FROM_PATH in FROM_ROOT named TO_PATH in TO_ROOT.
If FROM_PATH is a directory, copy it recursively. No history is
preserved. Temporary allocations are from POOL. */
static svn_error_t *
fs_revision_link(svn_fs_root_t *from_root,
svn_fs_root_t *to_root,
const char *path,
apr_pool_t *pool)
{
if (! to_root->is_txn_root)
return SVN_FS__NOT_TXN(to_root);
path = svn_fs__canonicalize_abspath(path, pool);
return svn_error_trace(copy_helper(from_root, path, to_root, path,
FALSE, pool));
}
/* Discover the copy ancestry of PATH under ROOT. Return a relevant
ancestor/revision combination in *PATH_P and *REV_P. Temporary
allocations are in POOL. */
static svn_error_t *
fs_copied_from(svn_revnum_t *rev_p,
const char **path_p,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
dag_node_t *node;
const char *copyfrom_path, *copyfrom_str = NULL;
svn_revnum_t copyfrom_rev;
char *str, *buf;
/* Check to see if there is a cached version of this copyfrom
entry. */
if (! root->is_txn_root) {
fs_rev_root_data_t *frd = root->fsap_data;
copyfrom_str = svn_hash_gets(frd->copyfrom_cache, path);
}
if (copyfrom_str)
{
if (*copyfrom_str == 0)
{
/* We have a cached entry that says there is no copyfrom
here. */
copyfrom_rev = SVN_INVALID_REVNUM;
copyfrom_path = NULL;
}
else
{
/* Parse the copyfrom string for our cached entry. */
buf = apr_pstrdup(pool, copyfrom_str);
str = svn_cstring_tokenize(" ", &buf);
copyfrom_rev = SVN_STR_TO_REV(str);
copyfrom_path = buf;
}
}
else
{
/* There is no cached entry, look it up the old-fashioned
way. */
- SVN_ERR(get_dag(&node, root, path, TRUE, pool));
+ SVN_ERR(get_dag(&node, root, path, pool));
SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(&copyfrom_rev, node));
SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(&copyfrom_path, node));
}
*rev_p = copyfrom_rev;
*path_p = copyfrom_path;
return SVN_NO_ERROR;
}
/* Files. */
/* Create the empty file PATH under ROOT. Temporary allocations are
in POOL. */
static svn_error_t *
fs_make_file(svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
parent_path_t *parent_path;
dag_node_t *child;
const char *txn_id = root->txn;
SVN_ERR(check_newline(path, pool));
path = svn_fs__canonicalize_abspath(path, pool);
SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
txn_id, pool));
/* If there's already a file by that name, complain.
This also catches the case of trying to make a file named `/'. */
if (parent_path->node)
return SVN_FS__ALREADY_EXISTS(root, path);
/* Check (non-recursively) to see if path is locked; if so, check
that we can use it. */
if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, FALSE, FALSE,
pool));
/* Create the file. */
SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool));
SVN_ERR(svn_fs_fs__dag_make_file(&child,
parent_path->parent->node,
parent_path_path(parent_path->parent,
pool),
parent_path->entry,
txn_id,
pool));
/* Add this file to the path cache. */
SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool), child,
pool));
/* Make a record of this modification in the changes table. */
return add_change(root->fs, txn_id, path, svn_fs_fs__dag_get_id(child),
svn_fs_path_change_add, TRUE, FALSE, svn_node_file,
SVN_INVALID_REVNUM, NULL, pool);
}
/* Set *LENGTH_P to the size of the file PATH under ROOT. Temporary
allocations are in POOL. */
static svn_error_t *
fs_file_length(svn_filesize_t *length_p,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
dag_node_t *file;
/* First create a dag_node_t from the root/path pair. */
- SVN_ERR(get_dag(&file, root, path, FALSE, pool));
+ SVN_ERR(get_dag(&file, root, path, pool));
/* Now fetch its length */
return svn_fs_fs__dag_file_length(length_p, file, pool);
}
/* Set *CHECKSUM to the checksum of type KIND for PATH under ROOT, or
NULL if that information isn't available. Temporary allocations
are from POOL. */
static svn_error_t *
fs_file_checksum(svn_checksum_t **checksum,
svn_checksum_kind_t kind,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
dag_node_t *file;
- SVN_ERR(get_dag(&file, root, path, FALSE, pool));
+ SVN_ERR(get_dag(&file, root, path, pool));
return svn_fs_fs__dag_file_checksum(checksum, file, kind, pool);
}
/* --- Machinery for svn_fs_file_contents() --- */
/* Set *CONTENTS to a readable stream that will return the contents of
PATH under ROOT. The stream is allocated in POOL. */
static svn_error_t *
fs_file_contents(svn_stream_t **contents,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
dag_node_t *node;
svn_stream_t *file_stream;
/* First create a dag_node_t from the root/path pair. */
- SVN_ERR(get_dag(&node, root, path, FALSE, pool));
+ SVN_ERR(get_dag(&node, root, path, pool));
/* Then create a readable stream from the dag_node_t. */
SVN_ERR(svn_fs_fs__dag_get_contents(&file_stream, node, pool));
*contents = file_stream;
return SVN_NO_ERROR;
}
/* --- End machinery for svn_fs_file_contents() --- */
/* --- Machinery for svn_fs_try_process_file_contents() --- */
static svn_error_t *
fs_try_process_file_contents(svn_boolean_t *success,
svn_fs_root_t *root,
const char *path,
svn_fs_process_contents_func_t processor,
void* baton,
apr_pool_t *pool)
{
dag_node_t *node;
- SVN_ERR(get_dag(&node, root, path, FALSE, pool));
+ SVN_ERR(get_dag(&node, root, path, pool));
return svn_fs_fs__dag_try_process_file_contents(success, node,
processor, baton, pool);
}
/* --- End machinery for svn_fs_try_process_file_contents() --- */
/* --- Machinery for svn_fs_apply_textdelta() --- */
/* Local baton type for all the helper functions below. */
typedef struct txdelta_baton_t
{
/* This is the custom-built window consumer given to us by the delta
library; it uniquely knows how to read data from our designated
"source" stream, interpret the window, and write data to our
designated "target" stream (in this case, our repos file.) */
svn_txdelta_window_handler_t interpreter;
void *interpreter_baton;
/* The original file info */
svn_fs_root_t *root;
const char *path;
/* Derived from the file info */
dag_node_t *node;
svn_stream_t *source_stream;
svn_stream_t *target_stream;
svn_stream_t *string_stream;
svn_stringbuf_t *target_string;
/* MD5 digest for the base text against which a delta is to be
applied, and for the resultant fulltext, respectively. Either or
both may be null, in which case ignored. */
svn_checksum_t *base_checksum;
svn_checksum_t *result_checksum;
/* Pool used by db txns */
apr_pool_t *pool;
} txdelta_baton_t;
/* ### see comment in window_consumer() regarding this function. */
/* Helper function of generic type `svn_write_fn_t'. Implements a
writable stream which appends to an svn_stringbuf_t. */
static svn_error_t *
write_to_string(void *baton, const char *data, apr_size_t *len)
{
txdelta_baton_t *tb = (txdelta_baton_t *) baton;
svn_stringbuf_appendbytes(tb->target_string, data, *len);
return SVN_NO_ERROR;
}
/* The main window handler returned by svn_fs_apply_textdelta. */
static svn_error_t *
window_consumer(svn_txdelta_window_t *window, void *baton)
{
txdelta_baton_t *tb = (txdelta_baton_t *) baton;
/* Send the window right through to the custom window interpreter.
In theory, the interpreter will then write more data to
cb->target_string. */
SVN_ERR(tb->interpreter(window, tb->interpreter_baton));
/* ### the write_to_string() callback for the txdelta's output stream
### should be doing all the flush determination logic, not here.
### in a drastic case, a window could generate a LOT more than the
### maximum buffer size. we want to flush to the underlying target
### stream much sooner (e.g. also in a streamy fashion). also, by
### moving this logic inside the stream, the stream becomes nice
### and encapsulated: it holds all the logic about buffering and
### flushing.
###
### further: I believe the buffering should be removed from tree.c
### the buffering should go into the target_stream itself, which
### is defined by reps-string.c. Specifically, I think the
### rep_write_contents() function will handle the buffering and
### the spill to the underlying DB. by locating it there, then
### anybody who gets a writable stream for FS content can take
### advantage of the buffering capability. this will be important
### when we export an FS API function for writing a fulltext into
### the FS, rather than forcing that fulltext thru apply_textdelta.
*/
/* Check to see if we need to purge the portion of the contents that
have been written thus far. */
if ((! window) || (tb->target_string->len > WRITE_BUFFER_SIZE))
{
apr_size_t len = tb->target_string->len;
SVN_ERR(svn_stream_write(tb->target_stream,
tb->target_string->data,
&len));
svn_stringbuf_setempty(tb->target_string);
}
/* Is the window NULL? If so, we're done. */
if (! window)
{
/* Close the internal-use stream. ### This used to be inside of
txn_body_fulltext_finalize_edits(), but that invoked a nested
Berkeley DB transaction -- scandalous! */
SVN_ERR(svn_stream_close(tb->target_stream));
SVN_ERR(svn_fs_fs__dag_finalize_edits(tb->node, tb->result_checksum,
tb->pool));
}
return SVN_NO_ERROR;
}
/* Helper function for fs_apply_textdelta. BATON is of type
txdelta_baton_t. */
static svn_error_t *
apply_textdelta(void *baton, apr_pool_t *pool)
{
txdelta_baton_t *tb = (txdelta_baton_t *) baton;
parent_path_t *parent_path;
const char *txn_id = tb->root->txn;
/* Call open_path with no flags, as we want this to return an error
if the node for which we are searching doesn't exist. */
SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id, pool));
/* Check (non-recursively) to see if path is locked; if so, check
that we can use it. */
if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
SVN_ERR(svn_fs_fs__allow_locked_operation(tb->path, tb->root->fs,
FALSE, FALSE, pool));
/* Now, make sure this path is mutable. */
SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path, pool));
tb->node = parent_path->node;
if (tb->base_checksum)
{
svn_checksum_t *checksum;
/* Until we finalize the node, its data_key points to the old
contents, in other words, the base text. */
SVN_ERR(svn_fs_fs__dag_file_checksum(&checksum, tb->node,
tb->base_checksum->kind, pool));
if (!svn_checksum_match(tb->base_checksum, checksum))
return svn_checksum_mismatch_err(tb->base_checksum, checksum, pool,
_("Base checksum mismatch on '%s'"),
tb->path);
}
/* Make a readable "source" stream out of the current contents of
ROOT/PATH; obviously, this must done in the context of a db_txn.
The stream is returned in tb->source_stream. */
SVN_ERR(svn_fs_fs__dag_get_contents(&(tb->source_stream),
tb->node, tb->pool));
/* Make a writable "target" stream */
SVN_ERR(svn_fs_fs__dag_get_edit_stream(&(tb->target_stream), tb->node,
tb->pool));
/* Make a writable "string" stream which writes data to
tb->target_string. */
tb->target_string = svn_stringbuf_create_empty(tb->pool);
tb->string_stream = svn_stream_create(tb, tb->pool);
svn_stream_set_write(tb->string_stream, write_to_string);
/* Now, create a custom window handler that uses our two streams. */
svn_txdelta_apply(tb->source_stream,
tb->string_stream,
NULL,
tb->path,
tb->pool,
&(tb->interpreter),
&(tb->interpreter_baton));
/* Make a record of this modification in the changes table. */
return add_change(tb->root->fs, txn_id, tb->path,
svn_fs_fs__dag_get_id(tb->node),
svn_fs_path_change_modify, TRUE, FALSE, svn_node_file,
SVN_INVALID_REVNUM, NULL, pool);
}
/* Set *CONTENTS_P and *CONTENTS_BATON_P to a window handler and baton
that will accept text delta windows to modify the contents of PATH
under ROOT. Allocations are in POOL. */
static svn_error_t *
fs_apply_textdelta(svn_txdelta_window_handler_t *contents_p,
void **contents_baton_p,
svn_fs_root_t *root,
const char *path,
svn_checksum_t *base_checksum,
svn_checksum_t *result_checksum,
apr_pool_t *pool)
{
txdelta_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
tb->root = root;
tb->path = svn_fs__canonicalize_abspath(path, pool);
tb->pool = pool;
tb->base_checksum = svn_checksum_dup(base_checksum, pool);
tb->result_checksum = svn_checksum_dup(result_checksum, pool);
SVN_ERR(apply_textdelta(tb, pool));
*contents_p = window_consumer;
*contents_baton_p = tb;
return SVN_NO_ERROR;
}
/* --- End machinery for svn_fs_apply_textdelta() --- */
/* --- Machinery for svn_fs_apply_text() --- */
/* Baton for svn_fs_apply_text(). */
struct text_baton_t
{
/* The original file info */
svn_fs_root_t *root;
const char *path;
/* Derived from the file info */
dag_node_t *node;
/* The returned stream that will accept the file's new contents. */
svn_stream_t *stream;
/* The actual fs stream that the returned stream will write to. */
svn_stream_t *file_stream;
/* MD5 digest for the final fulltext written to the file. May
be null, in which case ignored. */
svn_checksum_t *result_checksum;
/* Pool used by db txns */
apr_pool_t *pool;
};
/* A wrapper around svn_fs_fs__dag_finalize_edits, but for
* fulltext data, not text deltas. Closes BATON->file_stream.
*
* Note: If you're confused about how this function relates to another
* of similar name, think of it this way:
*
* svn_fs_apply_textdelta() ==> ... ==> txn_body_txdelta_finalize_edits()
* svn_fs_apply_text() ==> ... ==> txn_body_fulltext_finalize_edits()
*/
/* Write function for the publically returned stream. */
static svn_error_t *
text_stream_writer(void *baton,
const char *data,
apr_size_t *len)
{
struct text_baton_t *tb = baton;
/* Psst, here's some data. Pass it on to the -real- file stream. */
return svn_stream_write(tb->file_stream, data, len);
}
/* Close function for the publically returned stream. */
static svn_error_t *
text_stream_closer(void *baton)
{
struct text_baton_t *tb = baton;
/* Close the internal-use stream. ### This used to be inside of
txn_body_fulltext_finalize_edits(), but that invoked a nested
Berkeley DB transaction -- scandalous! */
SVN_ERR(svn_stream_close(tb->file_stream));
/* Need to tell fs that we're done sending text */
return svn_fs_fs__dag_finalize_edits(tb->node, tb->result_checksum,
tb->pool);
}
/* Helper function for fs_apply_text. BATON is of type
text_baton_t. */
static svn_error_t *
apply_text(void *baton, apr_pool_t *pool)
{
struct text_baton_t *tb = baton;
parent_path_t *parent_path;
const char *txn_id = tb->root->txn;
/* Call open_path with no flags, as we want this to return an error
if the node for which we are searching doesn't exist. */
SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id, pool));
/* Check (non-recursively) to see if path is locked; if so, check
that we can use it. */
if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
SVN_ERR(svn_fs_fs__allow_locked_operation(tb->path, tb->root->fs,
FALSE, FALSE, pool));
/* Now, make sure this path is mutable. */
SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path, pool));
tb->node = parent_path->node;
/* Make a writable stream for replacing the file's text. */
SVN_ERR(svn_fs_fs__dag_get_edit_stream(&(tb->file_stream), tb->node,
tb->pool));
/* Create a 'returnable' stream which writes to the file_stream. */
tb->stream = svn_stream_create(tb, tb->pool);
svn_stream_set_write(tb->stream, text_stream_writer);
svn_stream_set_close(tb->stream, text_stream_closer);
/* Make a record of this modification in the changes table. */
return add_change(tb->root->fs, txn_id, tb->path,
svn_fs_fs__dag_get_id(tb->node),
svn_fs_path_change_modify, TRUE, FALSE, svn_node_file,
SVN_INVALID_REVNUM, NULL, pool);
}
/* Return a writable stream that will set the contents of PATH under
ROOT. RESULT_CHECKSUM is the MD5 checksum of the final result.
Temporary allocations are in POOL. */
static svn_error_t *
fs_apply_text(svn_stream_t **contents_p,
svn_fs_root_t *root,
const char *path,
svn_checksum_t *result_checksum,
apr_pool_t *pool)
{
struct text_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
tb->root = root;
tb->path = svn_fs__canonicalize_abspath(path, pool);
tb->pool = pool;
tb->result_checksum = svn_checksum_dup(result_checksum, pool);
SVN_ERR(apply_text(tb, pool));
*contents_p = tb->stream;
return SVN_NO_ERROR;
}
/* --- End machinery for svn_fs_apply_text() --- */
/* Check if the contents of PATH1 under ROOT1 are different from the
contents of PATH2 under ROOT2. If they are different set
*CHANGED_P to TRUE, otherwise set it to FALSE. */
static svn_error_t *
fs_contents_changed(svn_boolean_t *changed_p,
svn_fs_root_t *root1,
const char *path1,
svn_fs_root_t *root2,
const char *path2,
apr_pool_t *pool)
{
dag_node_t *node1, *node2;
/* Check that roots are in the same fs. */
if (root1->fs != root2->fs)
return svn_error_create
(SVN_ERR_FS_GENERAL, NULL,
_("Cannot compare file contents between two different filesystems"));
/* Check that both paths are files. */
{
svn_node_kind_t kind;
SVN_ERR(svn_fs_fs__check_path(&kind, root1, path1, pool));
if (kind != svn_node_file)
return svn_error_createf
(SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path1);
SVN_ERR(svn_fs_fs__check_path(&kind, root2, path2, pool));
if (kind != svn_node_file)
return svn_error_createf
(SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path2);
}
- SVN_ERR(get_dag(&node1, root1, path1, TRUE, pool));
- SVN_ERR(get_dag(&node2, root2, path2, TRUE, pool));
+ SVN_ERR(get_dag(&node1, root1, path1, pool));
+ SVN_ERR(get_dag(&node2, root2, path2, pool));
return svn_fs_fs__dag_things_different(NULL, changed_p,
node1, node2);
}
/* Public interface to computing file text deltas. */
static svn_error_t *
fs_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
svn_fs_root_t *source_root,
const char *source_path,
svn_fs_root_t *target_root,
const char *target_path,
apr_pool_t *pool)
{
dag_node_t *source_node, *target_node;
if (source_root && source_path)
- SVN_ERR(get_dag(&source_node, source_root, source_path, TRUE, pool));
+ SVN_ERR(get_dag(&source_node, source_root, source_path, pool));
else
source_node = NULL;
- SVN_ERR(get_dag(&target_node, target_root, target_path, TRUE, pool));
+ SVN_ERR(get_dag(&target_node, target_root, target_path, pool));
/* Create a delta stream that turns the source into the target. */
return svn_fs_fs__dag_get_file_delta_stream(stream_p, source_node,
target_node, pool);
}
/* Finding Changes */
/* Set *CHANGED_PATHS_P to a newly allocated hash containing
descriptions of the paths changed under ROOT. The hash is keyed
with const char * paths and has svn_fs_path_change2_t * values. Use
POOL for all allocations. */
static svn_error_t *
fs_paths_changed(apr_hash_t **changed_paths_p,
svn_fs_root_t *root,
apr_pool_t *pool)
{
if (root->is_txn_root)
return svn_fs_fs__txn_changes_fetch(changed_paths_p, root->fs, root->txn,
pool);
else
{
fs_rev_root_data_t *frd = root->fsap_data;
return svn_fs_fs__paths_changed(changed_paths_p, root->fs, root->rev,
frd->copyfrom_cache, pool);
}
}
/* Our coolio opaque history object. */
typedef struct fs_history_data_t
{
/* filesystem object */
svn_fs_t *fs;
/* path and revision of historical location */
const char *path;
svn_revnum_t revision;
/* internal-use hints about where to resume the history search. */
const char *path_hint;
svn_revnum_t rev_hint;
/* FALSE until the first call to svn_fs_history_prev(). */
svn_boolean_t is_interesting;
} fs_history_data_t;
static svn_fs_history_t *
assemble_history(svn_fs_t *fs,
const char *path,
svn_revnum_t revision,
svn_boolean_t is_interesting,
const char *path_hint,
svn_revnum_t rev_hint,
apr_pool_t *pool);
/* Set *HISTORY_P to an opaque node history object which represents
PATH under ROOT. ROOT must be a revision root. Use POOL for all
allocations. */
static svn_error_t *
fs_node_history(svn_fs_history_t **history_p,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
svn_node_kind_t kind;
/* We require a revision root. */
if (root->is_txn_root)
return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
/* And we require that the path exist in the root. */
SVN_ERR(svn_fs_fs__check_path(&kind, root, path, pool));
if (kind == svn_node_none)
return SVN_FS__NOT_FOUND(root, path);
/* Okay, all seems well. Build our history object and return it. */
*history_p = assemble_history(root->fs,
svn_fs__canonicalize_abspath(path, pool),
root->rev, FALSE, NULL,
SVN_INVALID_REVNUM, pool);
return SVN_NO_ERROR;
}
/* Find the youngest copyroot for path PARENT_PATH or its parents in
filesystem FS, and store the copyroot in *REV_P and *PATH_P.
Perform all allocations in POOL. */
static svn_error_t *
find_youngest_copyroot(svn_revnum_t *rev_p,
const char **path_p,
svn_fs_t *fs,
parent_path_t *parent_path,
apr_pool_t *pool)
{
svn_revnum_t rev_mine;
svn_revnum_t rev_parent = SVN_INVALID_REVNUM;
const char *path_mine;
const char *path_parent = NULL;
/* First find our parent's youngest copyroot. */
if (parent_path->parent)
SVN_ERR(find_youngest_copyroot(&rev_parent, &path_parent, fs,
parent_path->parent, pool));
/* Find our copyroot. */
SVN_ERR(svn_fs_fs__dag_get_copyroot(&rev_mine, &path_mine,
parent_path->node));
/* If a parent and child were copied to in the same revision, prefer
the child copy target, since it is the copy relevant to the
history of the child. */
if (rev_mine >= rev_parent)
{
*rev_p = rev_mine;
*path_p = path_mine;
}
else
{
*rev_p = rev_parent;
*path_p = path_parent;
}
return SVN_NO_ERROR;
}
static svn_error_t *fs_closest_copy(svn_fs_root_t **root_p,
const char **path_p,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
svn_fs_t *fs = root->fs;
parent_path_t *parent_path, *copy_dst_parent_path;
svn_revnum_t copy_dst_rev, created_rev;
const char *copy_dst_path;
svn_fs_root_t *copy_dst_root;
dag_node_t *copy_dst_node;
svn_node_kind_t kind;
/* Initialize return values. */
*root_p = NULL;
*path_p = NULL;
path = svn_fs__canonicalize_abspath(path, pool);
SVN_ERR(open_path(&parent_path, root, path, 0, NULL, pool));
/* Find the youngest copyroot in the path of this node-rev, which
will indicate the target of the innermost copy affecting the
node-rev. */
SVN_ERR(find_youngest_copyroot(&copy_dst_rev, &copy_dst_path,
fs, parent_path, pool));
if (copy_dst_rev == 0) /* There are no copies affecting this node-rev. */
return SVN_NO_ERROR;
/* It is possible that this node was created from scratch at some
revision between COPY_DST_REV and REV. Make sure that PATH
exists as of COPY_DST_REV and is related to this node-rev. */
SVN_ERR(svn_fs_fs__revision_root(&copy_dst_root, fs, copy_dst_rev, pool));
SVN_ERR(svn_fs_fs__check_path(&kind, copy_dst_root, path, pool));
if (kind == svn_node_none)
return SVN_NO_ERROR;
SVN_ERR(open_path(&copy_dst_parent_path, copy_dst_root, path,
open_path_node_only, NULL, pool));
copy_dst_node = copy_dst_parent_path->node;
if (! svn_fs_fs__id_check_related(svn_fs_fs__dag_get_id(copy_dst_node),
svn_fs_fs__dag_get_id(parent_path->node)))
return SVN_NO_ERROR;
/* One final check must be done here. If you copy a directory and
create a new entity somewhere beneath that directory in the same
txn, then we can't claim that the copy affected the new entity.
For example, if you do:
copy dir1 dir2
create dir2/new-thing
commit
then dir2/new-thing was not affected by the copy of dir1 to dir2.
We detect this situation by asking if PATH@COPY_DST_REV's
created-rev is COPY_DST_REV, and that node-revision has no
predecessors, then there is no relevant closest copy.
*/
SVN_ERR(svn_fs_fs__dag_get_revision(&created_rev, copy_dst_node, pool));
if (created_rev == copy_dst_rev)
{
const svn_fs_id_t *pred;
SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred, copy_dst_node));
if (! pred)
return SVN_NO_ERROR;
}
/* The copy destination checks out. Return it. */
*root_p = copy_dst_root;
*path_p = copy_dst_path;
return SVN_NO_ERROR;
}
/* Set *PREV_PATH and *PREV_REV to the path and revision which
represent the location at which PATH in FS was located immediately
prior to REVISION iff there was a copy operation (to PATH or one of
its parent directories) between that previous location and
PATH@REVISION.
If there was no such copy operation in that portion of PATH's
history, set *PREV_PATH to NULL and *PREV_REV to SVN_INVALID_REVNUM. */
static svn_error_t *
prev_location(const char **prev_path,
svn_revnum_t *prev_rev,
svn_fs_t *fs,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
const char *copy_path, *copy_src_path, *remainder_path;
svn_fs_root_t *copy_root;
svn_revnum_t copy_src_rev;
/* Ask about the most recent copy which affected PATH@REVISION. If
there was no such copy, we're done. */
SVN_ERR(fs_closest_copy(&copy_root, &copy_path, root, path, pool));
if (! copy_root)
{
*prev_rev = SVN_INVALID_REVNUM;
*prev_path = NULL;
return SVN_NO_ERROR;
}
/* Ultimately, it's not the path of the closest copy's source that
we care about -- it's our own path's location in the copy source
revision. So we'll tack the relative path that expresses the
difference between the copy destination and our path in the copy
revision onto the copy source path to determine this information.
In other words, if our path is "/branches/my-branch/foo/bar", and
we know that the closest relevant copy was a copy of "/trunk" to
"/branches/my-branch", then that relative path under the copy
destination is "/foo/bar". Tacking that onto the copy source
path tells us that our path was located at "/trunk/foo/bar"
before the copy.
*/
SVN_ERR(fs_copied_from(&copy_src_rev, &copy_src_path,
copy_root, copy_path, pool));
remainder_path = svn_fspath__skip_ancestor(copy_path, path);
*prev_path = svn_fspath__join(copy_src_path, remainder_path, pool);
*prev_rev = copy_src_rev;
return SVN_NO_ERROR;
}
static svn_error_t *
fs_node_origin_rev(svn_revnum_t *revision,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
svn_fs_t *fs = root->fs;
const svn_fs_id_t *given_noderev_id, *cached_origin_id;
const char *node_id, *dash;
path = svn_fs__canonicalize_abspath(path, pool);
/* Check the cache first. */
SVN_ERR(svn_fs_fs__node_id(&given_noderev_id, root, path, pool));
node_id = svn_fs_fs__id_node_id(given_noderev_id);
/* Is it a brand new uncommitted node? */
if (node_id[0] == '_')
{
*revision = SVN_INVALID_REVNUM;
return SVN_NO_ERROR;
}
/* Maybe this is a new-style node ID that just has the revision
sitting right in it. */
dash = strchr(node_id, '-');
if (dash && *(dash+1))
{
*revision = SVN_STR_TO_REV(dash + 1);
return SVN_NO_ERROR;
}
/* The root node always has ID 0, created in revision 0 and will never
use the new-style ID format. */
if (strcmp(node_id, "0") == 0)
{
*revision = 0;
return SVN_NO_ERROR;
}
/* OK, it's an old-style ID? Maybe it's cached. */
SVN_ERR(svn_fs_fs__get_node_origin(&cached_origin_id,
fs,
node_id,
pool));
if (cached_origin_id != NULL)
{
*revision = svn_fs_fs__id_rev(cached_origin_id);
return SVN_NO_ERROR;
}
{
/* Ah well, the answer isn't in the ID itself or in the cache.
Let's actually calculate it, then. */
svn_fs_root_t *curroot = root;
apr_pool_t *subpool = svn_pool_create(pool);
apr_pool_t *predidpool = svn_pool_create(pool);
svn_stringbuf_t *lastpath = svn_stringbuf_create(path, pool);
svn_revnum_t lastrev = SVN_INVALID_REVNUM;
dag_node_t *node;
const svn_fs_id_t *pred_id;
/* Walk the closest-copy chain back to the first copy in our history.
NOTE: We merely *assume* that this is faster than walking the
predecessor chain, because we *assume* that copies of parent
directories happen less often than modifications to a given item. */
while (1)
{
svn_revnum_t currev;
const char *curpath = lastpath->data;
svn_pool_clear(subpool);
/* Get a root pointing to LASTREV. (The first time around,
LASTREV is invalid, but that's cool because CURROOT is
already initialized.) */
if (SVN_IS_VALID_REVNUM(lastrev))
SVN_ERR(svn_fs_fs__revision_root(&curroot, fs, lastrev, subpool));
/* Find the previous location using the closest-copy shortcut. */
SVN_ERR(prev_location(&curpath, &currev, fs, curroot, curpath,
subpool));
if (! curpath)
break;
/* Update our LASTPATH and LASTREV variables (which survive
SUBPOOL). */
svn_stringbuf_set(lastpath, curpath);
lastrev = currev;
}
/* Walk the predecessor links back to origin. */
SVN_ERR(svn_fs_fs__node_id(&pred_id, curroot, lastpath->data, predidpool));
do
{
svn_pool_clear(subpool);
SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, subpool));
/* Why not just fetch the predecessor ID in PREDIDPOOL?
Because svn_fs_fs__dag_get_predecessor_id() doesn't
necessarily honor the passed-in pool, and might return a
value cached in the node (which is allocated in
SUBPOOL... maybe). */
svn_pool_clear(predidpool);
SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
pred_id = pred_id ? svn_fs_fs__id_copy(pred_id, predidpool) : NULL;
}
while (pred_id);
/* When we get here, NODE should be the first node-revision in our
chain. */
SVN_ERR(svn_fs_fs__dag_get_revision(revision, node, pool));
/* Wow, I don't want to have to do all that again. Let's cache
the result. */
if (node_id[0] != '_')
SVN_ERR(svn_fs_fs__set_node_origin(fs, node_id,
svn_fs_fs__dag_get_id(node), pool));
svn_pool_destroy(subpool);
svn_pool_destroy(predidpool);
return SVN_NO_ERROR;
}
}
struct history_prev_args
{
svn_fs_history_t **prev_history_p;
svn_fs_history_t *history;
svn_boolean_t cross_copies;
apr_pool_t *pool;
};
static svn_error_t *
history_prev(void *baton, apr_pool_t *pool)
{
struct history_prev_args *args = baton;
svn_fs_history_t **prev_history = args->prev_history_p;
svn_fs_history_t *history = args->history;
fs_history_data_t *fhd = history->fsap_data;
const char *commit_path, *src_path, *path = fhd->path;
svn_revnum_t commit_rev, src_rev, dst_rev;
svn_revnum_t revision = fhd->revision;
apr_pool_t *retpool = args->pool;
svn_fs_t *fs = fhd->fs;
parent_path_t *parent_path;
dag_node_t *node;
svn_fs_root_t *root;
svn_boolean_t reported = fhd->is_interesting;
svn_revnum_t copyroot_rev;
const char *copyroot_path;
/* Initialize our return value. */
*prev_history = NULL;
/* If our last history report left us hints about where to pickup
the chase, then our last report was on the destination of a
copy. If we are crossing copies, start from those locations,
otherwise, we're all done here. */
if (fhd->path_hint && SVN_IS_VALID_REVNUM(fhd->rev_hint))
{
reported = FALSE;
if (! args->cross_copies)
return SVN_NO_ERROR;
path = fhd->path_hint;
revision = fhd->rev_hint;
}
/* Construct a ROOT for the current revision. */
SVN_ERR(svn_fs_fs__revision_root(&root, fs, revision, pool));
/* Open PATH/REVISION, and get its node and a bunch of other
goodies. */
SVN_ERR(open_path(&parent_path, root, path, 0, NULL, pool));
node = parent_path->node;
commit_path = svn_fs_fs__dag_get_created_path(node);
SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, pool));
/* The Subversion filesystem is written in such a way that a given
line of history may have at most one interesting history point
per filesystem revision. Either that node was edited (and
possibly copied), or it was copied but not edited. And a copy
source cannot be from the same revision as its destination. So,
if our history revision matches its node's commit revision, we
know that ... */
if (revision == commit_rev)
{
if (! reported)
{
/* ... we either have not yet reported on this revision (and
need now to do so) ... */
*prev_history = assemble_history(fs,
apr_pstrdup(retpool, commit_path),
commit_rev, TRUE, NULL,
SVN_INVALID_REVNUM, retpool);
return SVN_NO_ERROR;
}
else
{
/* ... or we *have* reported on this revision, and must now
progress toward this node's predecessor (unless there is
no predecessor, in which case we're all done!). */
const svn_fs_id_t *pred_id;
SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
if (! pred_id)
return SVN_NO_ERROR;
/* Replace NODE and friends with the information from its
predecessor. */
SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, pool));
commit_path = svn_fs_fs__dag_get_created_path(node);
SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, pool));
}
}
/* Find the youngest copyroot in the path of this node, including
itself. */
SVN_ERR(find_youngest_copyroot(&copyroot_rev, &copyroot_path, fs,
parent_path, pool));
/* Initialize some state variables. */
src_path = NULL;
src_rev = SVN_INVALID_REVNUM;
dst_rev = SVN_INVALID_REVNUM;
if (copyroot_rev > commit_rev)
{
const char *remainder_path;
const char *copy_dst, *copy_src;
svn_fs_root_t *copyroot_root;
SVN_ERR(svn_fs_fs__revision_root(&copyroot_root, fs, copyroot_rev,
pool));
- SVN_ERR(get_dag(&node, copyroot_root, copyroot_path, FALSE, pool));
+ SVN_ERR(get_dag(&node, copyroot_root, copyroot_path, pool));
copy_dst = svn_fs_fs__dag_get_created_path(node);
/* If our current path was the very destination of the copy,
then our new current path will be the copy source. If our
current path was instead the *child* of the destination of
the copy, then figure out its previous location by taking its
path relative to the copy destination and appending that to
the copy source. Finally, if our current path doesn't meet
one of these other criteria ... ### for now just fallback to
the old copy hunt algorithm. */
remainder_path = svn_fspath__skip_ancestor(copy_dst, path);
if (remainder_path)
{
/* If we get here, then our current path is the destination
of, or the child of the destination of, a copy. Fill
in the return values and get outta here. */
SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(&src_rev, node));
SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(&copy_src, node));
dst_rev = copyroot_rev;
src_path = svn_fspath__join(copy_src, remainder_path, pool);
}
}
/* If we calculated a copy source path and revision, we'll make a
'copy-style' history object. */
if (src_path && SVN_IS_VALID_REVNUM(src_rev))
{
svn_boolean_t retry = FALSE;
/* It's possible for us to find a copy location that is the same
as the history point we've just reported. If that happens,
we simply need to take another trip through this history
search. */
if ((dst_rev == revision) && reported)
retry = TRUE;
*prev_history = assemble_history(fs, apr_pstrdup(retpool, path),
dst_rev, ! retry,
src_path, src_rev, retpool);
}
else
{
*prev_history = assemble_history(fs, apr_pstrdup(retpool, commit_path),
commit_rev, TRUE, NULL,
SVN_INVALID_REVNUM, retpool);
}
return SVN_NO_ERROR;
}
/* Implement svn_fs_history_prev, set *PREV_HISTORY_P to a new
svn_fs_history_t object that represents the predecessory of
HISTORY. If CROSS_COPIES is true, *PREV_HISTORY_P may be related
only through a copy operation. Perform all allocations in POOL. */
static svn_error_t *
fs_history_prev(svn_fs_history_t **prev_history_p,
svn_fs_history_t *history,
svn_boolean_t cross_copies,
apr_pool_t *pool)
{
svn_fs_history_t *prev_history = NULL;
fs_history_data_t *fhd = history->fsap_data;
svn_fs_t *fs = fhd->fs;
/* Special case: the root directory changes in every single
revision, no exceptions. And, the root can't be the target (or
child of a target -- duh) of a copy. So, if that's our path,
then we need only decrement our revision by 1, and there you go. */
if (strcmp(fhd->path, "/") == 0)
{
if (! fhd->is_interesting)
prev_history = assemble_history(fs, "/", fhd->revision,
1, NULL, SVN_INVALID_REVNUM, pool);
else if (fhd->revision > 0)
prev_history = assemble_history(fs, "/", fhd->revision - 1,
1, NULL, SVN_INVALID_REVNUM, pool);
}
else
{
struct history_prev_args args;
prev_history = history;
while (1)
{
args.prev_history_p = &prev_history;
args.history = prev_history;
args.cross_copies = cross_copies;
args.pool = pool;
SVN_ERR(history_prev(&args, pool));
if (! prev_history)
break;
fhd = prev_history->fsap_data;
if (fhd->is_interesting)
break;
}
}
*prev_history_p = prev_history;
return SVN_NO_ERROR;
}
/* Set *PATH and *REVISION to the path and revision for the HISTORY
object. Use POOL for all allocations. */
static svn_error_t *
fs_history_location(const char **path,
svn_revnum_t *revision,
svn_fs_history_t *history,
apr_pool_t *pool)
{
fs_history_data_t *fhd = history->fsap_data;
*path = apr_pstrdup(pool, fhd->path);
*revision = fhd->revision;
return SVN_NO_ERROR;
}
static history_vtable_t history_vtable = {
fs_history_prev,
fs_history_location
};
/* Return a new history object (marked as "interesting") for PATH and
REVISION, allocated in POOL, and with its members set to the values
of the parameters provided. Note that PATH and PATH_HINT are not
duped into POOL -- it is the responsibility of the caller to ensure
that this happens. */
static svn_fs_history_t *
assemble_history(svn_fs_t *fs,
const char *path,
svn_revnum_t revision,
svn_boolean_t is_interesting,
const char *path_hint,
svn_revnum_t rev_hint,
apr_pool_t *pool)
{
svn_fs_history_t *history = apr_pcalloc(pool, sizeof(*history));
fs_history_data_t *fhd = apr_pcalloc(pool, sizeof(*fhd));
fhd->path = svn_fs__canonicalize_abspath(path, pool);
fhd->revision = revision;
fhd->is_interesting = is_interesting;
fhd->path_hint = path_hint;
fhd->rev_hint = rev_hint;
fhd->fs = fs;
history->vtable = &history_vtable;
history->fsap_data = fhd;
return history;
}
/* mergeinfo queries */
/* DIR_DAG is a directory DAG node which has mergeinfo in its
descendants. This function iterates over its children. For each
child with immediate mergeinfo, it adds its mergeinfo to
RESULT_CATALOG. appropriate arguments. For each child with
descendants with mergeinfo, it recurses. Note that it does *not*
call the action on the path for DIR_DAG itself.
POOL is used for temporary allocations, including the mergeinfo
hashes passed to actions; RESULT_POOL is used for the mergeinfo added
to RESULT_CATALOG.
*/
static svn_error_t *
crawl_directory_dag_for_mergeinfo(svn_fs_root_t *root,
const char *this_path,
dag_node_t *dir_dag,
svn_mergeinfo_catalog_t result_catalog,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_hash_t *entries;
apr_hash_index_t *hi;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, dir_dag,
scratch_pool));
for (hi = apr_hash_first(scratch_pool, entries);
hi;
hi = apr_hash_next(hi))
{
svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
const char *kid_path;
dag_node_t *kid_dag;
svn_boolean_t has_mergeinfo, go_down;
svn_pool_clear(iterpool);
kid_path = svn_fspath__join(this_path, dirent->name, iterpool);
- SVN_ERR(get_dag(&kid_dag, root, kid_path, TRUE, iterpool));
+ SVN_ERR(get_dag(&kid_dag, root, kid_path, iterpool));
SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo, kid_dag));
SVN_ERR(svn_fs_fs__dag_has_descendants_with_mergeinfo(&go_down, kid_dag));
if (has_mergeinfo)
{
/* Save this particular node's mergeinfo. */
apr_hash_t *proplist;
svn_mergeinfo_t kid_mergeinfo;
svn_string_t *mergeinfo_string;
svn_error_t *err;
SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, kid_dag, iterpool));
mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO);
if (!mergeinfo_string)
{
svn_string_t *idstr = svn_fs_fs__id_unparse(dirent->id, iterpool);
return svn_error_createf
(SVN_ERR_FS_CORRUPT, NULL,
_("Node-revision #'%s' claims to have mergeinfo but doesn't"),
idstr->data);
}
/* Issue #3896: If a node has syntactically invalid mergeinfo, then
treat it as if no mergeinfo is present rather than raising a parse
error. */
err = svn_mergeinfo_parse(&kid_mergeinfo,
mergeinfo_string->data,
result_pool);
if (err)
{
if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
svn_error_clear(err);
else
return svn_error_trace(err);
}
else
{
svn_hash_sets(result_catalog, apr_pstrdup(result_pool, kid_path),
kid_mergeinfo);
}
}
if (go_down)
SVN_ERR(crawl_directory_dag_for_mergeinfo(root,
kid_path,
kid_dag,
result_catalog,
result_pool,
iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Return the cache key as a combination of REV_ROOT->REV, the inheritance
flags INHERIT and ADJUST_INHERITED_MERGEINFO, and the PATH. The result
will be allocated in POOL..
*/
static const char *
mergeinfo_cache_key(const char *path,
svn_fs_root_t *rev_root,
svn_mergeinfo_inheritance_t inherit,
svn_boolean_t adjust_inherited_mergeinfo,
apr_pool_t *pool)
{
apr_int64_t number = rev_root->rev;
number = number * 4
+ (inherit == svn_mergeinfo_nearest_ancestor ? 2 : 0)
+ (adjust_inherited_mergeinfo ? 1 : 0);
return svn_fs_fs__combine_number_and_string(number, path, pool);
}
/* Calculates the mergeinfo for PATH under REV_ROOT using inheritance
type INHERIT. Returns it in *MERGEINFO, or NULL if there is none.
The result is allocated in RESULT_POOL; SCRATCH_POOL is
used for temporary allocations.
*/
static svn_error_t *
get_mergeinfo_for_path_internal(svn_mergeinfo_t *mergeinfo,
svn_fs_root_t *rev_root,
const char *path,
svn_mergeinfo_inheritance_t inherit,
svn_boolean_t adjust_inherited_mergeinfo,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
parent_path_t *parent_path, *nearest_ancestor;
apr_hash_t *proplist;
svn_string_t *mergeinfo_string;
path = svn_fs__canonicalize_abspath(path, scratch_pool);
SVN_ERR(open_path(&parent_path, rev_root, path, 0, NULL, scratch_pool));
if (inherit == svn_mergeinfo_nearest_ancestor && ! parent_path->parent)
return SVN_NO_ERROR;
if (inherit == svn_mergeinfo_nearest_ancestor)
nearest_ancestor = parent_path->parent;
else
nearest_ancestor = parent_path;
while (TRUE)
{
svn_boolean_t has_mergeinfo;
SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo,
nearest_ancestor->node));
if (has_mergeinfo)
break;
/* No need to loop if we're looking for explicit mergeinfo. */
if (inherit == svn_mergeinfo_explicit)
{
return SVN_NO_ERROR;
}
nearest_ancestor = nearest_ancestor->parent;
/* Run out? There's no mergeinfo. */
if (!nearest_ancestor)
{
return SVN_NO_ERROR;
}
}
SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, nearest_ancestor->node,
scratch_pool));
mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO);
if (!mergeinfo_string)
return svn_error_createf
(SVN_ERR_FS_CORRUPT, NULL,
_("Node-revision '%s@%ld' claims to have mergeinfo but doesn't"),
parent_path_path(nearest_ancestor, scratch_pool), rev_root->rev);
/* Parse the mergeinfo; store the result in *MERGEINFO. */
{
/* Issue #3896: If a node has syntactically invalid mergeinfo, then
treat it as if no mergeinfo is present rather than raising a parse
error. */
svn_error_t *err = svn_mergeinfo_parse(mergeinfo,
mergeinfo_string->data,
result_pool);
if (err)
{
if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
{
svn_error_clear(err);
err = NULL;
*mergeinfo = NULL;
}
return svn_error_trace(err);
}
}
/* If our nearest ancestor is the very path we inquired about, we
can return the mergeinfo results directly. Otherwise, we're
inheriting the mergeinfo, so we need to a) remove non-inheritable
ranges and b) telescope the merged-from paths. */
if (adjust_inherited_mergeinfo && (nearest_ancestor != parent_path))
{
svn_mergeinfo_t tmp_mergeinfo;
SVN_ERR(svn_mergeinfo_inheritable2(&tmp_mergeinfo, *mergeinfo,
NULL, SVN_INVALID_REVNUM,
SVN_INVALID_REVNUM, TRUE,
scratch_pool, scratch_pool));
SVN_ERR(svn_fs__append_to_merged_froms(mergeinfo, tmp_mergeinfo,
parent_path_relpath(
parent_path, nearest_ancestor,
scratch_pool),
result_pool));
}
return SVN_NO_ERROR;
}
/* Caching wrapper around get_mergeinfo_for_path_internal().
*/
static svn_error_t *
get_mergeinfo_for_path(svn_mergeinfo_t *mergeinfo,
svn_fs_root_t *rev_root,
const char *path,
svn_mergeinfo_inheritance_t inherit,
svn_boolean_t adjust_inherited_mergeinfo,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
fs_fs_data_t *ffd = rev_root->fs->fsap_data;
const char *cache_key;
svn_boolean_t found = FALSE;
svn_stringbuf_t *mergeinfo_exists;
*mergeinfo = NULL;
cache_key = mergeinfo_cache_key(path, rev_root, inherit,
adjust_inherited_mergeinfo, scratch_pool);
if (ffd->mergeinfo_existence_cache)
{
SVN_ERR(svn_cache__get((void **)&mergeinfo_exists, &found,
ffd->mergeinfo_existence_cache,
cache_key, result_pool));
if (found && mergeinfo_exists->data[0] == '1')
SVN_ERR(svn_cache__get((void **)mergeinfo, &found,
ffd->mergeinfo_cache,
cache_key, result_pool));
}
if (! found)
{
SVN_ERR(get_mergeinfo_for_path_internal(mergeinfo, rev_root, path,
inherit,
adjust_inherited_mergeinfo,
result_pool, scratch_pool));
if (ffd->mergeinfo_existence_cache)
{
mergeinfo_exists = svn_stringbuf_create(*mergeinfo ? "1" : "0",
scratch_pool);
SVN_ERR(svn_cache__set(ffd->mergeinfo_existence_cache,
cache_key, mergeinfo_exists, scratch_pool));
if (*mergeinfo)
SVN_ERR(svn_cache__set(ffd->mergeinfo_cache,
cache_key, *mergeinfo, scratch_pool));
}
}
return SVN_NO_ERROR;
}
/* Adds mergeinfo for each descendant of PATH (but not PATH itself)
under ROOT to RESULT_CATALOG. Returned values are allocated in
RESULT_POOL; temporary values in POOL. */
static svn_error_t *
add_descendant_mergeinfo(svn_mergeinfo_catalog_t result_catalog,
svn_fs_root_t *root,
const char *path,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
dag_node_t *this_dag;
svn_boolean_t go_down;
- SVN_ERR(get_dag(&this_dag, root, path, TRUE, scratch_pool));
+ SVN_ERR(get_dag(&this_dag, root, path, scratch_pool));
SVN_ERR(svn_fs_fs__dag_has_descendants_with_mergeinfo(&go_down,
this_dag));
if (go_down)
SVN_ERR(crawl_directory_dag_for_mergeinfo(root,
path,
this_dag,
result_catalog,
result_pool,
scratch_pool));
return SVN_NO_ERROR;
}
/* Get the mergeinfo for a set of paths, returned in
*MERGEINFO_CATALOG. Returned values are allocated in
POOL, while temporary values are allocated in a sub-pool. */
static svn_error_t *
get_mergeinfos_for_paths(svn_fs_root_t *root,
svn_mergeinfo_catalog_t *mergeinfo_catalog,
const apr_array_header_t *paths,
svn_mergeinfo_inheritance_t inherit,
svn_boolean_t include_descendants,
svn_boolean_t adjust_inherited_mergeinfo,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_mergeinfo_catalog_t result_catalog = svn_hash__make(result_pool);
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
int i;
for (i = 0; i < paths->nelts; i++)
{
svn_error_t *err;
svn_mergeinfo_t path_mergeinfo;
const char *path = APR_ARRAY_IDX(paths, i, const char *);
svn_pool_clear(iterpool);
err = get_mergeinfo_for_path(&path_mergeinfo, root, path,
inherit, adjust_inherited_mergeinfo,
result_pool, iterpool);
if (err)
{
if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
{
svn_error_clear(err);
err = NULL;
path_mergeinfo = NULL;
}
else
{
return svn_error_trace(err);
}
}
if (path_mergeinfo)
svn_hash_sets(result_catalog, path, path_mergeinfo);
if (include_descendants)
SVN_ERR(add_descendant_mergeinfo(result_catalog, root, path,
result_pool, scratch_pool));
}
svn_pool_destroy(iterpool);
*mergeinfo_catalog = result_catalog;
return SVN_NO_ERROR;
}
/* Implements svn_fs_get_mergeinfo. */
static svn_error_t *
fs_get_mergeinfo(svn_mergeinfo_catalog_t *catalog,
svn_fs_root_t *root,
const apr_array_header_t *paths,
svn_mergeinfo_inheritance_t inherit,
svn_boolean_t include_descendants,
svn_boolean_t adjust_inherited_mergeinfo,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
fs_fs_data_t *ffd = root->fs->fsap_data;
/* We require a revision root. */
if (root->is_txn_root)
return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
/* We have to actually be able to find the mergeinfo metadata! */
if (! svn_fs_fs__fs_supports_mergeinfo(root->fs))
return svn_error_createf
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Querying mergeinfo requires version %d of the FSFS filesystem "
"schema; filesystem '%s' uses only version %d"),
SVN_FS_FS__MIN_MERGEINFO_FORMAT, root->fs->path, ffd->format);
/* Retrieve a path -> mergeinfo hash mapping. */
return get_mergeinfos_for_paths(root, catalog, paths,
inherit,
include_descendants,
adjust_inherited_mergeinfo,
result_pool, scratch_pool);
}
/* The vtable associated with root objects. */
static root_vtable_t root_vtable = {
fs_paths_changed,
svn_fs_fs__check_path,
fs_node_history,
svn_fs_fs__node_id,
svn_fs_fs__node_created_rev,
fs_node_origin_rev,
fs_node_created_path,
fs_delete_node,
fs_copied_from,
fs_closest_copy,
fs_node_prop,
fs_node_proplist,
fs_change_node_prop,
fs_props_changed,
fs_dir_entries,
fs_make_dir,
fs_copy,
fs_revision_link,
fs_file_length,
fs_file_checksum,
fs_file_contents,
fs_try_process_file_contents,
fs_make_file,
fs_apply_textdelta,
fs_apply_text,
fs_contents_changed,
fs_get_file_delta_stream,
fs_merge,
fs_get_mergeinfo,
};
/* Construct a new root object in FS, allocated from POOL. */
static svn_fs_root_t *
make_root(svn_fs_t *fs,
apr_pool_t *pool)
{
svn_fs_root_t *root = apr_pcalloc(pool, sizeof(*root));
root->fs = fs;
root->pool = pool;
root->vtable = &root_vtable;
return root;
}
/* Construct a root object referring to the root of REVISION in FS,
whose root directory is ROOT_DIR. Create the new root in POOL. */
static svn_fs_root_t *
make_revision_root(svn_fs_t *fs,
svn_revnum_t rev,
dag_node_t *root_dir,
apr_pool_t *pool)
{
svn_fs_root_t *root = make_root(fs, pool);
fs_rev_root_data_t *frd = apr_pcalloc(root->pool, sizeof(*frd));
root->is_txn_root = FALSE;
root->rev = rev;
frd->root_dir = root_dir;
frd->copyfrom_cache = svn_hash__make(root->pool);
root->fsap_data = frd;
return root;
}
/* Construct a root object referring to the root of the transaction
named TXN and based on revision BASE_REV in FS, with FLAGS to
describe transaction's behavior. Create the new root in POOL. */
static svn_error_t *
make_txn_root(svn_fs_root_t **root_p,
svn_fs_t *fs,
const char *txn,
svn_revnum_t base_rev,
apr_uint32_t flags,
apr_pool_t *pool)
{
svn_fs_root_t *root = make_root(fs, pool);
fs_txn_root_data_t *frd = apr_pcalloc(root->pool, sizeof(*frd));
root->is_txn_root = TRUE;
root->txn = apr_pstrdup(root->pool, txn);
root->txn_flags = flags;
root->rev = base_rev;
frd->txn_id = txn;
/* Because this cache actually tries to invalidate elements, keep
the number of elements per page down.
Note that since dag_node_cache_invalidate uses svn_cache__iter,
this *cannot* be a memcache-based cache. */
SVN_ERR(svn_cache__create_inprocess(&(frd->txn_node_cache),
svn_fs_fs__dag_serialize,
svn_fs_fs__dag_deserialize,
APR_HASH_KEY_STRING,
32, 20, FALSE,
apr_pstrcat(pool, txn, ":TXN",
(char *)NULL),
root->pool));
/* Initialize transaction-local caches in FS.
Note that we cannot put those caches in frd because that content
fs root object is not available where we would need it. */
SVN_ERR(svn_fs_fs__initialize_txn_caches(fs, txn, pool));
root->fsap_data = frd;
*root_p = root;
return SVN_NO_ERROR;
}
/* Verify. */
static APR_INLINE const char *
stringify_node(dag_node_t *node,
apr_pool_t *pool)
{
/* ### TODO: print some PATH@REV to it, too. */
return svn_fs_fs__id_unparse(svn_fs_fs__dag_get_id(node), pool)->data;
}
/* Check metadata sanity on NODE, and on its children. Manually verify
information for DAG nodes in revision REV, and trust the metadata
accuracy for nodes belonging to older revisions. */
static svn_error_t *
verify_node(dag_node_t *node,
svn_revnum_t rev,
apr_pool_t *pool)
{
svn_boolean_t has_mergeinfo;
apr_int64_t mergeinfo_count;
const svn_fs_id_t *pred_id;
svn_fs_t *fs = svn_fs_fs__dag_get_fs(node);
int pred_count;
svn_node_kind_t kind;
apr_pool_t *iterpool = svn_pool_create(pool);
/* Fetch some data. */
SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo, node));
SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_count, node));
SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
SVN_ERR(svn_fs_fs__dag_get_predecessor_count(&pred_count, node));
kind = svn_fs_fs__dag_node_kind(node);
/* Sanity check. */
if (mergeinfo_count < 0)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
"Negative mergeinfo-count %" APR_INT64_T_FMT
" on node '%s'",
mergeinfo_count, stringify_node(node, iterpool));
/* Issue #4129. (This check will explicitly catch non-root instances too.) */
if (pred_id)
{
dag_node_t *pred;
int pred_pred_count;
SVN_ERR(svn_fs_fs__dag_get_node(&pred, fs, pred_id, iterpool));
SVN_ERR(svn_fs_fs__dag_get_predecessor_count(&pred_pred_count, pred));
if (pred_pred_count+1 != pred_count)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
"Predecessor count mismatch: "
"%s has %d, but %s has %d",
stringify_node(node, iterpool), pred_count,
stringify_node(pred, iterpool),
pred_pred_count);
}
/* Kind-dependent verifications. */
if (kind == svn_node_none)
{
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
"Node '%s' has kind 'none'",
stringify_node(node, iterpool));
}
if (kind == svn_node_file)
{
if (has_mergeinfo != mergeinfo_count) /* comparing int to bool */
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
"File node '%s' has inconsistent mergeinfo: "
"has_mergeinfo=%d, "
"mergeinfo_count=%" APR_INT64_T_FMT,
stringify_node(node, iterpool),
has_mergeinfo, mergeinfo_count);
}
if (kind == svn_node_dir)
{
apr_hash_t *entries;
apr_hash_index_t *hi;
apr_int64_t children_mergeinfo = 0;
SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, node, pool));
/* Compute CHILDREN_MERGEINFO. */
for (hi = apr_hash_first(pool, entries);
hi;
hi = apr_hash_next(hi))
{
svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
dag_node_t *child;
svn_revnum_t child_rev;
apr_int64_t child_mergeinfo;
svn_pool_clear(iterpool);
/* Compute CHILD_REV. */
SVN_ERR(svn_fs_fs__dag_get_node(&child, fs, dirent->id, iterpool));
SVN_ERR(svn_fs_fs__dag_get_revision(&child_rev, child, iterpool));
if (child_rev == rev)
SVN_ERR(verify_node(child, rev, iterpool));
SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&child_mergeinfo, child));
children_mergeinfo += child_mergeinfo;
}
/* Side-effect of issue #4129. */
if (children_mergeinfo+has_mergeinfo != mergeinfo_count)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
"Mergeinfo-count discrepancy on '%s': "
"expected %" APR_INT64_T_FMT "+%d, "
"counted %" APR_INT64_T_FMT,
stringify_node(node, iterpool),
mergeinfo_count, has_mergeinfo,
children_mergeinfo);
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__verify_root(svn_fs_root_t *root,
apr_pool_t *pool)
{
svn_fs_t *fs = root->fs;
dag_node_t *root_dir;
/* Issue #4129: bogus pred-counts and minfo-cnt's on the root node-rev
(and elsewhere). This code makes more thorough checks than the
commit-time checks in validate_root_noderev(). */
/* Callers should disable caches by setting SVN_FS_CONFIG_FSFS_CACHE_NS;
see r1462436.
When this code is called in the library, we want to ensure we
use the on-disk data --- rather than some data that was read
in the possibly-distance past and cached since. */
if (root->is_txn_root)
{
fs_txn_root_data_t *frd = root->fsap_data;
SVN_ERR(svn_fs_fs__dag_txn_root(&root_dir, fs, frd->txn_id, pool));
}
else
{
fs_rev_root_data_t *frd = root->fsap_data;
root_dir = frd->root_dir;
}
/* Recursively verify ROOT_DIR. */
SVN_ERR(verify_node(root_dir, root->rev, pool));
/* Verify explicitly the predecessor of the root. */
{
const svn_fs_id_t *pred_id;
/* Only r0 should have no predecessor. */
SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, root_dir));
if (! root->is_txn_root && !!pred_id != !!root->rev)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
"r%ld's root node's predecessor is "
"unexpectedly '%s'",
root->rev,
(pred_id
? svn_fs_fs__id_unparse(pred_id, pool)->data
: "(null)"));
if (root->is_txn_root && !pred_id)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
"Transaction '%s''s root node's predecessor is "
"unexpectedly NULL",
root->txn);
/* Check the predecessor's revision. */
if (pred_id)
{
svn_revnum_t pred_rev = svn_fs_fs__id_rev(pred_id);
if (! root->is_txn_root && pred_rev+1 != root->rev)
/* Issue #4129. */
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
"r%ld's root node's predecessor is r%ld"
" but should be r%ld",
root->rev, pred_rev, root->rev - 1);
if (root->is_txn_root && pred_rev != root->rev)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
"Transaction '%s''s root node's predecessor"
" is r%ld"
" but should be r%ld",
root->txn, pred_rev, root->rev);
}
}
return SVN_NO_ERROR;
}
Index: vendor/subversion/dist/subversion/libsvn_ra_serf/commit.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_ra_serf/commit.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_ra_serf/commit.c (revision 286501)
@@ -1,2522 +1,2531 @@
/*
* commit.c : entry point for commit RA functions for ra_serf
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include <apr_uri.h>
#include <serf.h>
#include "svn_hash.h"
#include "svn_pools.h"
#include "svn_ra.h"
#include "svn_dav.h"
#include "svn_xml.h"
#include "svn_config.h"
#include "svn_delta.h"
#include "svn_base64.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_props.h"
#include "svn_private_config.h"
#include "private/svn_dep_compat.h"
#include "private/svn_fspath.h"
#include "private/svn_skel.h"
#include "ra_serf.h"
#include "../libsvn_ra/ra_loader.h"
/* Baton passed back with the commit editor. */
typedef struct commit_context_t {
/* Pool for our commit. */
apr_pool_t *pool;
svn_ra_serf__session_t *session;
svn_ra_serf__connection_t *conn;
apr_hash_t *revprop_table;
svn_commit_callback2_t callback;
void *callback_baton;
apr_hash_t *lock_tokens;
svn_boolean_t keep_locks;
apr_hash_t *deleted_entries; /* deleted files (for delete+add detection) */
/* HTTP v2 stuff */
const char *txn_url; /* txn URL (!svn/txn/TXN_NAME) */
const char *txn_root_url; /* commit anchor txn root URL */
/* HTTP v1 stuff (only valid when 'txn_url' is NULL) */
const char *activity_url; /* activity base URL... */
const char *baseline_url; /* the working-baseline resource */
const char *checked_in_url; /* checked-in root to base CHECKOUTs from */
const char *vcc_url; /* vcc url */
} commit_context_t;
#define USING_HTTPV2_COMMIT_SUPPORT(commit_ctx) ((commit_ctx)->txn_url != NULL)
/* Structure associated with a PROPPATCH request. */
typedef struct proppatch_context_t {
apr_pool_t *pool;
const char *relpath;
const char *path;
commit_context_t *commit;
/* Changed and removed properties. */
apr_hash_t *changed_props;
apr_hash_t *removed_props;
/* Same, for the old value (*old_value_p). */
apr_hash_t *previous_changed_props;
apr_hash_t *previous_removed_props;
/* In HTTP v2, this is the file/directory version we think we're changing. */
svn_revnum_t base_revision;
} proppatch_context_t;
typedef struct delete_context_t {
const char *relpath;
svn_revnum_t revision;
commit_context_t *commit;
} delete_context_t;
/* Represents a directory. */
typedef struct dir_context_t {
/* Pool for our directory. */
apr_pool_t *pool;
/* The root commit we're in progress for. */
commit_context_t *commit;
/* URL to operate against (used for CHECKOUT and PROPPATCH before
HTTP v2, for PROPPATCH in HTTP v2). */
const char *url;
/* How many pending changes we have left in this directory. */
unsigned int ref_count;
/* Is this directory being added? (Otherwise, just opened.) */
svn_boolean_t added;
/* Our parent */
struct dir_context_t *parent_dir;
/* The directory name; if "", we're the 'root' */
const char *relpath;
/* The basename of the directory. "" for the 'root' */
const char *name;
/* The base revision of the dir. */
svn_revnum_t base_revision;
const char *copy_path;
svn_revnum_t copy_revision;
/* Changed and removed properties */
apr_hash_t *changed_props;
apr_hash_t *removed_props;
/* The checked-out working resource for this directory. May be NULL; if so
call checkout_dir() first. */
const char *working_url;
} dir_context_t;
/* Represents a file to be committed. */
typedef struct file_context_t {
/* Pool for our file. */
apr_pool_t *pool;
/* The root commit we're in progress for. */
commit_context_t *commit;
/* Is this file being added? (Otherwise, just opened.) */
svn_boolean_t added;
dir_context_t *parent_dir;
const char *relpath;
const char *name;
/* The checked-out working resource for this file. */
const char *working_url;
/* The base revision of the file. */
svn_revnum_t base_revision;
/* Copy path and revision */
const char *copy_path;
svn_revnum_t copy_revision;
/* stream */
svn_stream_t *stream;
/* Temporary file containing the svndiff. */
apr_file_t *svndiff;
/* Our base checksum as reported by the WC. */
const char *base_checksum;
/* Our resulting checksum as reported by the WC. */
const char *result_checksum;
/* Changed and removed properties. */
apr_hash_t *changed_props;
apr_hash_t *removed_props;
/* URL to PUT the file at. */
const char *url;
} file_context_t;
/* Setup routines and handlers for various requests we'll invoke. */
static svn_error_t *
return_response_err(svn_ra_serf__handler_t *handler)
{
svn_error_t *err;
/* We should have captured SLINE and LOCATION in the HANDLER. */
SVN_ERR_ASSERT(handler->handler_pool != NULL);
/* Ye Olde Fallback Error */
err = svn_error_compose_create(
handler->server_error != NULL
? handler->server_error->error
: SVN_NO_ERROR,
svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
_("%s of '%s': %d %s"),
handler->method, handler->path,
handler->sline.code, handler->sline.reason));
/* Try to return one of the standard errors for 301, 404, etc.,
then look for an error embedded in the response. */
return svn_error_compose_create(svn_ra_serf__error_on_status(
handler->sline,
handler->path,
handler->location),
err);
}
/* Implements svn_ra_serf__request_body_delegate_t */
static svn_error_t *
create_checkout_body(serf_bucket_t **bkt,
void *baton,
serf_bucket_alloc_t *alloc,
apr_pool_t *pool)
{
const char *activity_url = baton;
serf_bucket_t *body_bkt;
body_bkt = serf_bucket_aggregate_create(alloc);
svn_ra_serf__add_xml_header_buckets(body_bkt, alloc);
svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:checkout",
"xmlns:D", "DAV:",
NULL);
svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:activity-set", NULL);
svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:href", NULL);
SVN_ERR_ASSERT(activity_url != NULL);
svn_ra_serf__add_cdata_len_buckets(body_bkt, alloc,
activity_url,
strlen(activity_url));
svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:href");
svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:activity-set");
svn_ra_serf__add_tag_buckets(body_bkt, "D:apply-to-version", NULL, alloc);
svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:checkout");
*bkt = body_bkt;
return SVN_NO_ERROR;
}
/* Using the HTTPv1 protocol, perform a CHECKOUT of NODE_URL within the
given COMMIT_CTX. The resulting working resource will be returned in
*WORKING_URL, allocated from RESULT_POOL. All temporary allocations
are performed in SCRATCH_POOL.
### are these URLs actually repos relpath values? or fspath? or maybe
### the abspath portion of the full URL.
This function operates synchronously.
Strictly speaking, we could perform "all" of the CHECKOUT requests
when the commit starts, and only block when we need a specific
answer. Or, at a minimum, send off these individual requests async
and block when we need the answer (eg PUT or PROPPATCH).
However: the investment to speed this up is not worthwhile, given
that CHECKOUT (and the related round trip) is completely obviated
in HTTPv2.
*/
static svn_error_t *
checkout_node(const char **working_url,
const commit_context_t *commit_ctx,
const char *node_url,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_ra_serf__handler_t handler = { 0 };
apr_status_t status;
apr_uri_t uri;
/* HANDLER_POOL is the scratch pool since we don't need to remember
anything from the handler. We just want the working resource. */
handler.handler_pool = scratch_pool;
handler.session = commit_ctx->session;
handler.conn = commit_ctx->conn;
handler.body_delegate = create_checkout_body;
handler.body_delegate_baton = (/* const */ void *)commit_ctx->activity_url;
handler.body_type = "text/xml";
handler.response_handler = svn_ra_serf__expect_empty_body;
handler.response_baton = &handler;
handler.method = "CHECKOUT";
handler.path = node_url;
SVN_ERR(svn_ra_serf__context_run_one(&handler, scratch_pool));
if (handler.sline.code != 201)
return svn_error_trace(return_response_err(&handler));
if (handler.location == NULL)
return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
_("No Location header received"));
/* We only want the path portion of the Location header.
(code.google.com sometimes returns an 'http:' scheme for an
'https:' transaction ... we'll work around that by stripping the
scheme, host, and port here and re-adding the correct ones
later. */
status = apr_uri_parse(scratch_pool, handler.location, &uri);
if (status)
return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
_("Error parsing Location header value"));
*working_url = svn_urlpath__canonicalize(uri.path, result_pool);
return SVN_NO_ERROR;
}
/* This is a wrapper around checkout_node() (which see for
documentation) which simply retries the CHECKOUT request when it
fails due to an SVN_ERR_APMOD_BAD_BASELINE error return from the
server.
See http://subversion.tigris.org/issues/show_bug.cgi?id=4127 for
details.
*/
static svn_error_t *
retry_checkout_node(const char **working_url,
const commit_context_t *commit_ctx,
const char *node_url,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_error_t *err = SVN_NO_ERROR;
int retry_count = 5; /* Magic, arbitrary number. */
do
{
svn_error_clear(err);
err = checkout_node(working_url, commit_ctx, node_url,
result_pool, scratch_pool);
/* There's a small chance of a race condition here if Apache is
experiencing heavy commit concurrency or if the network has
long latency. It's possible that the value of HEAD changed
between the time we fetched the latest baseline and the time
we try to CHECKOUT that baseline. If that happens, Apache
will throw us a BAD_BASELINE error (deltaV says you can only
checkout the latest baseline). We just ignore that specific
error and retry a few times, asking for the latest baseline
again. */
if (err && (err->apr_err != SVN_ERR_APMOD_BAD_BASELINE))
return err;
}
while (err && retry_count--);
return err;
}
static svn_error_t *
checkout_dir(dir_context_t *dir,
apr_pool_t *scratch_pool)
{
svn_error_t *err;
dir_context_t *p_dir = dir;
const char *checkout_url;
const char **working;
if (dir->working_url)
{
return SVN_NO_ERROR;
}
/* Is this directory or one of our parent dirs newly added?
* If so, we're already implicitly checked out. */
while (p_dir)
{
if (p_dir->added)
{
/* Calculate the working_url by skipping the shared ancestor bewteen
* the parent->relpath and dir->relpath. This is safe since an
* add is guaranteed to have a parent that is checked out. */
dir_context_t *parent = p_dir->parent_dir;
const char *relpath = svn_relpath_skip_ancestor(parent->relpath,
dir->relpath);
/* Implicitly checkout this dir now. */
SVN_ERR_ASSERT(parent->working_url);
dir->working_url = svn_path_url_add_component2(
parent->working_url,
relpath, dir->pool);
return SVN_NO_ERROR;
}
p_dir = p_dir->parent_dir;
}
/* We could be called twice for the root: once to checkout the baseline;
* once to checkout the directory itself if we need to do so.
* Note: CHECKOUT_URL should live longer than HANDLER.
*/
if (!dir->parent_dir && !dir->commit->baseline_url)
{
checkout_url = dir->commit->vcc_url;
working = &dir->commit->baseline_url;
}
else
{
checkout_url = dir->url;
working = &dir->working_url;
}
/* Checkout our directory into the activity URL now. */
err = retry_checkout_node(working, dir->commit, checkout_url,
dir->pool, scratch_pool);
if (err)
{
if (err->apr_err == SVN_ERR_FS_CONFLICT)
SVN_ERR_W(err, apr_psprintf(scratch_pool,
_("Directory '%s' is out of date; try updating"),
svn_dirent_local_style(dir->relpath, scratch_pool)));
return err;
}
return SVN_NO_ERROR;
}
/* Set *CHECKED_IN_URL to the appropriate DAV version url for
* RELPATH (relative to the root of SESSION).
*
* Try to find this version url in three ways:
* First, if SESSION->callbacks->get_wc_prop() is defined, try to read the
* version url from the working copy properties.
* Second, if the version url of the parent directory PARENT_VSN_URL is
* defined, set *CHECKED_IN_URL to the concatenation of PARENT_VSN_URL with
* RELPATH.
* Else, fetch the version url for the root of SESSION using CONN and
* BASE_REVISION, and set *CHECKED_IN_URL to the concatenation of that
* with RELPATH.
*
* Allocate the result in RESULT_POOL, and use SCRATCH_POOL for
* temporary allocation.
*/
static svn_error_t *
get_version_url(const char **checked_in_url,
svn_ra_serf__session_t *session,
const char *relpath,
svn_revnum_t base_revision,
const char *parent_vsn_url,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *root_checkout;
if (session->wc_callbacks->get_wc_prop)
{
const svn_string_t *current_version;
SVN_ERR(session->wc_callbacks->get_wc_prop(
session->wc_callback_baton,
relpath,
SVN_RA_SERF__WC_CHECKED_IN_URL,
&current_version, scratch_pool));
if (current_version)
{
*checked_in_url =
svn_urlpath__canonicalize(current_version->data, result_pool);
return SVN_NO_ERROR;
}
}
if (parent_vsn_url)
{
root_checkout = parent_vsn_url;
}
else
{
const char *propfind_url;
svn_ra_serf__connection_t *conn = session->conns[0];
if (SVN_IS_VALID_REVNUM(base_revision))
{
/* mod_dav_svn can't handle the "Label:" header that
svn_ra_serf__deliver_props() is going to try to use for
this lookup, so we'll do things the hard(er) way, by
looking up the version URL from a resource in the
baseline collection. */
/* ### conn==NULL for session->conns[0]. same as CONN. */
SVN_ERR(svn_ra_serf__get_stable_url(&propfind_url,
NULL /* latest_revnum */,
session, NULL /* conn */,
NULL /* url */, base_revision,
scratch_pool, scratch_pool));
}
else
{
propfind_url = session->session_url.path;
}
SVN_ERR(svn_ra_serf__fetch_dav_prop(&root_checkout,
conn, propfind_url, base_revision,
"checked-in",
scratch_pool, scratch_pool));
if (!root_checkout)
return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
_("Path '%s' not present"),
session->session_url.path);
root_checkout = svn_urlpath__canonicalize(root_checkout, scratch_pool);
}
*checked_in_url = svn_path_url_add_component2(root_checkout, relpath,
result_pool);
return SVN_NO_ERROR;
}
static svn_error_t *
checkout_file(file_context_t *file,
apr_pool_t *scratch_pool)
{
svn_error_t *err;
dir_context_t *parent_dir = file->parent_dir;
const char *checkout_url;
/* Is one of our parent dirs newly added? If so, we're already
* implicitly checked out.
*/
while (parent_dir)
{
if (parent_dir->added)
{
/* Implicitly checkout this file now. */
file->working_url = svn_path_url_add_component2(
parent_dir->working_url,
svn_relpath_skip_ancestor(
parent_dir->relpath, file->relpath),
file->pool);
return SVN_NO_ERROR;
}
parent_dir = parent_dir->parent_dir;
}
SVN_ERR(get_version_url(&checkout_url,
file->commit->session,
file->relpath, file->base_revision,
NULL, scratch_pool, scratch_pool));
/* Checkout our file into the activity URL now. */
err = retry_checkout_node(&file->working_url, file->commit, checkout_url,
file->pool, scratch_pool);
if (err)
{
if (err->apr_err == SVN_ERR_FS_CONFLICT)
SVN_ERR_W(err, apr_psprintf(scratch_pool,
_("File '%s' is out of date; try updating"),
svn_dirent_local_style(file->relpath, scratch_pool)));
return err;
}
return SVN_NO_ERROR;
}
/* Helper function for proppatch_walker() below. */
static svn_error_t *
get_encoding_and_cdata(const char **encoding_p,
const svn_string_t **encoded_value_p,
serf_bucket_alloc_t *alloc,
const svn_string_t *value,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
if (value == NULL)
{
*encoding_p = NULL;
*encoded_value_p = NULL;
return SVN_NO_ERROR;
}
/* If a property is XML-safe, XML-encode it. Else, base64-encode
it. */
if (svn_xml_is_xml_safe(value->data, value->len))
{
svn_stringbuf_t *xml_esc = NULL;
svn_xml_escape_cdata_string(&xml_esc, value, scratch_pool);
*encoding_p = NULL;
*encoded_value_p = svn_string_create_from_buf(xml_esc, result_pool);
}
else
{
*encoding_p = "base64";
*encoded_value_p = svn_base64_encode_string2(value, TRUE, result_pool);
}
return SVN_NO_ERROR;
}
typedef struct walker_baton_t {
serf_bucket_t *body_bkt;
apr_pool_t *body_pool;
apr_hash_t *previous_changed_props;
apr_hash_t *previous_removed_props;
const char *path;
/* Hack, since change_rev_prop(old_value_p != NULL, value = NULL) uses D:set
rather than D:remove... (see notes/http-and-webdav/webdav-protocol) */
enum {
filter_all_props,
filter_props_with_old_value,
filter_props_without_old_value
} filter;
/* Is the property being deleted? */
svn_boolean_t deleting;
} walker_baton_t;
/* If we have (recorded in WB) the old value of the property named NS:NAME,
* then set *HAVE_OLD_VAL to TRUE and set *OLD_VAL_P to that old value
* (which may be NULL); else set *HAVE_OLD_VAL to FALSE. */
static svn_error_t *
derive_old_val(svn_boolean_t *have_old_val,
const svn_string_t **old_val_p,
walker_baton_t *wb,
const char *ns,
const char *name)
{
*have_old_val = FALSE;
if (wb->previous_changed_props)
{
const svn_string_t *val;
val = svn_ra_serf__get_prop_string(wb->previous_changed_props,
wb->path, ns, name);
if (val)
{
*have_old_val = TRUE;
*old_val_p = val;
}
}
if (wb->previous_removed_props)
{
const svn_string_t *val;
val = svn_ra_serf__get_prop_string(wb->previous_removed_props,
wb->path, ns, name);
if (val)
{
*have_old_val = TRUE;
*old_val_p = NULL;
}
}
return SVN_NO_ERROR;
}
static svn_error_t *
proppatch_walker(void *baton,
const char *ns,
const char *name,
const svn_string_t *val,
apr_pool_t *scratch_pool)
{
walker_baton_t *wb = baton;
serf_bucket_t *body_bkt = wb->body_bkt;
serf_bucket_t *cdata_bkt;
serf_bucket_alloc_t *alloc;
const char *encoding;
svn_boolean_t have_old_val;
const svn_string_t *old_val;
const svn_string_t *encoded_value;
const char *prop_name;
SVN_ERR(derive_old_val(&have_old_val, &old_val, wb, ns, name));
/* Jump through hoops to work with D:remove and its val = (""-for-NULL)
* representation. */
if (wb->filter != filter_all_props)
{
if (wb->filter == filter_props_with_old_value && ! have_old_val)
return SVN_NO_ERROR;
if (wb->filter == filter_props_without_old_value && have_old_val)
return SVN_NO_ERROR;
}
if (wb->deleting)
val = NULL;
alloc = body_bkt->allocator;
SVN_ERR(get_encoding_and_cdata(&encoding, &encoded_value, alloc, val,
wb->body_pool, scratch_pool));
if (encoded_value)
{
cdata_bkt = SERF_BUCKET_SIMPLE_STRING_LEN(encoded_value->data,
encoded_value->len,
alloc);
}
else
{
cdata_bkt = NULL;
}
/* Use the namespace prefix instead of adding the xmlns attribute to support
property names containing ':' */
if (strcmp(ns, SVN_DAV_PROP_NS_SVN) == 0)
prop_name = apr_pstrcat(wb->body_pool, "S:", name, (char *)NULL);
else if (strcmp(ns, SVN_DAV_PROP_NS_CUSTOM) == 0)
prop_name = apr_pstrcat(wb->body_pool, "C:", name, (char *)NULL);
if (cdata_bkt)
svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, prop_name,
"V:encoding", encoding,
NULL);
else
svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, prop_name,
"V:" SVN_DAV__OLD_VALUE__ABSENT, "1",
NULL);
if (have_old_val)
{
const char *encoding2;
const svn_string_t *encoded_value2;
serf_bucket_t *cdata_bkt2;
SVN_ERR(get_encoding_and_cdata(&encoding2, &encoded_value2,
alloc, old_val,
wb->body_pool, scratch_pool));
if (encoded_value2)
{
cdata_bkt2 = SERF_BUCKET_SIMPLE_STRING_LEN(encoded_value2->data,
encoded_value2->len,
alloc);
}
else
{
cdata_bkt2 = NULL;
}
if (cdata_bkt2)
svn_ra_serf__add_open_tag_buckets(body_bkt, alloc,
"V:" SVN_DAV__OLD_VALUE,
"V:encoding", encoding2,
NULL);
else
svn_ra_serf__add_open_tag_buckets(body_bkt, alloc,
"V:" SVN_DAV__OLD_VALUE,
"V:" SVN_DAV__OLD_VALUE__ABSENT, "1",
NULL);
if (cdata_bkt2)
serf_bucket_aggregate_append(body_bkt, cdata_bkt2);
svn_ra_serf__add_close_tag_buckets(body_bkt, alloc,
"V:" SVN_DAV__OLD_VALUE);
}
if (cdata_bkt)
serf_bucket_aggregate_append(body_bkt, cdata_bkt);
svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, prop_name);
return SVN_NO_ERROR;
}
/* Possible add the lock-token "If:" precondition header to HEADERS if
an examination of COMMIT_CTX and RELPATH indicates that this is the
right thing to do.
Generally speaking, if the client provided a lock token for
RELPATH, it's the right thing to do. There is a notable instance
where this is not the case, however. If the file at RELPATH was
explicitly deleted in this commit already, then mod_dav removed its
lock token when it fielded the DELETE request, so we don't want to
set the lock precondition again. (See
http://subversion.tigris.org/issues/show_bug.cgi?id=3674 for details.)
*/
static svn_error_t *
maybe_set_lock_token_header(serf_bucket_t *headers,
commit_context_t *commit_ctx,
const char *relpath,
apr_pool_t *pool)
{
const char *token;
if (! (relpath && commit_ctx->lock_tokens))
return SVN_NO_ERROR;
if (! svn_hash_gets(commit_ctx->deleted_entries, relpath))
{
token = svn_hash_gets(commit_ctx->lock_tokens, relpath);
if (token)
{
const char *token_header;
const char *token_uri;
apr_uri_t uri = commit_ctx->session->session_url;
/* Supplying the optional URI affects apache response when
the lock is broken, see issue 4369. When present any URI
must be absolute (RFC 2518 9.4). */
uri.path = (char *)svn_path_url_add_component2(uri.path, relpath,
pool);
token_uri = apr_uri_unparse(pool, &uri, 0);
token_header = apr_pstrcat(pool, "<", token_uri, "> (<", token, ">)",
(char *)NULL);
serf_bucket_headers_set(headers, "If", token_header);
}
}
return SVN_NO_ERROR;
}
static svn_error_t *
setup_proppatch_headers(serf_bucket_t *headers,
void *baton,
apr_pool_t *pool)
{
proppatch_context_t *proppatch = baton;
if (SVN_IS_VALID_REVNUM(proppatch->base_revision))
{
serf_bucket_headers_set(headers, SVN_DAV_VERSION_NAME_HEADER,
apr_psprintf(pool, "%ld",
proppatch->base_revision));
}
SVN_ERR(maybe_set_lock_token_header(headers, proppatch->commit,
proppatch->relpath, pool));
return SVN_NO_ERROR;
}
struct proppatch_body_baton_t {
proppatch_context_t *proppatch;
/* Content in the body should be allocated here, to live long enough. */
apr_pool_t *body_pool;
};
/* Implements svn_ra_serf__request_body_delegate_t */
static svn_error_t *
create_proppatch_body(serf_bucket_t **bkt,
void *baton,
serf_bucket_alloc_t *alloc,
apr_pool_t *scratch_pool)
{
struct proppatch_body_baton_t *pbb = baton;
proppatch_context_t *ctx = pbb->proppatch;
serf_bucket_t *body_bkt;
walker_baton_t wb = { 0 };
body_bkt = serf_bucket_aggregate_create(alloc);
svn_ra_serf__add_xml_header_buckets(body_bkt, alloc);
svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:propertyupdate",
"xmlns:D", "DAV:",
"xmlns:V", SVN_DAV_PROP_NS_DAV,
"xmlns:C", SVN_DAV_PROP_NS_CUSTOM,
"xmlns:S", SVN_DAV_PROP_NS_SVN,
NULL);
wb.body_bkt = body_bkt;
wb.body_pool = pbb->body_pool;
wb.previous_changed_props = ctx->previous_changed_props;
wb.previous_removed_props = ctx->previous_removed_props;
wb.path = ctx->path;
if (apr_hash_count(ctx->changed_props) > 0)
{
svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:set", NULL);
svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:prop", NULL);
wb.filter = filter_all_props;
wb.deleting = FALSE;
SVN_ERR(svn_ra_serf__walk_all_props(ctx->changed_props, ctx->path,
SVN_INVALID_REVNUM,
proppatch_walker, &wb,
scratch_pool));
svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:prop");
svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:set");
}
if (apr_hash_count(ctx->removed_props) > 0)
{
svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:set", NULL);
svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:prop", NULL);
wb.filter = filter_props_with_old_value;
wb.deleting = TRUE;
SVN_ERR(svn_ra_serf__walk_all_props(ctx->removed_props, ctx->path,
SVN_INVALID_REVNUM,
proppatch_walker, &wb,
scratch_pool));
svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:prop");
svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:set");
}
if (apr_hash_count(ctx->removed_props) > 0)
{
svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:remove", NULL);
svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:prop", NULL);
wb.filter = filter_props_without_old_value;
wb.deleting = TRUE;
SVN_ERR(svn_ra_serf__walk_all_props(ctx->removed_props, ctx->path,
SVN_INVALID_REVNUM,
proppatch_walker, &wb,
scratch_pool));
svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:prop");
svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:remove");
}
svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:propertyupdate");
*bkt = body_bkt;
return SVN_NO_ERROR;
}
static svn_error_t*
proppatch_resource(proppatch_context_t *proppatch,
commit_context_t *commit,
apr_pool_t *pool)
{
svn_ra_serf__handler_t *handler;
struct proppatch_body_baton_t pbb;
handler = apr_pcalloc(pool, sizeof(*handler));
handler->handler_pool = pool;
handler->method = "PROPPATCH";
handler->path = proppatch->path;
handler->conn = commit->conn;
handler->session = commit->session;
handler->header_delegate = setup_proppatch_headers;
handler->header_delegate_baton = proppatch;
pbb.proppatch = proppatch;
pbb.body_pool = pool;
handler->body_delegate = create_proppatch_body;
handler->body_delegate_baton = &pbb;
handler->response_handler = svn_ra_serf__handle_multistatus_only;
handler->response_baton = handler;
SVN_ERR(svn_ra_serf__context_run_one(handler, pool));
if (handler->sline.code != 207
|| (handler->server_error != NULL
&& handler->server_error->error != NULL))
{
return svn_error_create(
SVN_ERR_RA_DAV_PROPPATCH_FAILED,
return_response_err(handler),
_("At least one property change failed; repository"
" is unchanged"));
}
return SVN_NO_ERROR;
}
/* Implements svn_ra_serf__request_body_delegate_t */
static svn_error_t *
create_put_body(serf_bucket_t **body_bkt,
void *baton,
serf_bucket_alloc_t *alloc,
apr_pool_t *pool)
{
file_context_t *ctx = baton;
apr_off_t offset;
/* We need to flush the file, make it unbuffered (so that it can be
* zero-copied via mmap), and reset the position before attempting to
* deliver the file.
*
* N.B. If we have APR 1.3+, we can unbuffer the file to let us use mmap
* and zero-copy the PUT body. However, on older APR versions, we can't
* check the buffer status; but serf will fall through and create a file
* bucket for us on the buffered svndiff handle.
*/
apr_file_flush(ctx->svndiff);
#if APR_VERSION_AT_LEAST(1, 3, 0)
apr_file_buffer_set(ctx->svndiff, NULL, 0);
#endif
offset = 0;
apr_file_seek(ctx->svndiff, APR_SET, &offset);
*body_bkt = serf_bucket_file_create(ctx->svndiff, alloc);
return SVN_NO_ERROR;
}
/* Implements svn_ra_serf__request_body_delegate_t */
static svn_error_t *
create_empty_put_body(serf_bucket_t **body_bkt,
void *baton,
serf_bucket_alloc_t *alloc,
apr_pool_t *pool)
{
*body_bkt = SERF_BUCKET_SIMPLE_STRING("", alloc);
return SVN_NO_ERROR;
}
static svn_error_t *
setup_put_headers(serf_bucket_t *headers,
void *baton,
apr_pool_t *pool)
{
file_context_t *ctx = baton;
if (SVN_IS_VALID_REVNUM(ctx->base_revision))
{
serf_bucket_headers_set(headers, SVN_DAV_VERSION_NAME_HEADER,
apr_psprintf(pool, "%ld", ctx->base_revision));
}
if (ctx->base_checksum)
{
serf_bucket_headers_set(headers, SVN_DAV_BASE_FULLTEXT_MD5_HEADER,
ctx->base_checksum);
}
if (ctx->result_checksum)
{
serf_bucket_headers_set(headers, SVN_DAV_RESULT_FULLTEXT_MD5_HEADER,
ctx->result_checksum);
}
SVN_ERR(maybe_set_lock_token_header(headers, ctx->commit,
ctx->relpath, pool));
return APR_SUCCESS;
}
static svn_error_t *
setup_copy_file_headers(serf_bucket_t *headers,
void *baton,
apr_pool_t *pool)
{
file_context_t *file = baton;
apr_uri_t uri;
const char *absolute_uri;
/* The Dest URI must be absolute. Bummer. */
uri = file->commit->session->session_url;
uri.path = (char*)file->url;
absolute_uri = apr_uri_unparse(pool, &uri, 0);
serf_bucket_headers_set(headers, "Destination", absolute_uri);
serf_bucket_headers_setn(headers, "Depth", "0");
serf_bucket_headers_setn(headers, "Overwrite", "T");
return SVN_NO_ERROR;
}
static svn_error_t *
setup_if_header_recursive(svn_boolean_t *added,
serf_bucket_t *headers,
commit_context_t *commit_ctx,
const char *rq_relpath,
apr_pool_t *pool)
{
svn_stringbuf_t *sb = NULL;
apr_hash_index_t *hi;
apr_pool_t *iterpool = NULL;
if (!commit_ctx->lock_tokens)
{
*added = FALSE;
return SVN_NO_ERROR;
}
/* We try to create a directory, so within the Subversion world that
would imply that there is nothing here, but mod_dav_svn still sees
locks on the old nodes here as in DAV it is perfectly legal to lock
something that is not there...
Let's make mod_dav, mod_dav_svn and the DAV RFC happy by providing
the locks we know of with the request */
for (hi = apr_hash_first(pool, commit_ctx->lock_tokens);
hi;
hi = apr_hash_next(hi))
{
const char *relpath = svn__apr_hash_index_key(hi);
apr_uri_t uri;
if (!svn_relpath_skip_ancestor(rq_relpath, relpath))
continue;
else if (svn_hash_gets(commit_ctx->deleted_entries, relpath))
{
/* When a path is already explicit deleted then its lock
will be removed by mod_dav. But mod_dav doesn't remove
locks on descendants */
continue;
}
if (!iterpool)
iterpool = svn_pool_create(pool);
else
svn_pool_clear(iterpool);
if (sb == NULL)
sb = svn_stringbuf_create("", pool);
else
svn_stringbuf_appendbyte(sb, ' ');
uri = commit_ctx->session->session_url;
uri.path = (char *)svn_path_url_add_component2(uri.path, relpath,
iterpool);
svn_stringbuf_appendbyte(sb, '<');
svn_stringbuf_appendcstr(sb, apr_uri_unparse(iterpool, &uri, 0));
svn_stringbuf_appendcstr(sb, "> (<");
svn_stringbuf_appendcstr(sb, svn__apr_hash_index_val(hi));
svn_stringbuf_appendcstr(sb, ">)");
}
if (iterpool)
svn_pool_destroy(iterpool);
if (sb)
{
serf_bucket_headers_set(headers, "If", sb->data);
*added = TRUE;
}
else
*added = FALSE;
return SVN_NO_ERROR;
}
static svn_error_t *
setup_add_dir_common_headers(serf_bucket_t *headers,
void *baton,
apr_pool_t *pool)
{
dir_context_t *dir = baton;
svn_boolean_t added;
return svn_error_trace(
setup_if_header_recursive(&added, headers, dir->commit, dir->relpath,
pool));
}
static svn_error_t *
setup_copy_dir_headers(serf_bucket_t *headers,
void *baton,
apr_pool_t *pool)
{
dir_context_t *dir = baton;
apr_uri_t uri;
const char *absolute_uri;
/* The Dest URI must be absolute. Bummer. */
uri = dir->commit->session->session_url;
if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit))
{
uri.path = (char *)dir->url;
}
else
{
uri.path = (char *)svn_path_url_add_component2(
dir->parent_dir->working_url,
dir->name, pool);
}
absolute_uri = apr_uri_unparse(pool, &uri, 0);
serf_bucket_headers_set(headers, "Destination", absolute_uri);
serf_bucket_headers_setn(headers, "Depth", "infinity");
serf_bucket_headers_setn(headers, "Overwrite", "T");
/* Implicitly checkout this dir now. */
dir->working_url = apr_pstrdup(dir->pool, uri.path);
return svn_error_trace(setup_add_dir_common_headers(headers, baton, pool));
}
static svn_error_t *
setup_delete_headers(serf_bucket_t *headers,
void *baton,
apr_pool_t *pool)
{
delete_context_t *del = baton;
svn_boolean_t added;
serf_bucket_headers_set(headers, SVN_DAV_VERSION_NAME_HEADER,
apr_ltoa(pool, del->revision));
SVN_ERR(setup_if_header_recursive(&added, headers, del->commit,
del->relpath, pool));
if (added && del->commit->keep_locks)
serf_bucket_headers_setn(headers, SVN_DAV_OPTIONS_HEADER,
SVN_DAV_OPTION_KEEP_LOCKS);
return SVN_NO_ERROR;
}
/* Helper function to write the svndiff stream to temporary file. */
static svn_error_t *
svndiff_stream_write(void *file_baton,
const char *data,
apr_size_t *len)
{
file_context_t *ctx = file_baton;
apr_status_t status;
status = apr_file_write_full(ctx->svndiff, data, *len, NULL);
if (status)
return svn_error_wrap_apr(status, _("Failed writing updated file"));
return SVN_NO_ERROR;
}
/* POST against 'me' resource handlers. */
/* Implements svn_ra_serf__request_body_delegate_t */
static svn_error_t *
create_txn_post_body(serf_bucket_t **body_bkt,
void *baton,
serf_bucket_alloc_t *alloc,
apr_pool_t *pool)
{
apr_hash_t *revprops = baton;
svn_skel_t *request_skel;
svn_stringbuf_t *skel_str;
request_skel = svn_skel__make_empty_list(pool);
if (revprops)
{
svn_skel_t *proplist_skel;
SVN_ERR(svn_skel__unparse_proplist(&proplist_skel, revprops, pool));
svn_skel__prepend(proplist_skel, request_skel);
svn_skel__prepend_str("create-txn-with-props", request_skel, pool);
skel_str = svn_skel__unparse(request_skel, pool);
*body_bkt = SERF_BUCKET_SIMPLE_STRING(skel_str->data, alloc);
}
else
{
*body_bkt = SERF_BUCKET_SIMPLE_STRING("( create-txn )", alloc);
}
return SVN_NO_ERROR;
}
/* Implements svn_ra_serf__request_header_delegate_t */
static svn_error_t *
setup_post_headers(serf_bucket_t *headers,
void *baton,
apr_pool_t *pool)
{
#ifdef SVN_DAV_SEND_VTXN_NAME
/* Enable this to exercise the VTXN-NAME code based on a client
supplied transaction name. */
serf_bucket_headers_set(headers, SVN_DAV_VTXN_NAME_HEADER,
svn_uuid_generate(pool));
#endif
return SVN_NO_ERROR;
}
/* Handler baton for POST request. */
typedef struct post_response_ctx_t
{
svn_ra_serf__handler_t *handler;
commit_context_t *commit_ctx;
} post_response_ctx_t;
/* This implements serf_bucket_headers_do_callback_fn_t. */
static int
post_headers_iterator_callback(void *baton,
const char *key,
const char *val)
{
post_response_ctx_t *prc = baton;
commit_context_t *prc_cc = prc->commit_ctx;
svn_ra_serf__session_t *sess = prc_cc->session;
/* If we provided a UUID to the POST request, we should get back
from the server an SVN_DAV_VTXN_NAME_HEADER header; otherwise we
expect the SVN_DAV_TXN_NAME_HEADER. We certainly don't expect to
see both. */
if (svn_cstring_casecmp(key, SVN_DAV_TXN_NAME_HEADER) == 0)
{
/* Build out txn and txn-root URLs using the txn name we're
given, and store the whole lot of it in the commit context. */
prc_cc->txn_url =
svn_path_url_add_component2(sess->txn_stub, val, prc_cc->pool);
prc_cc->txn_root_url =
svn_path_url_add_component2(sess->txn_root_stub, val, prc_cc->pool);
}
if (svn_cstring_casecmp(key, SVN_DAV_VTXN_NAME_HEADER) == 0)
{
/* Build out vtxn and vtxn-root URLs using the vtxn name we're
given, and store the whole lot of it in the commit context. */
prc_cc->txn_url =
svn_path_url_add_component2(sess->vtxn_stub, val, prc_cc->pool);
prc_cc->txn_root_url =
svn_path_url_add_component2(sess->vtxn_root_stub, val, prc_cc->pool);
}
return 0;
}
/* A custom serf_response_handler_t which is mostly a wrapper around
svn_ra_serf__expect_empty_body -- it just notices POST response
headers, too.
Implements svn_ra_serf__response_handler_t */
static svn_error_t *
post_response_handler(serf_request_t *request,
serf_bucket_t *response,
void *baton,
apr_pool_t *scratch_pool)
{
post_response_ctx_t *prc = baton;
serf_bucket_t *hdrs = serf_bucket_response_get_headers(response);
/* Then see which ones we can discover. */
serf_bucket_headers_do(hdrs, post_headers_iterator_callback, prc);
/* Execute the 'real' response handler to XML-parse the repsonse body. */
return svn_ra_serf__expect_empty_body(request, response,
prc->handler, scratch_pool);
}
/* Commit baton callbacks */
static svn_error_t *
open_root(void *edit_baton,
svn_revnum_t base_revision,
apr_pool_t *dir_pool,
void **root_baton)
{
commit_context_t *ctx = edit_baton;
svn_ra_serf__handler_t *handler;
proppatch_context_t *proppatch_ctx;
dir_context_t *dir;
apr_hash_index_t *hi;
const char *proppatch_target = NULL;
if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(ctx->session))
{
post_response_ctx_t *prc;
const char *rel_path;
svn_boolean_t post_with_revprops
= (NULL != svn_hash_gets(ctx->session->supported_posts,
"create-txn-with-props"));
/* Create our activity URL now on the server. */
handler = apr_pcalloc(ctx->pool, sizeof(*handler));
handler->handler_pool = ctx->pool;
handler->method = "POST";
handler->body_type = SVN_SKEL_MIME_TYPE;
handler->body_delegate = create_txn_post_body;
handler->body_delegate_baton =
post_with_revprops ? ctx->revprop_table : NULL;
handler->header_delegate = setup_post_headers;
handler->header_delegate_baton = NULL;
handler->path = ctx->session->me_resource;
handler->conn = ctx->session->conns[0];
handler->session = ctx->session;
prc = apr_pcalloc(ctx->pool, sizeof(*prc));
prc->handler = handler;
prc->commit_ctx = ctx;
handler->response_handler = post_response_handler;
handler->response_baton = prc;
SVN_ERR(svn_ra_serf__context_run_one(handler, ctx->pool));
if (handler->sline.code != 201)
{
apr_status_t status = SVN_ERR_RA_DAV_REQUEST_FAILED;
switch (handler->sline.code)
{
case 403:
status = SVN_ERR_RA_DAV_FORBIDDEN;
break;
case 404:
status = SVN_ERR_FS_NOT_FOUND;
break;
}
return svn_error_createf(status, NULL,
_("%s of '%s': %d %s (%s://%s)"),
handler->method, handler->path,
handler->sline.code, handler->sline.reason,
ctx->session->session_url.scheme,
ctx->session->session_url.hostinfo);
}
if (! (ctx->txn_root_url && ctx->txn_url))
{
return svn_error_createf(
SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
_("POST request did not return transaction information"));
}
/* Fixup the txn_root_url to point to the anchor of the commit. */
SVN_ERR(svn_ra_serf__get_relative_path(&rel_path,
ctx->session->session_url.path,
ctx->session, NULL, dir_pool));
ctx->txn_root_url = svn_path_url_add_component2(ctx->txn_root_url,
rel_path, ctx->pool);
/* Build our directory baton. */
dir = apr_pcalloc(dir_pool, sizeof(*dir));
dir->pool = dir_pool;
dir->commit = ctx;
dir->base_revision = base_revision;
dir->relpath = "";
dir->name = "";
dir->changed_props = apr_hash_make(dir->pool);
dir->removed_props = apr_hash_make(dir->pool);
dir->url = apr_pstrdup(dir->pool, ctx->txn_root_url);
/* If we included our revprops in the POST, we need not
PROPPATCH them. */
proppatch_target = post_with_revprops ? NULL : ctx->txn_url;
}
else
{
const char *activity_str = ctx->session->activity_collection_url;
if (!activity_str)
SVN_ERR(svn_ra_serf__v1_get_activity_collection(&activity_str,
ctx->session->conns[0],
ctx->pool,
ctx->pool));
/* Cache the result. */
if (activity_str)
{
ctx->session->activity_collection_url =
apr_pstrdup(ctx->session->pool, activity_str);
}
else
{
return svn_error_create(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL,
_("The OPTIONS response did not include the "
"requested activity-collection-set value"));
}
ctx->activity_url =
svn_path_url_add_component2(activity_str, svn_uuid_generate(ctx->pool),
ctx->pool);
/* Create our activity URL now on the server. */
handler = apr_pcalloc(ctx->pool, sizeof(*handler));
handler->handler_pool = ctx->pool;
handler->method = "MKACTIVITY";
handler->path = ctx->activity_url;
handler->conn = ctx->session->conns[0];
handler->session = ctx->session;
handler->response_handler = svn_ra_serf__expect_empty_body;
handler->response_baton = handler;
SVN_ERR(svn_ra_serf__context_run_one(handler, ctx->pool));
if (handler->sline.code != 201)
{
apr_status_t status = SVN_ERR_RA_DAV_REQUEST_FAILED;
switch (handler->sline.code)
{
case 403:
status = SVN_ERR_RA_DAV_FORBIDDEN;
break;
case 404:
status = SVN_ERR_FS_NOT_FOUND;
break;
}
return svn_error_createf(status, NULL,
_("%s of '%s': %d %s (%s://%s)"),
handler->method, handler->path,
handler->sline.code, handler->sline.reason,
ctx->session->session_url.scheme,
ctx->session->session_url.hostinfo);
}
/* Now go fetch our VCC and baseline so we can do a CHECKOUT. */
SVN_ERR(svn_ra_serf__discover_vcc(&(ctx->vcc_url), ctx->session,
ctx->conn, ctx->pool));
/* Build our directory baton. */
dir = apr_pcalloc(dir_pool, sizeof(*dir));
dir->pool = dir_pool;
dir->commit = ctx;
dir->base_revision = base_revision;
dir->relpath = "";
dir->name = "";
dir->changed_props = apr_hash_make(dir->pool);
dir->removed_props = apr_hash_make(dir->pool);
SVN_ERR(get_version_url(&dir->url, dir->commit->session,
dir->relpath,
dir->base_revision, ctx->checked_in_url,
dir->pool, dir->pool /* scratch_pool */));
ctx->checked_in_url = dir->url;
/* Checkout our root dir */
SVN_ERR(checkout_dir(dir, dir->pool /* scratch_pool */));
proppatch_target = ctx->baseline_url;
}
/* Unless this is NULL -- which means we don't need to PROPPATCH the
transaction with our revprops -- then, you know, PROPPATCH the
transaction with our revprops. */
if (proppatch_target)
{
proppatch_ctx = apr_pcalloc(ctx->pool, sizeof(*proppatch_ctx));
proppatch_ctx->pool = dir_pool;
proppatch_ctx->commit = ctx;
proppatch_ctx->path = proppatch_target;
proppatch_ctx->changed_props = apr_hash_make(proppatch_ctx->pool);
proppatch_ctx->removed_props = apr_hash_make(proppatch_ctx->pool);
proppatch_ctx->base_revision = SVN_INVALID_REVNUM;
for (hi = apr_hash_first(ctx->pool, ctx->revprop_table); hi;
hi = apr_hash_next(hi))
{
const char *name = svn__apr_hash_index_key(hi);
svn_string_t *value = svn__apr_hash_index_val(hi);
const char *ns;
if (strncmp(name, SVN_PROP_PREFIX, sizeof(SVN_PROP_PREFIX) - 1) == 0)
{
ns = SVN_DAV_PROP_NS_SVN;
name += sizeof(SVN_PROP_PREFIX) - 1;
}
else
{
ns = SVN_DAV_PROP_NS_CUSTOM;
}
svn_ra_serf__set_prop(proppatch_ctx->changed_props,
proppatch_ctx->path,
ns, name, value, proppatch_ctx->pool);
}
SVN_ERR(proppatch_resource(proppatch_ctx, dir->commit, ctx->pool));
}
*root_baton = dir;
return SVN_NO_ERROR;
}
static svn_error_t *
delete_entry(const char *path,
svn_revnum_t revision,
void *parent_baton,
apr_pool_t *pool)
{
dir_context_t *dir = parent_baton;
delete_context_t *delete_ctx;
svn_ra_serf__handler_t *handler;
const char *delete_target;
if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit))
{
delete_target = svn_path_url_add_component2(dir->commit->txn_root_url,
path, dir->pool);
}
else
{
/* Ensure our directory has been checked out */
SVN_ERR(checkout_dir(dir, pool /* scratch_pool */));
delete_target = svn_path_url_add_component2(dir->working_url,
svn_relpath_basename(path,
NULL),
pool);
}
/* DELETE our entry */
delete_ctx = apr_pcalloc(pool, sizeof(*delete_ctx));
delete_ctx->relpath = apr_pstrdup(pool, path);
delete_ctx->revision = revision;
delete_ctx->commit = dir->commit;
handler = apr_pcalloc(pool, sizeof(*handler));
handler->handler_pool = pool;
handler->session = dir->commit->session;
handler->conn = dir->commit->conn;
handler->response_handler = svn_ra_serf__expect_empty_body;
handler->response_baton = handler;
handler->header_delegate = setup_delete_headers;
handler->header_delegate_baton = delete_ctx;
handler->method = "DELETE";
handler->path = delete_target;
SVN_ERR(svn_ra_serf__context_run_one(handler, pool));
/* 204 No Content: item successfully deleted */
if (handler->sline.code != 204)
{
return svn_error_trace(return_response_err(handler));
}
svn_hash_sets(dir->commit->deleted_entries,
apr_pstrdup(dir->commit->pool, path), (void *)1);
return SVN_NO_ERROR;
}
static svn_error_t *
add_directory(const char *path,
void *parent_baton,
const char *copyfrom_path,
svn_revnum_t copyfrom_revision,
apr_pool_t *dir_pool,
void **child_baton)
{
dir_context_t *parent = parent_baton;
dir_context_t *dir;
svn_ra_serf__handler_t *handler;
apr_status_t status;
const char *mkcol_target;
dir = apr_pcalloc(dir_pool, sizeof(*dir));
dir->pool = dir_pool;
dir->parent_dir = parent;
dir->commit = parent->commit;
dir->added = TRUE;
dir->base_revision = SVN_INVALID_REVNUM;
dir->copy_revision = copyfrom_revision;
dir->copy_path = apr_pstrdup(dir->pool, copyfrom_path);
dir->relpath = apr_pstrdup(dir->pool, path);
dir->name = svn_relpath_basename(dir->relpath, NULL);
dir->changed_props = apr_hash_make(dir->pool);
dir->removed_props = apr_hash_make(dir->pool);
if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit))
{
dir->url = svn_path_url_add_component2(parent->commit->txn_root_url,
path, dir->pool);
mkcol_target = dir->url;
}
else
{
/* Ensure our parent is checked out. */
SVN_ERR(checkout_dir(parent, dir->pool /* scratch_pool */));
dir->url = svn_path_url_add_component2(parent->commit->checked_in_url,
dir->name, dir->pool);
mkcol_target = svn_path_url_add_component2(
parent->working_url,
dir->name, dir->pool);
}
handler = apr_pcalloc(dir->pool, sizeof(*handler));
handler->handler_pool = dir->pool;
handler->conn = dir->commit->conn;
handler->session = dir->commit->session;
handler->response_handler = svn_ra_serf__expect_empty_body;
handler->response_baton = handler;
if (!dir->copy_path)
{
handler->method = "MKCOL";
handler->path = mkcol_target;
handler->header_delegate = setup_add_dir_common_headers;
handler->header_delegate_baton = dir;
}
else
{
apr_uri_t uri;
const char *req_url;
status = apr_uri_parse(dir->pool, dir->copy_path, &uri);
if (status)
{
return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
_("Unable to parse URL '%s'"),
dir->copy_path);
}
/* ### conn==NULL for session->conns[0]. same as commit->conn. */
SVN_ERR(svn_ra_serf__get_stable_url(&req_url, NULL /* latest_revnum */,
dir->commit->session,
NULL /* conn */,
uri.path, dir->copy_revision,
dir_pool, dir_pool));
handler->method = "COPY";
handler->path = req_url;
handler->header_delegate = setup_copy_dir_headers;
handler->header_delegate_baton = dir;
}
SVN_ERR(svn_ra_serf__context_run_one(handler, dir->pool));
switch (handler->sline.code)
{
case 201: /* Created: item was successfully copied */
case 204: /* No Content: item successfully replaced an existing target */
break;
case 403:
return svn_error_createf(SVN_ERR_RA_DAV_FORBIDDEN, NULL,
_("Access to '%s' forbidden"),
handler->path);
default:
return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
_("Adding directory failed: %s on %s "
"(%d %s)"),
handler->method, handler->path,
handler->sline.code, handler->sline.reason);
}
*child_baton = dir;
return SVN_NO_ERROR;
}
static svn_error_t *
open_directory(const char *path,
void *parent_baton,
svn_revnum_t base_revision,
apr_pool_t *dir_pool,
void **child_baton)
{
dir_context_t *parent = parent_baton;
dir_context_t *dir;
dir = apr_pcalloc(dir_pool, sizeof(*dir));
dir->pool = dir_pool;
dir->parent_dir = parent;
dir->commit = parent->commit;
dir->added = FALSE;
dir->base_revision = base_revision;
dir->relpath = apr_pstrdup(dir->pool, path);
dir->name = svn_relpath_basename(dir->relpath, NULL);
dir->changed_props = apr_hash_make(dir->pool);
dir->removed_props = apr_hash_make(dir->pool);
if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit))
{
dir->url = svn_path_url_add_component2(parent->commit->txn_root_url,
path, dir->pool);
}
else
{
SVN_ERR(get_version_url(&dir->url,
dir->commit->session,
dir->relpath, dir->base_revision,
dir->commit->checked_in_url,
dir->pool, dir->pool /* scratch_pool */));
}
*child_baton = dir;
return SVN_NO_ERROR;
}
static svn_error_t *
change_dir_prop(void *dir_baton,
const char *name,
const svn_string_t *value,
apr_pool_t *pool)
{
dir_context_t *dir = dir_baton;
const char *ns;
const char *proppatch_target;
if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit))
{
proppatch_target = dir->url;
}
else
{
/* Ensure we have a checked out dir. */
SVN_ERR(checkout_dir(dir, pool /* scratch_pool */));
proppatch_target = dir->working_url;
}
name = apr_pstrdup(dir->pool, name);
if (strncmp(name, SVN_PROP_PREFIX, sizeof(SVN_PROP_PREFIX) - 1) == 0)
{
ns = SVN_DAV_PROP_NS_SVN;
name += sizeof(SVN_PROP_PREFIX) - 1;
}
else
{
ns = SVN_DAV_PROP_NS_CUSTOM;
}
if (value)
{
value = svn_string_dup(value, dir->pool);
svn_ra_serf__set_prop(dir->changed_props, proppatch_target,
ns, name, value, dir->pool);
}
else
{
value = svn_string_create_empty(dir->pool);
svn_ra_serf__set_prop(dir->removed_props, proppatch_target,
ns, name, value, dir->pool);
}
return SVN_NO_ERROR;
}
static svn_error_t *
close_directory(void *dir_baton,
apr_pool_t *pool)
{
dir_context_t *dir = dir_baton;
/* Huh? We're going to be called before the texts are sent. Ugh.
* Therefore, just wave politely at our caller.
*/
/* PROPPATCH our prop change and pass it along. */
if (apr_hash_count(dir->changed_props) ||
apr_hash_count(dir->removed_props))
{
proppatch_context_t *proppatch_ctx;
proppatch_ctx = apr_pcalloc(pool, sizeof(*proppatch_ctx));
proppatch_ctx->pool = pool;
proppatch_ctx->commit = dir->commit;
proppatch_ctx->relpath = dir->relpath;
proppatch_ctx->changed_props = dir->changed_props;
proppatch_ctx->removed_props = dir->removed_props;
proppatch_ctx->base_revision = dir->base_revision;
if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit))
{
proppatch_ctx->path = dir->url;
}
else
{
proppatch_ctx->path = dir->working_url;
}
SVN_ERR(proppatch_resource(proppatch_ctx, dir->commit, dir->pool));
}
return SVN_NO_ERROR;
}
static svn_error_t *
add_file(const char *path,
void *parent_baton,
const char *copy_path,
svn_revnum_t copy_revision,
apr_pool_t *file_pool,
void **file_baton)
{
dir_context_t *dir = parent_baton;
file_context_t *new_file;
const char *deleted_parent = path;
new_file = apr_pcalloc(file_pool, sizeof(*new_file));
new_file->pool = file_pool;
dir->ref_count++;
new_file->parent_dir = dir;
new_file->commit = dir->commit;
new_file->relpath = apr_pstrdup(new_file->pool, path);
new_file->name = svn_relpath_basename(new_file->relpath, NULL);
new_file->added = TRUE;
new_file->base_revision = SVN_INVALID_REVNUM;
new_file->copy_path = apr_pstrdup(new_file->pool, copy_path);
new_file->copy_revision = copy_revision;
new_file->changed_props = apr_hash_make(new_file->pool);
new_file->removed_props = apr_hash_make(new_file->pool);
/* Ensure that the file doesn't exist by doing a HEAD on the
resource. If we're using HTTP v2, we'll just look into the
transaction root tree for this thing. */
if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit))
{
new_file->url = svn_path_url_add_component2(dir->commit->txn_root_url,
path, new_file->pool);
}
else
{
/* Ensure our parent directory has been checked out */
SVN_ERR(checkout_dir(dir, new_file->pool /* scratch_pool */));
new_file->url =
svn_path_url_add_component2(dir->working_url,
new_file->name, new_file->pool);
}
while (deleted_parent && deleted_parent[0] != '\0')
{
if (svn_hash_gets(dir->commit->deleted_entries, deleted_parent))
{
break;
}
deleted_parent = svn_relpath_dirname(deleted_parent, file_pool);
}
if (! ((dir->added && !dir->copy_path) ||
(deleted_parent && deleted_parent[0] != '\0')))
{
svn_ra_serf__handler_t *handler;
handler = apr_pcalloc(new_file->pool, sizeof(*handler));
handler->handler_pool = new_file->pool;
handler->session = new_file->commit->session;
handler->conn = new_file->commit->conn;
handler->method = "HEAD";
handler->path = svn_path_url_add_component2(
dir->commit->session->session_url.path,
path, new_file->pool);
handler->response_handler = svn_ra_serf__expect_empty_body;
handler->response_baton = handler;
SVN_ERR(svn_ra_serf__context_run_one(handler, new_file->pool));
if (handler->sline.code != 404)
{
if (handler->sline.code != 200)
{
svn_error_t *err;
err = svn_ra_serf__error_on_status(handler->sline,
handler->path,
handler->location);
SVN_ERR(err);
}
return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
_("File '%s' already exists"), path);
}
}
*file_baton = new_file;
return SVN_NO_ERROR;
}
static svn_error_t *
open_file(const char *path,
void *parent_baton,
svn_revnum_t base_revision,
apr_pool_t *file_pool,
void **file_baton)
{
dir_context_t *parent = parent_baton;
file_context_t *new_file;
new_file = apr_pcalloc(file_pool, sizeof(*new_file));
new_file->pool = file_pool;
parent->ref_count++;
new_file->parent_dir = parent;
new_file->commit = parent->commit;
new_file->relpath = apr_pstrdup(new_file->pool, path);
new_file->name = svn_relpath_basename(new_file->relpath, NULL);
new_file->added = FALSE;
new_file->base_revision = base_revision;
new_file->changed_props = apr_hash_make(new_file->pool);
new_file->removed_props = apr_hash_make(new_file->pool);
if (USING_HTTPV2_COMMIT_SUPPORT(parent->commit))
{
new_file->url = svn_path_url_add_component2(parent->commit->txn_root_url,
path, new_file->pool);
}
else
{
/* CHECKOUT the file into our activity. */
SVN_ERR(checkout_file(new_file, new_file->pool /* scratch_pool */));
new_file->url = new_file->working_url;
}
*file_baton = new_file;
return SVN_NO_ERROR;
}
static svn_error_t *
apply_textdelta(void *file_baton,
const char *base_checksum,
apr_pool_t *pool,
svn_txdelta_window_handler_t *handler,
void **handler_baton)
{
file_context_t *ctx = file_baton;
/* Store the stream in a temporary file; we'll give it to serf when we
* close this file.
*
* TODO: There should be a way we can stream the request body instead of
* writing to a temporary file (ugh). A special svn stream serf bucket
* that returns EAGAIN until we receive the done call? But, when
* would we run through the serf context? Grr.
*
* ctx->pool is the same for all files in the commit that send a
* textdelta so this file is explicitly closed in close_file to
* avoid too many simultaneously open files.
*/
SVN_ERR(svn_io_open_unique_file3(&ctx->svndiff, NULL, NULL,
svn_io_file_del_on_pool_cleanup,
ctx->pool, pool));
ctx->stream = svn_stream_create(ctx, pool);
svn_stream_set_write(ctx->stream, svndiff_stream_write);
svn_txdelta_to_svndiff3(handler, handler_baton, ctx->stream, 0,
SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool);
if (base_checksum)
ctx->base_checksum = apr_pstrdup(ctx->pool, base_checksum);
return SVN_NO_ERROR;
}
static svn_error_t *
change_file_prop(void *file_baton,
const char *name,
const svn_string_t *value,
apr_pool_t *pool)
{
file_context_t *file = file_baton;
const char *ns;
name = apr_pstrdup(file->pool, name);
if (strncmp(name, SVN_PROP_PREFIX, sizeof(SVN_PROP_PREFIX) - 1) == 0)
{
ns = SVN_DAV_PROP_NS_SVN;
name += sizeof(SVN_PROP_PREFIX) - 1;
}
else
{
ns = SVN_DAV_PROP_NS_CUSTOM;
}
if (value)
{
value = svn_string_dup(value, file->pool);
svn_ra_serf__set_prop(file->changed_props, file->url,
ns, name, value, file->pool);
}
else
{
value = svn_string_create_empty(file->pool);
svn_ra_serf__set_prop(file->removed_props, file->url,
ns, name, value, file->pool);
}
return SVN_NO_ERROR;
}
static svn_error_t *
close_file(void *file_baton,
const char *text_checksum,
apr_pool_t *scratch_pool)
{
file_context_t *ctx = file_baton;
svn_boolean_t put_empty_file = FALSE;
apr_status_t status;
ctx->result_checksum = text_checksum;
if (ctx->copy_path)
{
svn_ra_serf__handler_t *handler;
apr_uri_t uri;
const char *req_url;
status = apr_uri_parse(scratch_pool, ctx->copy_path, &uri);
if (status)
{
return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
_("Unable to parse URL '%s'"),
ctx->copy_path);
}
/* ### conn==NULL for session->conns[0]. same as commit->conn. */
SVN_ERR(svn_ra_serf__get_stable_url(&req_url, NULL /* latest_revnum */,
ctx->commit->session,
NULL /* conn */,
uri.path, ctx->copy_revision,
scratch_pool, scratch_pool));
handler = apr_pcalloc(scratch_pool, sizeof(*handler));
handler->handler_pool = scratch_pool;
handler->method = "COPY";
handler->path = req_url;
handler->conn = ctx->commit->conn;
handler->session = ctx->commit->session;
handler->response_handler = svn_ra_serf__expect_empty_body;
handler->response_baton = handler;
handler->header_delegate = setup_copy_file_headers;
handler->header_delegate_baton = ctx;
SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool));
if (handler->sline.code != 201 && handler->sline.code != 204)
{
return svn_error_trace(return_response_err(handler));
}
}
/* If we got no stream of changes, but this is an added-without-history
* file, make a note that we'll be PUTting a zero-byte file to the server.
*/
if ((!ctx->stream) && ctx->added && (!ctx->copy_path))
put_empty_file = TRUE;
/* If we had a stream of changes, push them to the server... */
if (ctx->stream || put_empty_file)
{
svn_ra_serf__handler_t *handler;
handler = apr_pcalloc(scratch_pool, sizeof(*handler));
handler->handler_pool = scratch_pool;
handler->method = "PUT";
handler->path = ctx->url;
handler->conn = ctx->commit->conn;
handler->session = ctx->commit->session;
handler->response_handler = svn_ra_serf__expect_empty_body;
handler->response_baton = handler;
if (put_empty_file)
{
handler->body_delegate = create_empty_put_body;
handler->body_delegate_baton = ctx;
handler->body_type = "text/plain";
}
else
{
handler->body_delegate = create_put_body;
handler->body_delegate_baton = ctx;
handler->body_type = SVN_SVNDIFF_MIME_TYPE;
}
handler->header_delegate = setup_put_headers;
handler->header_delegate_baton = ctx;
SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool));
if (handler->sline.code != 204 && handler->sline.code != 201)
{
return svn_error_trace(return_response_err(handler));
}
}
if (ctx->svndiff)
SVN_ERR(svn_io_file_close(ctx->svndiff, scratch_pool));
/* If we had any prop changes, push them via PROPPATCH. */
if (apr_hash_count(ctx->changed_props) ||
apr_hash_count(ctx->removed_props))
{
proppatch_context_t *proppatch;
proppatch = apr_pcalloc(ctx->pool, sizeof(*proppatch));
proppatch->pool = ctx->pool;
proppatch->relpath = ctx->relpath;
proppatch->path = ctx->url;
proppatch->commit = ctx->commit;
proppatch->changed_props = ctx->changed_props;
proppatch->removed_props = ctx->removed_props;
proppatch->base_revision = ctx->base_revision;
SVN_ERR(proppatch_resource(proppatch, ctx->commit, ctx->pool));
}
return SVN_NO_ERROR;
}
static svn_error_t *
close_edit(void *edit_baton,
apr_pool_t *pool)
{
commit_context_t *ctx = edit_baton;
const char *merge_target =
ctx->activity_url ? ctx->activity_url : ctx->txn_url;
const svn_commit_info_t *commit_info;
int response_code;
+ svn_error_t *err = NULL;
/* MERGE our activity */
SVN_ERR(svn_ra_serf__run_merge(&commit_info, &response_code,
ctx->session,
ctx->session->conns[0],
merge_target,
ctx->lock_tokens,
ctx->keep_locks,
pool, pool));
if (response_code != 200)
{
return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
_("MERGE request failed: returned %d "
"(during commit)"),
response_code);
}
+ ctx->txn_url = NULL; /* If HTTPv2, the txn is now done */
+
/* Inform the WC that we did a commit. */
if (ctx->callback)
- SVN_ERR(ctx->callback(commit_info, ctx->callback_baton, pool));
+ err = ctx->callback(commit_info, ctx->callback_baton, pool);
/* If we're using activities, DELETE our completed activity. */
if (ctx->activity_url)
{
svn_ra_serf__handler_t *handler;
handler = apr_pcalloc(pool, sizeof(*handler));
handler->handler_pool = pool;
handler->method = "DELETE";
handler->path = ctx->activity_url;
handler->conn = ctx->conn;
handler->session = ctx->session;
handler->response_handler = svn_ra_serf__expect_empty_body;
handler->response_baton = handler;
- SVN_ERR(svn_ra_serf__context_run_one(handler, pool));
+ ctx->activity_url = NULL; /* Don't try again in abort_edit() on fail */
+ SVN_ERR(svn_error_compose_create(
+ err,
+ svn_ra_serf__context_run_one(handler, pool)));
+
SVN_ERR_ASSERT(handler->sline.code == 204);
}
+
+ SVN_ERR(err);
return SVN_NO_ERROR;
}
static svn_error_t *
abort_edit(void *edit_baton,
apr_pool_t *pool)
{
commit_context_t *ctx = edit_baton;
svn_ra_serf__handler_t *handler;
/* If an activity or transaction wasn't even created, don't bother
trying to delete it. */
if (! (ctx->activity_url || ctx->txn_url))
return SVN_NO_ERROR;
/* An error occurred on conns[0]. serf 0.4.0 remembers that the connection
had a problem. We need to reset it, in order to use it again. */
serf_connection_reset(ctx->session->conns[0]->conn);
/* DELETE our aborted activity */
handler = apr_pcalloc(pool, sizeof(*handler));
handler->handler_pool = pool;
handler->method = "DELETE";
handler->conn = ctx->session->conns[0];
handler->session = ctx->session;
handler->response_handler = svn_ra_serf__expect_empty_body;
handler->response_baton = handler;
if (USING_HTTPV2_COMMIT_SUPPORT(ctx)) /* HTTP v2 */
handler->path = ctx->txn_url;
else
handler->path = ctx->activity_url;
SVN_ERR(svn_ra_serf__context_run_one(handler, pool));
/* 204 if deleted,
403 if DELETE was forbidden (indicates MKACTIVITY was forbidden too),
404 if the activity wasn't found. */
if (handler->sline.code != 204
&& handler->sline.code != 403
&& handler->sline.code != 404
)
{
return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
_("DELETE returned unexpected status: %d"),
handler->sline.code);
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_ra_serf__get_commit_editor(svn_ra_session_t *ra_session,
const svn_delta_editor_t **ret_editor,
void **edit_baton,
apr_hash_t *revprop_table,
svn_commit_callback2_t callback,
void *callback_baton,
apr_hash_t *lock_tokens,
svn_boolean_t keep_locks,
apr_pool_t *pool)
{
svn_ra_serf__session_t *session = ra_session->priv;
svn_delta_editor_t *editor;
commit_context_t *ctx;
const char *repos_root;
const char *base_relpath;
svn_boolean_t supports_ephemeral_props;
ctx = apr_pcalloc(pool, sizeof(*ctx));
ctx->pool = pool;
ctx->session = session;
ctx->conn = session->conns[0];
ctx->revprop_table = svn_prop_hash_dup(revprop_table, pool);
/* If the server supports ephemeral properties, add some carrying
interesting version information. */
SVN_ERR(svn_ra_serf__has_capability(ra_session, &supports_ephemeral_props,
SVN_RA_CAPABILITY_EPHEMERAL_TXNPROPS,
pool));
if (supports_ephemeral_props)
{
svn_hash_sets(ctx->revprop_table,
apr_pstrdup(pool, SVN_PROP_TXN_CLIENT_COMPAT_VERSION),
svn_string_create(SVN_VER_NUMBER, pool));
svn_hash_sets(ctx->revprop_table,
apr_pstrdup(pool, SVN_PROP_TXN_USER_AGENT),
svn_string_create(session->useragent, pool));
}
ctx->callback = callback;
ctx->callback_baton = callback_baton;
ctx->lock_tokens = (lock_tokens && apr_hash_count(lock_tokens))
? lock_tokens : NULL;
ctx->keep_locks = keep_locks;
ctx->deleted_entries = apr_hash_make(ctx->pool);
editor = svn_delta_default_editor(pool);
editor->open_root = open_root;
editor->delete_entry = delete_entry;
editor->add_directory = add_directory;
editor->open_directory = open_directory;
editor->change_dir_prop = change_dir_prop;
editor->close_directory = close_directory;
editor->add_file = add_file;
editor->open_file = open_file;
editor->apply_textdelta = apply_textdelta;
editor->change_file_prop = change_file_prop;
editor->close_file = close_file;
editor->close_edit = close_edit;
editor->abort_edit = abort_edit;
*ret_editor = editor;
*edit_baton = ctx;
SVN_ERR(svn_ra_serf__get_repos_root(ra_session, &repos_root, pool));
base_relpath = svn_uri_skip_ancestor(repos_root, session->session_url_str,
pool);
SVN_ERR(svn_editor__insert_shims(ret_editor, edit_baton, *ret_editor,
*edit_baton, repos_root, base_relpath,
session->shim_callbacks, pool, pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_ra_serf__change_rev_prop(svn_ra_session_t *ra_session,
svn_revnum_t rev,
const char *name,
const svn_string_t *const *old_value_p,
const svn_string_t *value,
apr_pool_t *pool)
{
svn_ra_serf__session_t *session = ra_session->priv;
proppatch_context_t *proppatch_ctx;
commit_context_t *commit;
const char *proppatch_target;
const char *ns;
svn_error_t *err;
if (old_value_p)
{
svn_boolean_t capable;
SVN_ERR(svn_ra_serf__has_capability(ra_session, &capable,
SVN_RA_CAPABILITY_ATOMIC_REVPROPS,
pool));
/* How did you get past the same check in svn_ra_change_rev_prop2()? */
SVN_ERR_ASSERT(capable);
}
commit = apr_pcalloc(pool, sizeof(*commit));
commit->pool = pool;
commit->session = session;
commit->conn = session->conns[0];
if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session))
{
proppatch_target = apr_psprintf(pool, "%s/%ld", session->rev_stub, rev);
}
else
{
const char *vcc_url;
SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, commit->session,
commit->conn, pool));
SVN_ERR(svn_ra_serf__fetch_dav_prop(&proppatch_target,
commit->conn, vcc_url, rev,
"href",
pool, pool));
}
if (strncmp(name, SVN_PROP_PREFIX, sizeof(SVN_PROP_PREFIX) - 1) == 0)
{
ns = SVN_DAV_PROP_NS_SVN;
name += sizeof(SVN_PROP_PREFIX) - 1;
}
else
{
ns = SVN_DAV_PROP_NS_CUSTOM;
}
/* PROPPATCH our log message and pass it along. */
proppatch_ctx = apr_pcalloc(pool, sizeof(*proppatch_ctx));
proppatch_ctx->pool = pool;
proppatch_ctx->commit = commit;
proppatch_ctx->path = proppatch_target;
proppatch_ctx->changed_props = apr_hash_make(proppatch_ctx->pool);
proppatch_ctx->removed_props = apr_hash_make(proppatch_ctx->pool);
if (old_value_p)
{
proppatch_ctx->previous_changed_props = apr_hash_make(proppatch_ctx->pool);
proppatch_ctx->previous_removed_props = apr_hash_make(proppatch_ctx->pool);
}
proppatch_ctx->base_revision = SVN_INVALID_REVNUM;
if (old_value_p && *old_value_p)
{
svn_ra_serf__set_prop(proppatch_ctx->previous_changed_props,
proppatch_ctx->path,
ns, name, *old_value_p, proppatch_ctx->pool);
}
else if (old_value_p)
{
svn_string_t *dummy_value = svn_string_create_empty(proppatch_ctx->pool);
svn_ra_serf__set_prop(proppatch_ctx->previous_removed_props,
proppatch_ctx->path,
ns, name, dummy_value, proppatch_ctx->pool);
}
if (value)
{
svn_ra_serf__set_prop(proppatch_ctx->changed_props, proppatch_ctx->path,
ns, name, value, proppatch_ctx->pool);
}
else
{
value = svn_string_create_empty(proppatch_ctx->pool);
svn_ra_serf__set_prop(proppatch_ctx->removed_props, proppatch_ctx->path,
ns, name, value, proppatch_ctx->pool);
}
err = proppatch_resource(proppatch_ctx, commit, proppatch_ctx->pool);
if (err)
return
svn_error_create
(SVN_ERR_RA_DAV_REQUEST_FAILED, err,
_("DAV request failed; it's possible that the repository's "
"pre-revprop-change hook either failed or is non-existent"));
return SVN_NO_ERROR;
}
Index: vendor/subversion/dist/subversion/libsvn_ra_serf/options.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_ra_serf/options.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_ra_serf/options.c (revision 286501)
@@ -1,687 +1,690 @@
/*
* options.c : entry point for OPTIONS RA functions for ra_serf
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include <apr_uri.h>
#include <serf.h>
#include "svn_dirent_uri.h"
#include "svn_hash.h"
#include "svn_pools.h"
#include "svn_ra.h"
#include "svn_dav.h"
#include "svn_xml.h"
+#include "svn_ctype.h"
#include "../libsvn_ra/ra_loader.h"
#include "svn_private_config.h"
#include "private/svn_fspath.h"
#include "ra_serf.h"
/* In a debug build, setting this environment variable to "yes" will force
the client to speak v1, even if the server is capable of speaking v2. */
#define SVN_IGNORE_V2_ENV_VAR "SVN_I_LIKE_LATENCY_SO_IGNORE_HTTPV2"
/*
* This enum represents the current state of our XML parsing for an OPTIONS.
*/
enum options_state_e {
INITIAL = 0,
OPTIONS,
ACTIVITY_COLLECTION,
HREF
};
typedef struct options_context_t {
/* pool to allocate memory from */
apr_pool_t *pool;
/* Have we extracted options values from the headers already? */
svn_boolean_t headers_processed;
svn_ra_serf__session_t *session;
svn_ra_serf__connection_t *conn;
svn_ra_serf__handler_t *handler;
svn_ra_serf__response_handler_t inner_handler;
void *inner_baton;
const char *activity_collection;
svn_revnum_t youngest_rev;
} options_context_t;
#define D_ "DAV:"
#define S_ SVN_XML_NAMESPACE
static const svn_ra_serf__xml_transition_t options_ttable[] = {
{ INITIAL, D_, "options-response", OPTIONS,
FALSE, { NULL }, FALSE },
{ OPTIONS, D_, "activity-collection-set", ACTIVITY_COLLECTION,
FALSE, { NULL }, FALSE },
{ ACTIVITY_COLLECTION, D_, "href", HREF,
TRUE, { NULL }, TRUE },
{ 0 }
};
/* Conforms to svn_ra_serf__xml_closed_t */
static svn_error_t *
options_closed(svn_ra_serf__xml_estate_t *xes,
void *baton,
int leaving_state,
const svn_string_t *cdata,
apr_hash_t *attrs,
apr_pool_t *scratch_pool)
{
options_context_t *opt_ctx = baton;
SVN_ERR_ASSERT(leaving_state == HREF);
SVN_ERR_ASSERT(cdata != NULL);
opt_ctx->activity_collection = svn_urlpath__canonicalize(cdata->data,
opt_ctx->pool);
return SVN_NO_ERROR;
}
static svn_error_t *
create_options_body(serf_bucket_t **body_bkt,
void *baton,
serf_bucket_alloc_t *alloc,
apr_pool_t *pool)
{
serf_bucket_t *body;
body = serf_bucket_aggregate_create(alloc);
svn_ra_serf__add_xml_header_buckets(body, alloc);
svn_ra_serf__add_open_tag_buckets(body, alloc, "D:options",
"xmlns:D", "DAV:",
NULL);
svn_ra_serf__add_tag_buckets(body, "D:activity-collection-set", NULL, alloc);
svn_ra_serf__add_close_tag_buckets(body, alloc, "D:options");
*body_bkt = body;
return SVN_NO_ERROR;
}
/* We use these static pointers so we can employ pointer comparison
* of our capabilities hash members instead of strcmp()ing all over
* the place.
*/
/* Both server and repository support the capability. */
static const char *const capability_yes = "yes";
/* Either server or repository does not support the capability. */
static const char *const capability_no = "no";
/* Server supports the capability, but don't yet know if repository does. */
static const char *const capability_server_yes = "server-yes";
/* This implements serf_bucket_headers_do_callback_fn_t.
*/
static int
capabilities_headers_iterator_callback(void *baton,
const char *key,
const char *val)
{
options_context_t *opt_ctx = baton;
svn_ra_serf__session_t *session = opt_ctx->session;
if (svn_cstring_casecmp(key, "dav") == 0)
{
/* Each header may contain multiple values, separated by commas, e.g.:
DAV: version-control,checkout,working-resource
DAV: merge,baseline,activity,version-controlled-collection
DAV: http://subversion.tigris.org/xmlns/dav/svn/depth */
apr_array_header_t *vals = svn_cstring_split(val, ",", TRUE,
opt_ctx->pool);
/* Right now we only have a few capabilities to detect, so just
seek for them directly. This could be written slightly more
efficiently, but that wouldn't be worth it until we have many
more capabilities. */
if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_DEPTH, vals))
{
svn_hash_sets(session->capabilities,
SVN_RA_CAPABILITY_DEPTH, capability_yes);
}
if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_MERGEINFO, vals))
{
/* The server doesn't know what repository we're referring
to, so it can't just say capability_yes. */
if (!svn_hash_gets(session->capabilities,
SVN_RA_CAPABILITY_MERGEINFO))
{
svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO,
capability_server_yes);
}
}
if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_LOG_REVPROPS, vals))
{
svn_hash_sets(session->capabilities,
SVN_RA_CAPABILITY_LOG_REVPROPS, capability_yes);
}
if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_ATOMIC_REVPROPS, vals))
{
svn_hash_sets(session->capabilities,
SVN_RA_CAPABILITY_ATOMIC_REVPROPS, capability_yes);
}
if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_PARTIAL_REPLAY, vals))
{
svn_hash_sets(session->capabilities,
SVN_RA_CAPABILITY_PARTIAL_REPLAY, capability_yes);
}
if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_INHERITED_PROPS, vals))
{
svn_hash_sets(session->capabilities,
SVN_RA_CAPABILITY_INHERITED_PROPS, capability_yes);
}
if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_REVERSE_FILE_REVS,
vals))
{
svn_hash_sets(session->capabilities,
SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE,
capability_yes);
}
if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_EPHEMERAL_TXNPROPS, vals))
{
svn_hash_sets(session->capabilities,
SVN_RA_CAPABILITY_EPHEMERAL_TXNPROPS, capability_yes);
}
if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_INLINE_PROPS, vals))
{
session->supports_inline_props = TRUE;
}
if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_REPLAY_REV_RESOURCE, vals))
{
session->supports_rev_rsrc_replay = TRUE;
}
}
/* SVN-specific headers -- if present, server supports HTTP protocol v2 */
- else if (strncmp(key, "SVN", 3) == 0)
+ else if (!svn_ctype_casecmp(key[0], 'S')
+ && !svn_ctype_casecmp(key[1], 'V')
+ && !svn_ctype_casecmp(key[2], 'N'))
{
/* If we've not yet seen any information about supported POST
requests, we'll initialize the list/hash with "create-txn"
(which we know is supported by virtue of the server speaking
HTTPv2 at all. */
if (! session->supported_posts)
{
session->supported_posts = apr_hash_make(session->pool);
apr_hash_set(session->supported_posts, "create-txn", 10, (void *)1);
}
if (svn_cstring_casecmp(key, SVN_DAV_ROOT_URI_HEADER) == 0)
{
session->repos_root = session->session_url;
session->repos_root.path =
(char *)svn_fspath__canonicalize(val, session->pool);
session->repos_root_str =
svn_urlpath__canonicalize(
apr_uri_unparse(session->pool, &session->repos_root, 0),
session->pool);
}
else if (svn_cstring_casecmp(key, SVN_DAV_ME_RESOURCE_HEADER) == 0)
{
#ifdef SVN_DEBUG
char *ignore_v2_env_var = getenv(SVN_IGNORE_V2_ENV_VAR);
if (!(ignore_v2_env_var
&& apr_strnatcasecmp(ignore_v2_env_var, "yes") == 0))
session->me_resource = apr_pstrdup(session->pool, val);
#else
session->me_resource = apr_pstrdup(session->pool, val);
#endif
}
else if (svn_cstring_casecmp(key, SVN_DAV_REV_STUB_HEADER) == 0)
{
session->rev_stub = apr_pstrdup(session->pool, val);
}
else if (svn_cstring_casecmp(key, SVN_DAV_REV_ROOT_STUB_HEADER) == 0)
{
session->rev_root_stub = apr_pstrdup(session->pool, val);
}
else if (svn_cstring_casecmp(key, SVN_DAV_TXN_STUB_HEADER) == 0)
{
session->txn_stub = apr_pstrdup(session->pool, val);
}
else if (svn_cstring_casecmp(key, SVN_DAV_TXN_ROOT_STUB_HEADER) == 0)
{
session->txn_root_stub = apr_pstrdup(session->pool, val);
}
else if (svn_cstring_casecmp(key, SVN_DAV_VTXN_STUB_HEADER) == 0)
{
session->vtxn_stub = apr_pstrdup(session->pool, val);
}
else if (svn_cstring_casecmp(key, SVN_DAV_VTXN_ROOT_STUB_HEADER) == 0)
{
session->vtxn_root_stub = apr_pstrdup(session->pool, val);
}
else if (svn_cstring_casecmp(key, SVN_DAV_REPOS_UUID_HEADER) == 0)
{
session->uuid = apr_pstrdup(session->pool, val);
}
else if (svn_cstring_casecmp(key, SVN_DAV_YOUNGEST_REV_HEADER) == 0)
{
opt_ctx->youngest_rev = SVN_STR_TO_REV(val);
}
else if (svn_cstring_casecmp(key, SVN_DAV_ALLOW_BULK_UPDATES) == 0)
{
session->server_allows_bulk = apr_pstrdup(session->pool, val);
}
else if (svn_cstring_casecmp(key, SVN_DAV_SUPPORTED_POSTS_HEADER) == 0)
{
/* May contain multiple values, separated by commas. */
int i;
apr_array_header_t *vals = svn_cstring_split(val, ",", TRUE,
session->pool);
for (i = 0; i < vals->nelts; i++)
{
const char *post_val = APR_ARRAY_IDX(vals, i, const char *);
svn_hash_sets(session->supported_posts, post_val, (void *)1);
}
}
else if (svn_cstring_casecmp(key, SVN_DAV_REPOSITORY_MERGEINFO) == 0)
{
if (svn_cstring_casecmp(val, "yes") == 0)
{
svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO,
capability_yes);
}
else if (svn_cstring_casecmp(val, "no") == 0)
{
svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO,
capability_no);
}
}
}
return 0;
}
/* A custom serf_response_handler_t which is mostly a wrapper around
the expat-based response handler -- it just notices OPTIONS response
headers first, before handing off to the xml parser.
Implements svn_ra_serf__response_handler_t */
static svn_error_t *
options_response_handler(serf_request_t *request,
serf_bucket_t *response,
void *baton,
apr_pool_t *pool)
{
options_context_t *opt_ctx = baton;
if (!opt_ctx->headers_processed)
{
svn_ra_serf__session_t *session = opt_ctx->session;
serf_bucket_t *hdrs = serf_bucket_response_get_headers(response);
/* Start out assuming all capabilities are unsupported. */
svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_PARTIAL_REPLAY,
capability_no);
svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_DEPTH,
capability_no);
svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO,
NULL);
svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_LOG_REVPROPS,
capability_no);
svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_ATOMIC_REVPROPS,
capability_no);
svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_INHERITED_PROPS,
capability_no);
svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_EPHEMERAL_TXNPROPS,
capability_no);
svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE,
capability_no);
/* Then see which ones we can discover. */
serf_bucket_headers_do(hdrs, capabilities_headers_iterator_callback,
opt_ctx);
/* Assume mergeinfo capability unsupported, if didn't recieve information
about server or repository mergeinfo capability. */
if (!svn_hash_gets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO))
svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO,
capability_no);
opt_ctx->headers_processed = TRUE;
}
/* Execute the 'real' response handler to XML-parse the response body. */
return opt_ctx->inner_handler(request, response, opt_ctx->inner_baton, pool);
}
static svn_error_t *
create_options_req(options_context_t **opt_ctx,
svn_ra_serf__session_t *session,
svn_ra_serf__connection_t *conn,
apr_pool_t *pool)
{
options_context_t *new_ctx;
svn_ra_serf__xml_context_t *xmlctx;
svn_ra_serf__handler_t *handler;
new_ctx = apr_pcalloc(pool, sizeof(*new_ctx));
new_ctx->pool = pool;
new_ctx->session = session;
new_ctx->conn = conn;
new_ctx->youngest_rev = SVN_INVALID_REVNUM;
xmlctx = svn_ra_serf__xml_context_create(options_ttable,
NULL, options_closed, NULL,
new_ctx,
pool);
handler = svn_ra_serf__create_expat_handler(xmlctx, pool);
handler->method = "OPTIONS";
handler->path = session->session_url.path;
handler->body_delegate = create_options_body;
handler->body_type = "text/xml";
handler->conn = conn;
handler->session = session;
new_ctx->handler = handler;
new_ctx->inner_handler = handler->response_handler;
new_ctx->inner_baton = handler->response_baton;
handler->response_handler = options_response_handler;
handler->response_baton = new_ctx;
*opt_ctx = new_ctx;
return SVN_NO_ERROR;
}
svn_error_t *
svn_ra_serf__v2_get_youngest_revnum(svn_revnum_t *youngest,
svn_ra_serf__connection_t *conn,
apr_pool_t *scratch_pool)
{
svn_ra_serf__session_t *session = conn->session;
options_context_t *opt_ctx;
SVN_ERR_ASSERT(SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session));
SVN_ERR(create_options_req(&opt_ctx, session, conn, scratch_pool));
SVN_ERR(svn_ra_serf__context_run_one(opt_ctx->handler, scratch_pool));
SVN_ERR(svn_ra_serf__error_on_status(opt_ctx->handler->sline,
opt_ctx->handler->path,
opt_ctx->handler->location));
*youngest = opt_ctx->youngest_rev;
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(*youngest));
return SVN_NO_ERROR;
}
svn_error_t *
svn_ra_serf__v1_get_activity_collection(const char **activity_url,
svn_ra_serf__connection_t *conn,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_ra_serf__session_t *session = conn->session;
options_context_t *opt_ctx;
SVN_ERR_ASSERT(!SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session));
SVN_ERR(create_options_req(&opt_ctx, session, conn, scratch_pool));
SVN_ERR(svn_ra_serf__context_run_one(opt_ctx->handler, scratch_pool));
SVN_ERR(svn_ra_serf__error_on_status(opt_ctx->handler->sline,
opt_ctx->handler->path,
opt_ctx->handler->location));
*activity_url = apr_pstrdup(result_pool, opt_ctx->activity_collection);
return SVN_NO_ERROR;
}
/** Capabilities exchange. */
svn_error_t *
svn_ra_serf__exchange_capabilities(svn_ra_serf__session_t *serf_sess,
const char **corrected_url,
apr_pool_t *pool)
{
options_context_t *opt_ctx;
svn_error_t *err;
/* This routine automatically fills in serf_sess->capabilities */
SVN_ERR(create_options_req(&opt_ctx, serf_sess, serf_sess->conns[0], pool));
err = svn_ra_serf__context_run_one(opt_ctx->handler, pool);
/* If our caller cares about server redirections, and our response
carries such a thing, report as much. We'll disregard ERR --
it's most likely just a complaint about the response body not
successfully parsing as XML or somesuch. */
if (corrected_url && (opt_ctx->handler->sline.code == 301))
{
svn_error_clear(err);
*corrected_url = opt_ctx->handler->location;
return SVN_NO_ERROR;
}
SVN_ERR(svn_error_compose_create(
svn_ra_serf__error_on_status(opt_ctx->handler->sline,
serf_sess->session_url.path,
opt_ctx->handler->location),
err));
/* Opportunistically cache any reported activity URL. (We don't
want to have to ask for this again later, potentially against an
unreadable commit anchor URL.) */
if (opt_ctx->activity_collection)
{
serf_sess->activity_collection_url =
apr_pstrdup(serf_sess->pool, opt_ctx->activity_collection);
}
return SVN_NO_ERROR;
}
static svn_error_t *
create_simple_options_body(serf_bucket_t **body_bkt,
void *baton,
serf_bucket_alloc_t *alloc,
apr_pool_t *pool)
{
serf_bucket_t *body;
serf_bucket_t *s;
body = serf_bucket_aggregate_create(alloc);
svn_ra_serf__add_xml_header_buckets(body, alloc);
s = SERF_BUCKET_SIMPLE_STRING("<D:options xmlns:D=\"DAV:\" />", alloc);
serf_bucket_aggregate_append(body, s);
*body_bkt = body;
return SVN_NO_ERROR;
}
svn_error_t *
svn_ra_serf__probe_proxy(svn_ra_serf__session_t *serf_sess,
apr_pool_t *scratch_pool)
{
svn_ra_serf__handler_t *handler;
handler = apr_pcalloc(scratch_pool, sizeof(*handler));
handler->handler_pool = scratch_pool;
handler->method = "OPTIONS";
handler->path = serf_sess->session_url.path;
handler->conn = serf_sess->conns[0];
handler->session = serf_sess;
/* We don't care about the response body, so discard it. */
handler->response_handler = svn_ra_serf__handle_discard_body;
/* We need a simple body, in order to send it in chunked format. */
handler->body_delegate = create_simple_options_body;
/* No special headers. */
SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool));
/* Some versions of nginx in reverse proxy mode will return 411. They want
a Content-Length header, rather than chunked requests. We can keep other
HTTP/1.1 features, but will disable the chunking. */
if (handler->sline.code == 411)
{
serf_sess->using_chunked_requests = FALSE;
return SVN_NO_ERROR;
}
SVN_ERR(svn_ra_serf__error_on_status(handler->sline,
handler->path,
handler->location));
return SVN_NO_ERROR;
}
svn_error_t *
svn_ra_serf__has_capability(svn_ra_session_t *ra_session,
svn_boolean_t *has,
const char *capability,
apr_pool_t *pool)
{
svn_ra_serf__session_t *serf_sess = ra_session->priv;
const char *cap_result;
/* This capability doesn't rely on anything server side. */
if (strcmp(capability, SVN_RA_CAPABILITY_COMMIT_REVPROPS) == 0)
{
*has = TRUE;
return SVN_NO_ERROR;
}
cap_result = svn_hash_gets(serf_sess->capabilities, capability);
/* If any capability is unknown, they're all unknown, so ask. */
if (cap_result == NULL)
SVN_ERR(svn_ra_serf__exchange_capabilities(serf_sess, NULL, pool));
/* Try again, now that we've fetched the capabilities. */
cap_result = svn_hash_gets(serf_sess->capabilities, capability);
/* Some capabilities depend on the repository as well as the server. */
if (cap_result == capability_server_yes)
{
if (strcmp(capability, SVN_RA_CAPABILITY_MERGEINFO) == 0)
{
/* Handle mergeinfo specially. Mergeinfo depends on the
repository as well as the server, but the server routine
that answered our svn_ra_serf__exchange_capabilities() call above
didn't even know which repository we were interested in
-- it just told us whether the server supports mergeinfo.
If the answer was 'no', there's no point checking the
particular repository; but if it was 'yes', we still must
change it to 'no' iff the repository itself doesn't
support mergeinfo. */
svn_mergeinfo_catalog_t ignored;
svn_error_t *err;
apr_array_header_t *paths = apr_array_make(pool, 1,
sizeof(char *));
APR_ARRAY_PUSH(paths, const char *) = "";
err = svn_ra_serf__get_mergeinfo(ra_session, &ignored, paths, 0,
FALSE, FALSE, pool);
if (err)
{
if (err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)
{
svn_error_clear(err);
cap_result = capability_no;
}
else if (err->apr_err == SVN_ERR_FS_NOT_FOUND)
{
/* Mergeinfo requests use relative paths, and
anyway we're in r0, so this is a likely error,
but it means the repository supports mergeinfo! */
svn_error_clear(err);
cap_result = capability_yes;
}
else
return err;
}
else
cap_result = capability_yes;
svn_hash_sets(serf_sess->capabilities,
SVN_RA_CAPABILITY_MERGEINFO, cap_result);
}
else
{
return svn_error_createf
(SVN_ERR_UNKNOWN_CAPABILITY, NULL,
_("Don't know how to handle '%s' for capability '%s'"),
capability_server_yes, capability);
}
}
if (cap_result == capability_yes)
{
*has = TRUE;
}
else if (cap_result == capability_no)
{
*has = FALSE;
}
else if (cap_result == NULL)
{
return svn_error_createf
(SVN_ERR_UNKNOWN_CAPABILITY, NULL,
_("Don't know anything about capability '%s'"), capability);
}
else /* "can't happen" */
{
/* Well, let's hope it's a string. */
return svn_error_createf
(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL,
_("Attempt to fetch capability '%s' resulted in '%s'"),
capability, cap_result);
}
return SVN_NO_ERROR;
}
Index: vendor/subversion/dist/subversion/libsvn_repos/commit.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_repos/commit.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_repos/commit.c (revision 286501)
@@ -1,1392 +1,1399 @@
/* commit.c --- editor for committing changes to a filesystem.
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include <string.h>
#include <apr_pools.h>
#include <apr_file_io.h>
#include "svn_hash.h"
#include "svn_compat.h"
#include "svn_pools.h"
#include "svn_error.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_delta.h"
#include "svn_fs.h"
#include "svn_repos.h"
#include "svn_checksum.h"
#include "svn_ctype.h"
#include "svn_props.h"
#include "svn_mergeinfo.h"
#include "svn_private_config.h"
#include "repos.h"
#include "private/svn_fspath.h"
#include "private/svn_fs_private.h"
#include "private/svn_repos_private.h"
#include "private/svn_editor.h"
/*** Editor batons. ***/
struct edit_baton
{
apr_pool_t *pool;
/** Supplied when the editor is created: **/
/* Revision properties to set for this commit. */
apr_hash_t *revprop_table;
/* Callback to run when the commit is done. */
svn_commit_callback2_t commit_callback;
void *commit_callback_baton;
/* Callback to check authorizations on paths. */
svn_repos_authz_callback_t authz_callback;
void *authz_baton;
/* The already-open svn repository to commit to. */
svn_repos_t *repos;
/* URL to the root of the open repository. */
const char *repos_url;
/* The name of the repository (here for convenience). */
const char *repos_name;
/* The filesystem associated with the REPOS above (here for
convenience). */
svn_fs_t *fs;
/* Location in fs where the edit will begin. */
const char *base_path;
/* Does this set of interfaces 'own' the commit transaction? */
svn_boolean_t txn_owner;
/* svn transaction associated with this edit (created in
open_root, or supplied by the public API caller). */
svn_fs_txn_t *txn;
/** Filled in during open_root: **/
/* The name of the transaction. */
const char *txn_name;
/* The object representing the root directory of the svn txn. */
svn_fs_root_t *txn_root;
/* Avoid aborting an fs transaction more than once */
svn_boolean_t txn_aborted;
/** Filled in when the edit is closed: **/
/* The new revision created by this commit. */
svn_revnum_t *new_rev;
/* The date (according to the repository) of this commit. */
const char **committed_date;
/* The author (also according to the repository) of this commit. */
const char **committed_author;
};
struct dir_baton
{
struct edit_baton *edit_baton;
struct dir_baton *parent;
const char *path; /* the -absolute- path to this dir in the fs */
svn_revnum_t base_rev; /* the revision I'm based on */
svn_boolean_t was_copied; /* was this directory added with history? */
apr_pool_t *pool; /* my personal pool, in which I am allocated. */
};
struct file_baton
{
struct edit_baton *edit_baton;
const char *path; /* the -absolute- path to this file in the fs */
};
struct ev2_baton
{
/* The repository we are editing. */
svn_repos_t *repos;
/* The authz baton for checks; NULL to skip authz. */
svn_authz_t *authz;
/* The repository name and user for performing authz checks. */
const char *authz_repos_name;
const char *authz_user;
/* Callback to provide info about the committed revision. */
svn_commit_callback2_t commit_cb;
void *commit_baton;
/* The FS txn editor */
svn_editor_t *inner;
/* The name of the open transaction (so we know what to commit) */
const char *txn_name;
};
/* Create and return a generic out-of-dateness error. */
static svn_error_t *
out_of_date(const char *path, svn_node_kind_t kind)
{
return svn_error_createf(SVN_ERR_FS_TXN_OUT_OF_DATE, NULL,
(kind == svn_node_dir
? _("Directory '%s' is out of date")
: kind == svn_node_file
? _("File '%s' is out of date")
: _("'%s' is out of date")),
path);
}
static svn_error_t *
invoke_commit_cb(svn_commit_callback2_t commit_cb,
void *commit_baton,
svn_fs_t *fs,
svn_revnum_t revision,
const char *post_commit_errstr,
apr_pool_t *scratch_pool)
{
/* FS interface returns non-const values. */
/* const */ svn_string_t *date;
/* const */ svn_string_t *author;
svn_commit_info_t *commit_info;
if (commit_cb == NULL)
return SVN_NO_ERROR;
SVN_ERR(svn_fs_revision_prop(&date, fs, revision, SVN_PROP_REVISION_DATE,
scratch_pool));
SVN_ERR(svn_fs_revision_prop(&author, fs, revision,
SVN_PROP_REVISION_AUTHOR,
scratch_pool));
commit_info = svn_create_commit_info(scratch_pool);
/* fill up the svn_commit_info structure */
commit_info->revision = revision;
commit_info->date = date ? date->data : NULL;
commit_info->author = author ? author->data : NULL;
commit_info->post_commit_err = post_commit_errstr;
return svn_error_trace(commit_cb(commit_info, commit_baton, scratch_pool));
}
/* If EDITOR_BATON contains a valid authz callback, verify that the
REQUIRED access to PATH in ROOT is authorized. Return an error
appropriate for throwing out of the commit editor with SVN_ERR. If
no authz callback is present in EDITOR_BATON, then authorize all
paths. Use POOL for temporary allocation only. */
static svn_error_t *
check_authz(struct edit_baton *editor_baton, const char *path,
svn_fs_root_t *root, svn_repos_authz_access_t required,
apr_pool_t *pool)
{
if (editor_baton->authz_callback)
{
svn_boolean_t allowed;
SVN_ERR(editor_baton->authz_callback(required, &allowed, root, path,
editor_baton->authz_baton, pool));
if (!allowed)
return svn_error_create(required & svn_authz_write ?
SVN_ERR_AUTHZ_UNWRITABLE :
SVN_ERR_AUTHZ_UNREADABLE,
NULL, "Access denied");
}
return SVN_NO_ERROR;
}
/* Return a directory baton allocated in POOL which represents
FULL_PATH, which is the immediate directory child of the directory
represented by PARENT_BATON. EDIT_BATON is the commit editor
baton. WAS_COPIED reveals whether or not this directory is the
result of a copy operation. BASE_REVISION is the base revision of
the directory. */
static struct dir_baton *
make_dir_baton(struct edit_baton *edit_baton,
struct dir_baton *parent_baton,
const char *full_path,
svn_boolean_t was_copied,
svn_revnum_t base_revision,
apr_pool_t *pool)
{
struct dir_baton *db;
db = apr_pcalloc(pool, sizeof(*db));
db->edit_baton = edit_baton;
db->parent = parent_baton;
db->pool = pool;
db->path = full_path;
db->was_copied = was_copied;
db->base_rev = base_revision;
return db;
}
/* This function is the shared guts of add_file() and add_directory(),
which see for the meanings of the parameters. The only extra
parameter here is IS_DIR, which is TRUE when adding a directory,
and FALSE when adding a file. */
static svn_error_t *
add_file_or_directory(const char *path,
void *parent_baton,
const char *copy_path,
svn_revnum_t copy_revision,
svn_boolean_t is_dir,
apr_pool_t *pool,
void **return_baton)
{
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
apr_pool_t *subpool = svn_pool_create(pool);
svn_boolean_t was_copied = FALSE;
const char *full_path;
/* Reject paths which contain control characters (related to issue #4340). */
SVN_ERR(svn_path_check_valid(path, pool));
full_path = svn_fspath__join(eb->base_path,
svn_relpath_canonicalize(path, pool), pool);
/* Sanity check. */
if (copy_path && (! SVN_IS_VALID_REVNUM(copy_revision)))
return svn_error_createf
(SVN_ERR_FS_GENERAL, NULL,
_("Got source path but no source revision for '%s'"), full_path);
if (copy_path)
{
const char *fs_path;
svn_fs_root_t *copy_root;
svn_node_kind_t kind;
size_t repos_url_len;
svn_repos_authz_access_t required;
/* Copy requires recursive write access to the destination path
and write access to the parent path. */
required = svn_authz_write | (is_dir ? svn_authz_recursive : 0);
SVN_ERR(check_authz(eb, full_path, eb->txn_root,
required, subpool));
SVN_ERR(check_authz(eb, pb->path, eb->txn_root,
svn_authz_write, subpool));
/* Check PATH in our transaction. Make sure it does not exist
unless its parent directory was copied (in which case, the
thing might have been copied in as well), else return an
out-of-dateness error. */
SVN_ERR(svn_fs_check_path(&kind, eb->txn_root, full_path, subpool));
if ((kind != svn_node_none) && (! pb->was_copied))
return svn_error_trace(out_of_date(full_path, kind));
/* For now, require that the url come from the same repository
that this commit is operating on. */
copy_path = svn_path_uri_decode(copy_path, subpool);
repos_url_len = strlen(eb->repos_url);
if (strncmp(copy_path, eb->repos_url, repos_url_len) != 0)
return svn_error_createf
(SVN_ERR_FS_GENERAL, NULL,
_("Source url '%s' is from different repository"), copy_path);
fs_path = apr_pstrdup(subpool, copy_path + repos_url_len);
/* Now use the "fs_path" as an absolute path within the
repository to make the copy from. */
SVN_ERR(svn_fs_revision_root(&copy_root, eb->fs,
copy_revision, subpool));
/* Copy also requires (recursive) read access to the source */
required = svn_authz_read | (is_dir ? svn_authz_recursive : 0);
SVN_ERR(check_authz(eb, fs_path, copy_root, required, subpool));
SVN_ERR(svn_fs_copy(copy_root, fs_path,
eb->txn_root, full_path, subpool));
was_copied = TRUE;
}
else
{
/* No ancestry given, just make a new directory or empty file.
Note that we don't perform an existence check here like the
copy-from case does -- that's because svn_fs_make_*()
already errors out if the file already exists. Verify write
access to the full path and to the parent. */
SVN_ERR(check_authz(eb, full_path, eb->txn_root,
svn_authz_write, subpool));
SVN_ERR(check_authz(eb, pb->path, eb->txn_root,
svn_authz_write, subpool));
if (is_dir)
SVN_ERR(svn_fs_make_dir(eb->txn_root, full_path, subpool));
else
SVN_ERR(svn_fs_make_file(eb->txn_root, full_path, subpool));
}
/* Cleanup our temporary subpool. */
svn_pool_destroy(subpool);
/* Build a new child baton. */
if (is_dir)
{
*return_baton = make_dir_baton(eb, pb, full_path, was_copied,
SVN_INVALID_REVNUM, pool);
}
else
{
struct file_baton *new_fb = apr_pcalloc(pool, sizeof(*new_fb));
new_fb->edit_baton = eb;
new_fb->path = full_path;
*return_baton = new_fb;
}
return SVN_NO_ERROR;
}
/*** Editor functions ***/
static svn_error_t *
open_root(void *edit_baton,
svn_revnum_t base_revision,
apr_pool_t *pool,
void **root_baton)
{
struct dir_baton *dirb;
struct edit_baton *eb = edit_baton;
svn_revnum_t youngest;
/* Ignore BASE_REVISION. We always build our transaction against
HEAD. However, we will keep it in our dir baton for out of
dateness checks. */
SVN_ERR(svn_fs_youngest_rev(&youngest, eb->fs, eb->pool));
/* Unless we've been instructed to use a specific transaction, we'll
make our own. */
if (eb->txn_owner)
{
SVN_ERR(svn_repos_fs_begin_txn_for_commit2(&(eb->txn),
eb->repos,
youngest,
eb->revprop_table,
eb->pool));
}
else /* Even if we aren't the owner of the transaction, we might
have been instructed to set some properties. */
{
apr_array_header_t *props = svn_prop_hash_to_array(eb->revprop_table,
pool);
SVN_ERR(svn_repos_fs_change_txn_props(eb->txn, props, pool));
}
SVN_ERR(svn_fs_txn_name(&(eb->txn_name), eb->txn, eb->pool));
SVN_ERR(svn_fs_txn_root(&(eb->txn_root), eb->txn, eb->pool));
/* Create a root dir baton. The `base_path' field is an -absolute-
path in the filesystem, upon which all further editor paths are
based. */
dirb = apr_pcalloc(pool, sizeof(*dirb));
dirb->edit_baton = edit_baton;
dirb->parent = NULL;
dirb->pool = pool;
dirb->was_copied = FALSE;
dirb->path = apr_pstrdup(pool, eb->base_path);
dirb->base_rev = base_revision;
*root_baton = dirb;
return SVN_NO_ERROR;
}
static svn_error_t *
delete_entry(const char *path,
svn_revnum_t revision,
void *parent_baton,
apr_pool_t *pool)
{
struct dir_baton *parent = parent_baton;
struct edit_baton *eb = parent->edit_baton;
svn_node_kind_t kind;
svn_revnum_t cr_rev;
svn_repos_authz_access_t required = svn_authz_write;
const char *full_path;
full_path = svn_fspath__join(eb->base_path,
svn_relpath_canonicalize(path, pool), pool);
/* Check PATH in our transaction. */
SVN_ERR(svn_fs_check_path(&kind, eb->txn_root, full_path, pool));
/* Deletion requires a recursive write access, as well as write
access to the parent directory. */
if (kind == svn_node_dir)
required |= svn_authz_recursive;
SVN_ERR(check_authz(eb, full_path, eb->txn_root,
required, pool));
SVN_ERR(check_authz(eb, parent->path, eb->txn_root,
svn_authz_write, pool));
/* If PATH doesn't exist in the txn, the working copy is out of date. */
if (kind == svn_node_none)
return svn_error_trace(out_of_date(full_path, kind));
/* Now, make sure we're deleting the node we *think* we're
deleting, else return an out-of-dateness error. */
SVN_ERR(svn_fs_node_created_rev(&cr_rev, eb->txn_root, full_path, pool));
if (SVN_IS_VALID_REVNUM(revision) && (revision < cr_rev))
return svn_error_trace(out_of_date(full_path, kind));
/* This routine is a mindless wrapper. We call svn_fs_delete()
because that will delete files and recursively delete
directories. */
return svn_fs_delete(eb->txn_root, full_path, pool);
}
static svn_error_t *
add_directory(const char *path,
void *parent_baton,
const char *copy_path,
svn_revnum_t copy_revision,
apr_pool_t *pool,
void **child_baton)
{
return add_file_or_directory(path, parent_baton, copy_path, copy_revision,
TRUE /* is_dir */, pool, child_baton);
}
static svn_error_t *
open_directory(const char *path,
void *parent_baton,
svn_revnum_t base_revision,
apr_pool_t *pool,
void **child_baton)
{
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
svn_node_kind_t kind;
const char *full_path;
full_path = svn_fspath__join(eb->base_path,
svn_relpath_canonicalize(path, pool), pool);
/* Check PATH in our transaction. If it does not exist,
return a 'Path not present' error. */
SVN_ERR(svn_fs_check_path(&kind, eb->txn_root, full_path, pool));
if (kind == svn_node_none)
return svn_error_createf(SVN_ERR_FS_NOT_DIRECTORY, NULL,
_("Path '%s' not present"),
path);
/* Build a new dir baton for this directory. */
*child_baton = make_dir_baton(eb, pb, full_path, pb->was_copied,
base_revision, pool);
return SVN_NO_ERROR;
}
static svn_error_t *
apply_textdelta(void *file_baton,
const char *base_checksum,
apr_pool_t *pool,
svn_txdelta_window_handler_t *handler,
void **handler_baton)
{
struct file_baton *fb = file_baton;
/* Check for write authorization. */
SVN_ERR(check_authz(fb->edit_baton, fb->path,
fb->edit_baton->txn_root,
svn_authz_write, pool));
return svn_fs_apply_textdelta(handler, handler_baton,
fb->edit_baton->txn_root,
fb->path,
base_checksum,
NULL,
pool);
}
static svn_error_t *
add_file(const char *path,
void *parent_baton,
const char *copy_path,
svn_revnum_t copy_revision,
apr_pool_t *pool,
void **file_baton)
{
return add_file_or_directory(path, parent_baton, copy_path, copy_revision,
FALSE /* is_dir */, pool, file_baton);
}
static svn_error_t *
open_file(const char *path,
void *parent_baton,
svn_revnum_t base_revision,
apr_pool_t *pool,
void **file_baton)
{
struct file_baton *new_fb;
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
svn_revnum_t cr_rev;
apr_pool_t *subpool = svn_pool_create(pool);
const char *full_path;
full_path = svn_fspath__join(eb->base_path,
svn_relpath_canonicalize(path, pool), pool);
/* Check for read authorization. */
SVN_ERR(check_authz(eb, full_path, eb->txn_root,
svn_authz_read, subpool));
/* Get this node's creation revision (doubles as an existence check). */
SVN_ERR(svn_fs_node_created_rev(&cr_rev, eb->txn_root, full_path,
subpool));
/* If the node our caller has is an older revision number than the
one in our transaction, return an out-of-dateness error. */
if (SVN_IS_VALID_REVNUM(base_revision) && (base_revision < cr_rev))
return svn_error_trace(out_of_date(full_path, svn_node_file));
/* Build a new file baton */
new_fb = apr_pcalloc(pool, sizeof(*new_fb));
new_fb->edit_baton = eb;
new_fb->path = full_path;
*file_baton = new_fb;
/* Destory the work subpool. */
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
static svn_error_t *
change_file_prop(void *file_baton,
const char *name,
const svn_string_t *value,
apr_pool_t *pool)
{
struct file_baton *fb = file_baton;
struct edit_baton *eb = fb->edit_baton;
/* Check for write authorization. */
SVN_ERR(check_authz(eb, fb->path, eb->txn_root,
svn_authz_write, pool));
return svn_repos_fs_change_node_prop(eb->txn_root, fb->path,
name, value, pool);
}
static svn_error_t *
close_file(void *file_baton,
const char *text_digest,
apr_pool_t *pool)
{
struct file_baton *fb = file_baton;
if (text_digest)
{
svn_checksum_t *checksum;
svn_checksum_t *text_checksum;
SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5,
fb->edit_baton->txn_root, fb->path,
TRUE, pool));
SVN_ERR(svn_checksum_parse_hex(&text_checksum, svn_checksum_md5,
text_digest, pool));
if (!svn_checksum_match(text_checksum, checksum))
return svn_checksum_mismatch_err(text_checksum, checksum, pool,
_("Checksum mismatch for resulting fulltext\n(%s)"),
fb->path);
}
return SVN_NO_ERROR;
}
static svn_error_t *
change_dir_prop(void *dir_baton,
const char *name,
const svn_string_t *value,
apr_pool_t *pool)
{
struct dir_baton *db = dir_baton;
struct edit_baton *eb = db->edit_baton;
/* Check for write authorization. */
SVN_ERR(check_authz(eb, db->path, eb->txn_root,
svn_authz_write, pool));
if (SVN_IS_VALID_REVNUM(db->base_rev))
{
/* Subversion rule: propchanges can only happen on a directory
which is up-to-date. */
svn_revnum_t created_rev;
SVN_ERR(svn_fs_node_created_rev(&created_rev,
eb->txn_root, db->path, pool));
if (db->base_rev < created_rev)
return svn_error_trace(out_of_date(db->path, svn_node_dir));
}
return svn_repos_fs_change_node_prop(eb->txn_root, db->path,
name, value, pool);
}
const char *
svn_repos__post_commit_error_str(svn_error_t *err,
apr_pool_t *pool)
{
svn_error_t *hook_err1, *hook_err2;
const char *msg;
if (! err)
return _("(no error)");
err = svn_error_purge_tracing(err);
/* hook_err1 is the SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED wrapped
error from the post-commit script, if any, and hook_err2 should
be the original error, but be defensive and handle a case where
SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED doesn't wrap an error. */
hook_err1 = svn_error_find_cause(err, SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED);
if (hook_err1 && hook_err1->child)
hook_err2 = hook_err1->child;
else
hook_err2 = hook_err1;
/* This implementation counts on svn_repos_fs_commit_txn() and
libsvn_repos/commit.c:complete_cb() returning
svn_fs_commit_txn() as the parent error with a child
SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED error. If the parent error
is SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED then there was no error
in svn_fs_commit_txn().
The post-commit hook error message is already self describing, so
it can be dropped into an error message without any additional
text. */
if (hook_err1)
{
if (err == hook_err1)
{
if (hook_err2->message)
msg = apr_pstrdup(pool, hook_err2->message);
else
msg = _("post-commit hook failed with no error message.");
}
else
{
msg = hook_err2->message
? apr_pstrdup(pool, hook_err2->message)
: _("post-commit hook failed with no error message.");
msg = apr_psprintf(
pool,
_("post commit FS processing had error:\n%s\n%s"),
err->message ? err->message : _("(no error message)"),
msg);
}
}
else
{
msg = apr_psprintf(pool,
_("post commit FS processing had error:\n%s"),
err->message ? err->message
: _("(no error message)"));
}
return msg;
}
static svn_error_t *
close_edit(void *edit_baton,
apr_pool_t *pool)
{
struct edit_baton *eb = edit_baton;
svn_revnum_t new_revision = SVN_INVALID_REVNUM;
svn_error_t *err;
const char *conflict;
const char *post_commit_err = NULL;
/* If no transaction has been created (ie. if open_root wasn't
called before close_edit), abort the operation here with an
error. */
if (! eb->txn)
return svn_error_create(SVN_ERR_REPOS_BAD_ARGS, NULL,
"No valid transaction supplied to close_edit");
/* Commit. */
err = svn_repos_fs_commit_txn(&conflict, eb->repos,
&new_revision, eb->txn, pool);
if (SVN_IS_VALID_REVNUM(new_revision))
{
/* The actual commit succeeded, i.e. the transaction does no longer
exist and we can't use txn_root for conflict resolution etc.
Since close_edit is supposed to release resources, do it now. */
if (eb->txn_root)
svn_fs_close_root(eb->txn_root);
if (err)
{
/* If the error was in post-commit, then the commit itself
succeeded. In which case, save the post-commit warning
(to be reported back to the client, who will probably
display it as a warning) and clear the error. */
post_commit_err = svn_repos__post_commit_error_str(err, pool);
svn_error_clear(err);
}
+
+ /* Make sure a future abort doesn't perform
+ any work. This may occur if the commit
+ callback returns an error! */
+
+ eb->txn = NULL;
+ eb->txn_root = NULL;
}
else
{
/* ### todo: we should check whether it really was a conflict,
and return the conflict info if so? */
/* If the commit failed, it's *probably* due to a conflict --
that is, the txn being out-of-date. The filesystem gives us
the ability to continue diddling the transaction and try
again; but let's face it: that's not how the cvs or svn works
from a user interface standpoint. Thus we don't make use of
this fs feature (for now, at least.)
So, in a nutshell: svn commits are an all-or-nothing deal.
Each commit creates a new fs txn which either succeeds or is
aborted completely. No second chances; the user simply
needs to update and commit again :) */
eb->txn_aborted = TRUE;
return svn_error_trace(
svn_error_compose_create(err,
svn_fs_abort_txn(eb->txn, pool)));
}
/* At this point, the post-commit error has been converted to a string.
That information will be passed to a callback, if provided. If the
callback invocation fails in some way, that failure is returned here.
IOW, the post-commit error information is low priority compared to
other gunk here. */
/* Pass new revision information to the caller's callback. */
return svn_error_trace(invoke_commit_cb(eb->commit_callback,
eb->commit_callback_baton,
eb->repos->fs,
new_revision,
post_commit_err,
pool));
}
static svn_error_t *
abort_edit(void *edit_baton,
apr_pool_t *pool)
{
struct edit_baton *eb = edit_baton;
if ((! eb->txn) || (! eb->txn_owner) || eb->txn_aborted)
return SVN_NO_ERROR;
eb->txn_aborted = TRUE;
/* Since abort_edit is supposed to release resources, do it now. */
if (eb->txn_root)
svn_fs_close_root(eb->txn_root);
return svn_error_trace(svn_fs_abort_txn(eb->txn, pool));
}
static svn_error_t *
fetch_props_func(apr_hash_t **props,
void *baton,
const char *path,
svn_revnum_t base_revision,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
struct edit_baton *eb = baton;
svn_fs_root_t *fs_root;
svn_error_t *err;
SVN_ERR(svn_fs_revision_root(&fs_root, eb->fs,
svn_fs_txn_base_revision(eb->txn),
scratch_pool));
err = svn_fs_node_proplist(props, fs_root, path, result_pool);
if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
{
svn_error_clear(err);
*props = apr_hash_make(result_pool);
return SVN_NO_ERROR;
}
else if (err)
return svn_error_trace(err);
return SVN_NO_ERROR;
}
static svn_error_t *
fetch_kind_func(svn_node_kind_t *kind,
void *baton,
const char *path,
svn_revnum_t base_revision,
apr_pool_t *scratch_pool)
{
struct edit_baton *eb = baton;
svn_fs_root_t *fs_root;
if (!SVN_IS_VALID_REVNUM(base_revision))
base_revision = svn_fs_txn_base_revision(eb->txn);
SVN_ERR(svn_fs_revision_root(&fs_root, eb->fs, base_revision, scratch_pool));
SVN_ERR(svn_fs_check_path(kind, fs_root, path, scratch_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
fetch_base_func(const char **filename,
void *baton,
const char *path,
svn_revnum_t base_revision,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
struct edit_baton *eb = baton;
svn_stream_t *contents;
svn_stream_t *file_stream;
const char *tmp_filename;
svn_fs_root_t *fs_root;
svn_error_t *err;
if (!SVN_IS_VALID_REVNUM(base_revision))
base_revision = svn_fs_txn_base_revision(eb->txn);
SVN_ERR(svn_fs_revision_root(&fs_root, eb->fs, base_revision, scratch_pool));
err = svn_fs_file_contents(&contents, fs_root, path, scratch_pool);
if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
{
svn_error_clear(err);
*filename = NULL;
return SVN_NO_ERROR;
}
else if (err)
return svn_error_trace(err);
SVN_ERR(svn_stream_open_unique(&file_stream, &tmp_filename, NULL,
svn_io_file_del_on_pool_cleanup,
scratch_pool, scratch_pool));
SVN_ERR(svn_stream_copy3(contents, file_stream, NULL, NULL, scratch_pool));
*filename = apr_pstrdup(result_pool, tmp_filename);
return SVN_NO_ERROR;
}
/*** Public interfaces. ***/
svn_error_t *
svn_repos_get_commit_editor5(const svn_delta_editor_t **editor,
void **edit_baton,
svn_repos_t *repos,
svn_fs_txn_t *txn,
const char *repos_url,
const char *base_path,
apr_hash_t *revprop_table,
svn_commit_callback2_t commit_callback,
void *commit_baton,
svn_repos_authz_callback_t authz_callback,
void *authz_baton,
apr_pool_t *pool)
{
svn_delta_editor_t *e;
apr_pool_t *subpool = svn_pool_create(pool);
struct edit_baton *eb;
svn_delta_shim_callbacks_t *shim_callbacks =
svn_delta_shim_callbacks_default(pool);
/* Do a global authz access lookup. Users with no write access
whatsoever to the repository don't get a commit editor. */
if (authz_callback)
{
svn_boolean_t allowed;
SVN_ERR(authz_callback(svn_authz_write, &allowed, NULL, NULL,
authz_baton, pool));
if (!allowed)
return svn_error_create(SVN_ERR_AUTHZ_UNWRITABLE, NULL,
"Not authorized to open a commit editor.");
}
/* Allocate the structures. */
e = svn_delta_default_editor(pool);
eb = apr_pcalloc(subpool, sizeof(*eb));
/* Set up the editor. */
e->open_root = open_root;
e->delete_entry = delete_entry;
e->add_directory = add_directory;
e->open_directory = open_directory;
e->change_dir_prop = change_dir_prop;
e->add_file = add_file;
e->open_file = open_file;
e->close_file = close_file;
e->apply_textdelta = apply_textdelta;
e->change_file_prop = change_file_prop;
e->close_edit = close_edit;
e->abort_edit = abort_edit;
/* Set up the edit baton. */
eb->pool = subpool;
eb->revprop_table = svn_prop_hash_dup(revprop_table, subpool);
eb->commit_callback = commit_callback;
eb->commit_callback_baton = commit_baton;
eb->authz_callback = authz_callback;
eb->authz_baton = authz_baton;
eb->base_path = svn_fspath__canonicalize(base_path, subpool);
eb->repos = repos;
eb->repos_url = repos_url;
eb->repos_name = svn_dirent_basename(svn_repos_path(repos, subpool),
subpool);
eb->fs = svn_repos_fs(repos);
eb->txn = txn;
eb->txn_owner = txn == NULL;
*edit_baton = eb;
*editor = e;
shim_callbacks->fetch_props_func = fetch_props_func;
shim_callbacks->fetch_kind_func = fetch_kind_func;
shim_callbacks->fetch_base_func = fetch_base_func;
shim_callbacks->fetch_baton = eb;
SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
eb->repos_url, eb->base_path,
shim_callbacks, pool, pool));
return SVN_NO_ERROR;
}
#if 0
static svn_error_t *
ev2_check_authz(const struct ev2_baton *eb,
const char *relpath,
svn_repos_authz_access_t required,
apr_pool_t *scratch_pool)
{
const char *fspath;
svn_boolean_t allowed;
if (eb->authz == NULL)
return SVN_NO_ERROR;
if (relpath)
fspath = apr_pstrcat(scratch_pool, "/", relpath, NULL);
else
fspath = NULL;
SVN_ERR(svn_repos_authz_check_access(eb->authz, eb->authz_repos_name, fspath,
eb->authz_user, required,
&allowed, scratch_pool));
if (!allowed)
return svn_error_create(required & svn_authz_write
? SVN_ERR_AUTHZ_UNWRITABLE
: SVN_ERR_AUTHZ_UNREADABLE,
NULL, "Access denied");
return SVN_NO_ERROR;
}
#endif
/* This implements svn_editor_cb_add_directory_t */
static svn_error_t *
add_directory_cb(void *baton,
const char *relpath,
const apr_array_header_t *children,
apr_hash_t *props,
svn_revnum_t replaces_rev,
apr_pool_t *scratch_pool)
{
struct ev2_baton *eb = baton;
SVN_ERR(svn_editor_add_directory(eb->inner, relpath, children, props,
replaces_rev));
return SVN_NO_ERROR;
}
/* This implements svn_editor_cb_add_file_t */
static svn_error_t *
add_file_cb(void *baton,
const char *relpath,
const svn_checksum_t *checksum,
svn_stream_t *contents,
apr_hash_t *props,
svn_revnum_t replaces_rev,
apr_pool_t *scratch_pool)
{
struct ev2_baton *eb = baton;
SVN_ERR(svn_editor_add_file(eb->inner, relpath, checksum, contents, props,
replaces_rev));
return SVN_NO_ERROR;
}
/* This implements svn_editor_cb_add_symlink_t */
static svn_error_t *
add_symlink_cb(void *baton,
const char *relpath,
const char *target,
apr_hash_t *props,
svn_revnum_t replaces_rev,
apr_pool_t *scratch_pool)
{
struct ev2_baton *eb = baton;
SVN_ERR(svn_editor_add_symlink(eb->inner, relpath, target, props,
replaces_rev));
return SVN_NO_ERROR;
}
/* This implements svn_editor_cb_add_absent_t */
static svn_error_t *
add_absent_cb(void *baton,
const char *relpath,
svn_node_kind_t kind,
svn_revnum_t replaces_rev,
apr_pool_t *scratch_pool)
{
struct ev2_baton *eb = baton;
SVN_ERR(svn_editor_add_absent(eb->inner, relpath, kind, replaces_rev));
return SVN_NO_ERROR;
}
/* This implements svn_editor_cb_alter_directory_t */
static svn_error_t *
alter_directory_cb(void *baton,
const char *relpath,
svn_revnum_t revision,
const apr_array_header_t *children,
apr_hash_t *props,
apr_pool_t *scratch_pool)
{
struct ev2_baton *eb = baton;
SVN_ERR(svn_editor_alter_directory(eb->inner, relpath, revision,
children, props));
return SVN_NO_ERROR;
}
/* This implements svn_editor_cb_alter_file_t */
static svn_error_t *
alter_file_cb(void *baton,
const char *relpath,
svn_revnum_t revision,
apr_hash_t *props,
const svn_checksum_t *checksum,
svn_stream_t *contents,
apr_pool_t *scratch_pool)
{
struct ev2_baton *eb = baton;
SVN_ERR(svn_editor_alter_file(eb->inner, relpath, revision, props,
checksum, contents));
return SVN_NO_ERROR;
}
/* This implements svn_editor_cb_alter_symlink_t */
static svn_error_t *
alter_symlink_cb(void *baton,
const char *relpath,
svn_revnum_t revision,
apr_hash_t *props,
const char *target,
apr_pool_t *scratch_pool)
{
struct ev2_baton *eb = baton;
SVN_ERR(svn_editor_alter_symlink(eb->inner, relpath, revision, props,
target));
return SVN_NO_ERROR;
}
/* This implements svn_editor_cb_delete_t */
static svn_error_t *
delete_cb(void *baton,
const char *relpath,
svn_revnum_t revision,
apr_pool_t *scratch_pool)
{
struct ev2_baton *eb = baton;
SVN_ERR(svn_editor_delete(eb->inner, relpath, revision));
return SVN_NO_ERROR;
}
/* This implements svn_editor_cb_copy_t */
static svn_error_t *
copy_cb(void *baton,
const char *src_relpath,
svn_revnum_t src_revision,
const char *dst_relpath,
svn_revnum_t replaces_rev,
apr_pool_t *scratch_pool)
{
struct ev2_baton *eb = baton;
SVN_ERR(svn_editor_copy(eb->inner, src_relpath, src_revision, dst_relpath,
replaces_rev));
return SVN_NO_ERROR;
}
/* This implements svn_editor_cb_move_t */
static svn_error_t *
move_cb(void *baton,
const char *src_relpath,
svn_revnum_t src_revision,
const char *dst_relpath,
svn_revnum_t replaces_rev,
apr_pool_t *scratch_pool)
{
struct ev2_baton *eb = baton;
SVN_ERR(svn_editor_move(eb->inner, src_relpath, src_revision, dst_relpath,
replaces_rev));
return SVN_NO_ERROR;
}
/* This implements svn_editor_cb_rotate_t */
static svn_error_t *
rotate_cb(void *baton,
const apr_array_header_t *relpaths,
const apr_array_header_t *revisions,
apr_pool_t *scratch_pool)
{
struct ev2_baton *eb = baton;
SVN_ERR(svn_editor_rotate(eb->inner, relpaths, revisions));
return SVN_NO_ERROR;
}
/* This implements svn_editor_cb_complete_t */
static svn_error_t *
complete_cb(void *baton,
apr_pool_t *scratch_pool)
{
struct ev2_baton *eb = baton;
svn_revnum_t revision;
svn_error_t *post_commit_err;
const char *conflict_path;
svn_error_t *err;
const char *post_commit_errstr;
apr_hash_t *hooks_env;
/* Parse the hooks-env file (if any). */
SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, eb->repos->hooks_env_path,
scratch_pool, scratch_pool));
/* The transaction has been fully edited. Let the pre-commit hook
have a look at the thing. */
SVN_ERR(svn_repos__hooks_pre_commit(eb->repos, hooks_env,
eb->txn_name, scratch_pool));
/* Hook is done. Let's do the actual commit. */
SVN_ERR(svn_fs__editor_commit(&revision, &post_commit_err, &conflict_path,
eb->inner, scratch_pool, scratch_pool));
/* Did a conflict occur during the commit process? */
if (conflict_path != NULL)
return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL,
_("Conflict at '%s'"), conflict_path);
/* Since did not receive an error during the commit process, and no
conflict was specified... we committed a revision. Run the hooks.
Other errors may have occurred within the FS (specified by the
POST_COMMIT_ERR localvar), but we need to run the hooks. */
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
err = svn_repos__hooks_post_commit(eb->repos, hooks_env, revision,
eb->txn_name, scratch_pool);
if (err)
err = svn_error_create(SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED, err,
_("Commit succeeded, but post-commit hook failed"));
/* Combine the FS errors with the hook errors, and stringify. */
err = svn_error_compose_create(post_commit_err, err);
if (err)
{
post_commit_errstr = svn_repos__post_commit_error_str(err, scratch_pool);
svn_error_clear(err);
}
else
{
post_commit_errstr = NULL;
}
return svn_error_trace(invoke_commit_cb(eb->commit_cb, eb->commit_baton,
eb->repos->fs, revision,
post_commit_errstr,
scratch_pool));
}
/* This implements svn_editor_cb_abort_t */
static svn_error_t *
abort_cb(void *baton,
apr_pool_t *scratch_pool)
{
struct ev2_baton *eb = baton;
SVN_ERR(svn_editor_abort(eb->inner));
return SVN_NO_ERROR;
}
static svn_error_t *
apply_revprops(svn_fs_t *fs,
const char *txn_name,
apr_hash_t *revprops,
apr_pool_t *scratch_pool)
{
svn_fs_txn_t *txn;
const apr_array_header_t *revprops_array;
/* The FS editor has a TXN inside it, but we can't access it. Open another
based on the TXN_NAME. */
SVN_ERR(svn_fs_open_txn(&txn, fs, txn_name, scratch_pool));
/* Validate and apply the revision properties. */
revprops_array = svn_prop_hash_to_array(revprops, scratch_pool);
SVN_ERR(svn_repos_fs_change_txn_props(txn, revprops_array, scratch_pool));
/* ### do we need to force the txn to close, or is it enough to wait
### for the pool to be cleared? */
return SVN_NO_ERROR;
}
svn_error_t *
svn_repos__get_commit_ev2(svn_editor_t **editor,
svn_repos_t *repos,
svn_authz_t *authz,
const char *authz_repos_name,
const char *authz_user,
apr_hash_t *revprops,
svn_commit_callback2_t commit_cb,
void *commit_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
static const svn_editor_cb_many_t editor_cbs = {
add_directory_cb,
add_file_cb,
add_symlink_cb,
add_absent_cb,
alter_directory_cb,
alter_file_cb,
alter_symlink_cb,
delete_cb,
copy_cb,
move_cb,
rotate_cb,
complete_cb,
abort_cb
};
struct ev2_baton *eb;
const svn_string_t *author;
apr_hash_t *hooks_env;
/* Parse the hooks-env file (if any). */
SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path,
scratch_pool, scratch_pool));
/* Can the user modify the repository at all? */
/* ### check against AUTHZ. */
author = svn_hash_gets(revprops, SVN_PROP_REVISION_AUTHOR);
eb = apr_palloc(result_pool, sizeof(*eb));
eb->repos = repos;
eb->authz = authz;
eb->authz_repos_name = authz_repos_name;
eb->authz_user = authz_user;
eb->commit_cb = commit_cb;
eb->commit_baton = commit_baton;
SVN_ERR(svn_fs__editor_create(&eb->inner, &eb->txn_name,
repos->fs, SVN_FS_TXN_CHECK_LOCKS,
cancel_func, cancel_baton,
result_pool, scratch_pool));
/* The TXN has been created. Go ahead and apply all revision properties. */
SVN_ERR(apply_revprops(repos->fs, eb->txn_name, revprops, scratch_pool));
/* Okay... some access is allowed. Let's run the start-commit hook. */
SVN_ERR(svn_repos__hooks_start_commit(repos, hooks_env,
author ? author->data : NULL,
repos->client_capabilities,
eb->txn_name, scratch_pool));
/* Wrap the FS editor within our editor. */
SVN_ERR(svn_editor_create(editor, eb, cancel_func, cancel_baton,
result_pool, scratch_pool));
SVN_ERR(svn_editor_setcb_many(*editor, &editor_cbs, scratch_pool));
return SVN_NO_ERROR;
}
Index: vendor/subversion/dist/subversion/libsvn_repos/load-fs-vtable.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_repos/load-fs-vtable.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_repos/load-fs-vtable.c (revision 286501)
@@ -1,1140 +1,1213 @@
/* load-fs-vtable.c --- dumpstream loader vtable for committing into a
* Subversion filesystem.
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include "svn_private_config.h"
#include "svn_hash.h"
#include "svn_pools.h"
#include "svn_error.h"
#include "svn_fs.h"
#include "svn_repos.h"
#include "svn_string.h"
#include "svn_props.h"
#include "repos.h"
#include "svn_private_config.h"
#include "svn_mergeinfo.h"
#include "svn_checksum.h"
#include "svn_subst.h"
#include "svn_ctype.h"
#include "svn_dirent_uri.h"
#include <apr_lib.h>
+#include "private/svn_repos_private.h"
#include "private/svn_fspath.h"
#include "private/svn_dep_compat.h"
#include "private/svn_mergeinfo_private.h"
/*----------------------------------------------------------------------*/
/** Batons used herein **/
struct parse_baton
{
svn_repos_t *repos;
svn_fs_t *fs;
svn_boolean_t use_history;
svn_boolean_t validate_props;
svn_boolean_t use_pre_commit_hook;
svn_boolean_t use_post_commit_hook;
enum svn_repos_load_uuid uuid_action;
const char *parent_dir; /* repository relpath, or NULL */
svn_repos_notify_func_t notify_func;
void *notify_baton;
- svn_repos_notify_t *notify;
+ apr_pool_t *notify_pool; /* scratch pool for notifications */
apr_pool_t *pool;
/* Start and end (inclusive) of revision range we'll pay attention
to, or a pair of SVN_INVALID_REVNUMs if we're not filtering by
revisions. */
svn_revnum_t start_rev;
svn_revnum_t end_rev;
/* A hash mapping copy-from revisions and mergeinfo range revisions
(svn_revnum_t *) in the dump stream to their corresponding revisions
(svn_revnum_t *) in the loaded repository. The hash and its
contents are allocated in POOL. */
/* ### See http://subversion.tigris.org/issues/show_bug.cgi?id=3903
### for discussion about improving the memory costs of this mapping. */
apr_hash_t *rev_map;
/* The most recent (youngest) revision from the dump stream mapped in
REV_MAP. If no revisions have been mapped yet, this is set to
SVN_INVALID_REVNUM. */
svn_revnum_t last_rev_mapped;
/* The oldest old revision loaded from the dump stream. If no revisions
have been loaded yet, this is set to SVN_INVALID_REVNUM. */
svn_revnum_t oldest_old_rev;
};
struct revision_baton
{
svn_revnum_t rev;
svn_fs_txn_t *txn;
svn_fs_root_t *txn_root;
const svn_string_t *datestamp;
apr_int32_t rev_offset;
svn_boolean_t skipped;
struct parse_baton *pb;
apr_pool_t *pool;
};
struct node_baton
{
const char *path;
svn_node_kind_t kind;
enum svn_node_action action;
svn_checksum_t *base_checksum; /* null, if not available */
svn_checksum_t *result_checksum; /* null, if not available */
svn_checksum_t *copy_source_checksum; /* null, if not available */
svn_revnum_t copyfrom_rev;
const char *copyfrom_path;
struct revision_baton *rb;
apr_pool_t *pool;
};
/*----------------------------------------------------------------------*/
/* Record the mapping of FROM_REV to TO_REV in REV_MAP, ensuring that
anything added to the hash is allocated in the hash's pool. */
static void
set_revision_mapping(apr_hash_t *rev_map,
svn_revnum_t from_rev,
svn_revnum_t to_rev)
{
svn_revnum_t *mapped_revs = apr_palloc(apr_hash_pool_get(rev_map),
sizeof(svn_revnum_t) * 2);
mapped_revs[0] = from_rev;
mapped_revs[1] = to_rev;
apr_hash_set(rev_map, mapped_revs,
sizeof(svn_revnum_t), mapped_revs + 1);
}
/* Return the revision to which FROM_REV maps in REV_MAP, or
SVN_INVALID_REVNUM if no such mapping exists. */
static svn_revnum_t
get_revision_mapping(apr_hash_t *rev_map,
svn_revnum_t from_rev)
{
svn_revnum_t *to_rev = apr_hash_get(rev_map, &from_rev,
sizeof(from_rev));
return to_rev ? *to_rev : SVN_INVALID_REVNUM;
}
/* Change revision property NAME to VALUE for REVISION in REPOS. If
VALIDATE_PROPS is set, use functions which perform validation of
the property value. Otherwise, bypass those checks. */
static svn_error_t *
change_rev_prop(svn_repos_t *repos,
svn_revnum_t revision,
const char *name,
const svn_string_t *value,
svn_boolean_t validate_props,
apr_pool_t *pool)
{
if (validate_props)
return svn_repos_fs_change_rev_prop4(repos, revision, NULL, name,
NULL, value, FALSE, FALSE,
NULL, NULL, pool);
else
return svn_fs_change_rev_prop2(svn_repos_fs(repos), revision, name,
NULL, value, pool);
}
/* Change property NAME to VALUE for PATH in TXN_ROOT. If
VALIDATE_PROPS is set, use functions which perform validation of
the property value. Otherwise, bypass those checks. */
static svn_error_t *
change_node_prop(svn_fs_root_t *txn_root,
const char *path,
const char *name,
const svn_string_t *value,
svn_boolean_t validate_props,
apr_pool_t *pool)
{
if (validate_props)
return svn_repos_fs_change_node_prop(txn_root, path, name, value, pool);
else
return svn_fs_change_node_prop(txn_root, path, name, value, pool);
}
/* Prepend the mergeinfo source paths in MERGEINFO_ORIG with PARENT_DIR, and
return it in *MERGEINFO_VAL. */
/* ### FIXME: Consider somehow sharing code with
### svnrdump/load_editor.c:prefix_mergeinfo_paths() */
static svn_error_t *
prefix_mergeinfo_paths(svn_string_t **mergeinfo_val,
const svn_string_t *mergeinfo_orig,
const char *parent_dir,
apr_pool_t *pool)
{
apr_hash_t *prefixed_mergeinfo, *mergeinfo;
apr_hash_index_t *hi;
void *rangelist;
SVN_ERR(svn_mergeinfo_parse(&mergeinfo, mergeinfo_orig->data, pool));
prefixed_mergeinfo = apr_hash_make(pool);
for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
{
const void *key;
const char *path, *merge_source;
apr_hash_this(hi, &key, NULL, &rangelist);
merge_source = svn_relpath_canonicalize(key, pool);
/* The svn:mergeinfo property syntax demands a repos abspath */
path = svn_fspath__canonicalize(svn_relpath_join(parent_dir,
merge_source, pool),
pool);
svn_hash_sets(prefixed_mergeinfo, path, rangelist);
}
return svn_mergeinfo_to_string(mergeinfo_val, prefixed_mergeinfo, pool);
}
/* Examine the mergeinfo in INITIAL_VAL, renumber revisions in rangelists
as appropriate, and return the (possibly new) mergeinfo in *FINAL_VAL
(allocated from POOL). */
/* ### FIXME: Consider somehow sharing code with
### svnrdump/load_editor.c:renumber_mergeinfo_revs() */
static svn_error_t *
renumber_mergeinfo_revs(svn_string_t **final_val,
const svn_string_t *initial_val,
struct revision_baton *rb,
apr_pool_t *pool)
{
apr_pool_t *subpool = svn_pool_create(pool);
svn_mergeinfo_t mergeinfo, predates_stream_mergeinfo;
svn_mergeinfo_t final_mergeinfo = apr_hash_make(subpool);
apr_hash_index_t *hi;
SVN_ERR(svn_mergeinfo_parse(&mergeinfo, initial_val->data, subpool));
/* Issue #3020
http://subversion.tigris.org/issues/show_bug.cgi?id=3020#desc16
Remove mergeinfo older than the oldest revision in the dump stream
and adjust its revisions by the difference between the head rev of
the target repository and the current dump stream rev. */
if (rb->pb->oldest_old_rev > 1)
{
SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(
&predates_stream_mergeinfo, mergeinfo,
rb->pb->oldest_old_rev - 1, 0,
TRUE, subpool, subpool));
SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(
&mergeinfo, mergeinfo,
rb->pb->oldest_old_rev - 1, 0,
FALSE, subpool, subpool));
SVN_ERR(svn_mergeinfo__adjust_mergeinfo_rangelists(
&predates_stream_mergeinfo, predates_stream_mergeinfo,
-rb->rev_offset, subpool, subpool));
}
else
{
predates_stream_mergeinfo = NULL;
}
for (hi = apr_hash_first(subpool, mergeinfo); hi; hi = apr_hash_next(hi))
{
const char *merge_source;
svn_rangelist_t *rangelist;
struct parse_baton *pb = rb->pb;
int i;
const void *key;
void *val;
apr_hash_this(hi, &key, NULL, &val);
merge_source = key;
rangelist = val;
/* Possibly renumber revisions in merge source's rangelist. */
for (i = 0; i < rangelist->nelts; i++)
{
svn_revnum_t rev_from_map;
svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i,
svn_merge_range_t *);
rev_from_map = get_revision_mapping(pb->rev_map, range->start);
if (SVN_IS_VALID_REVNUM(rev_from_map))
{
range->start = rev_from_map;
}
else if (range->start == pb->oldest_old_rev - 1)
{
/* Since the start revision of svn_merge_range_t are not
inclusive there is one possible valid start revision that
won't be found in the PB->REV_MAP mapping of load stream
revsions to loaded revisions: The revision immediately
preceeding the oldest revision from the load stream.
This is a valid revision for mergeinfo, but not a valid
copy from revision (which PB->REV_MAP also maps for) so it
will never be in the mapping.
If that is what we have here, then find the mapping for the
oldest rev from the load stream and subtract 1 to get the
renumbered, non-inclusive, start revision. */
rev_from_map = get_revision_mapping(pb->rev_map,
pb->oldest_old_rev);
if (SVN_IS_VALID_REVNUM(rev_from_map))
range->start = rev_from_map - 1;
}
else
{
/* If we can't remap the start revision then don't even bother
trying to remap the end revision. It's possible we might
actually succeed at the latter, which can result in invalid
mergeinfo with a start rev > end rev. If that gets into the
repository then a world of bustage breaks loose anytime that
bogus mergeinfo is parsed. See
http://subversion.tigris.org/issues/show_bug.cgi?id=3020#desc16.
*/
continue;
}
rev_from_map = get_revision_mapping(pb->rev_map, range->end);
if (SVN_IS_VALID_REVNUM(rev_from_map))
range->end = rev_from_map;
}
svn_hash_sets(final_mergeinfo, merge_source, rangelist);
}
if (predates_stream_mergeinfo)
SVN_ERR(svn_mergeinfo_merge2(final_mergeinfo, predates_stream_mergeinfo,
subpool, subpool));
- SVN_ERR(svn_mergeinfo_sort(final_mergeinfo, subpool));
+ SVN_ERR(svn_mergeinfo__canonicalize_ranges(final_mergeinfo, subpool));
- /* Mergeinfo revision sources for r0 and r1 are invalid; you can't merge r0
- or r1. However, svndumpfilter can be abused to produce r1 merge source
- revs. So if we encounter any, then strip them out, no need to put them
- into the load target. */
- SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(&final_mergeinfo,
- final_mergeinfo,
- 1, 0, FALSE,
- subpool, subpool));
-
SVN_ERR(svn_mergeinfo_to_string(final_val, final_mergeinfo, pool));
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
/*----------------------------------------------------------------------*/
/** vtable for doing commits to a fs **/
static svn_error_t *
make_node_baton(struct node_baton **node_baton_p,
apr_hash_t *headers,
struct revision_baton *rb,
apr_pool_t *pool)
{
struct node_baton *nb = apr_pcalloc(pool, sizeof(*nb));
const char *val;
/* Start with sensible defaults. */
nb->rb = rb;
nb->pool = pool;
nb->kind = svn_node_unknown;
/* Then add info from the headers. */
if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_PATH)))
{
val = svn_relpath_canonicalize(val, pool);
if (rb->pb->parent_dir)
nb->path = svn_relpath_join(rb->pb->parent_dir, val, pool);
else
nb->path = val;
}
if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_KIND)))
{
if (! strcmp(val, "file"))
nb->kind = svn_node_file;
else if (! strcmp(val, "dir"))
nb->kind = svn_node_dir;
}
nb->action = (enum svn_node_action)(-1); /* an invalid action code */
if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_ACTION)))
{
if (! strcmp(val, "change"))
nb->action = svn_node_action_change;
else if (! strcmp(val, "add"))
nb->action = svn_node_action_add;
else if (! strcmp(val, "delete"))
nb->action = svn_node_action_delete;
else if (! strcmp(val, "replace"))
nb->action = svn_node_action_replace;
}
nb->copyfrom_rev = SVN_INVALID_REVNUM;
if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV)))
{
nb->copyfrom_rev = SVN_STR_TO_REV(val);
}
if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH)))
{
val = svn_relpath_canonicalize(val, pool);
if (rb->pb->parent_dir)
nb->copyfrom_path = svn_relpath_join(rb->pb->parent_dir, val, pool);
else
nb->copyfrom_path = val;
}
if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_TEXT_CONTENT_CHECKSUM)))
{
SVN_ERR(svn_checksum_parse_hex(&nb->result_checksum, svn_checksum_md5,
val, pool));
}
if ((val = svn_hash_gets(headers,
SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_CHECKSUM)))
{
SVN_ERR(svn_checksum_parse_hex(&nb->base_checksum, svn_checksum_md5, val,
pool));
}
if ((val = svn_hash_gets(headers,
SVN_REPOS_DUMPFILE_TEXT_COPY_SOURCE_CHECKSUM)))
{
SVN_ERR(svn_checksum_parse_hex(&nb->copy_source_checksum,
svn_checksum_md5, val, pool));
}
/* What's cool about this dump format is that the parser just
ignores any unrecognized headers. :-) */
*node_baton_p = nb;
return SVN_NO_ERROR;
}
static struct revision_baton *
make_revision_baton(apr_hash_t *headers,
struct parse_baton *pb,
apr_pool_t *pool)
{
struct revision_baton *rb = apr_pcalloc(pool, sizeof(*rb));
const char *val;
rb->pb = pb;
rb->pool = pool;
rb->rev = SVN_INVALID_REVNUM;
if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_REVISION_NUMBER)))
{
rb->rev = SVN_STR_TO_REV(val);
/* If we're filtering revisions, is this one we'll skip? */
rb->skipped = (SVN_IS_VALID_REVNUM(pb->start_rev)
&& ((rb->rev < pb->start_rev) ||
(rb->rev > pb->end_rev)));
}
return rb;
}
static svn_error_t *
new_revision_record(void **revision_baton,
apr_hash_t *headers,
void *parse_baton,
apr_pool_t *pool)
{
struct parse_baton *pb = parse_baton;
struct revision_baton *rb;
svn_revnum_t head_rev;
rb = make_revision_baton(headers, pb, pool);
/* ### If we're filtering revisions, and this is one we've skipped,
### and we've skipped it because it has a revision number younger
### than the youngest in our acceptable range, then should we
### just bail out here? */
/*
if (rb->skipped && (rb->rev > pb->end_rev))
return svn_error_createf(SVN_ERR_CEASE_INVOCATION, 0,
_("Finished processing acceptable load "
"revision range"));
*/
SVN_ERR(svn_fs_youngest_rev(&head_rev, pb->fs, pool));
/* FIXME: This is a lame fallback loading multiple segments of dump in
several separate operations. It is highly susceptible to race conditions.
Calculate the revision 'offset' for finding copyfrom sources.
It might be positive or negative. */
rb->rev_offset = (apr_int32_t) ((rb->rev) - (head_rev + 1));
if ((rb->rev > 0) && (! rb->skipped))
{
/* Create a new fs txn. */
SVN_ERR(svn_fs_begin_txn2(&(rb->txn), pb->fs, head_rev, 0, pool));
SVN_ERR(svn_fs_txn_root(&(rb->txn_root), rb->txn, pool));
if (pb->notify_func)
{
- pb->notify->action = svn_repos_notify_load_txn_start;
- pb->notify->old_revision = rb->rev;
- pb->notify_func(pb->notify_baton, pb->notify, rb->pool);
+ /* ### TODO: Use proper scratch pool instead of pb->notify_pool */
+ svn_repos_notify_t *notify = svn_repos_notify_create(
+ svn_repos_notify_load_txn_start,
+ pb->notify_pool);
+
+ notify->old_revision = rb->rev;
+ pb->notify_func(pb->notify_baton, notify, pb->notify_pool);
+ svn_pool_clear(pb->notify_pool);
}
/* Stash the oldest "old" revision committed from the load stream. */
if (!SVN_IS_VALID_REVNUM(pb->oldest_old_rev))
pb->oldest_old_rev = rb->rev;
}
/* If we're skipping this revision, try to notify someone. */
if (rb->skipped && pb->notify_func)
{
- pb->notify->action = svn_repos_notify_load_skipped_rev;
- pb->notify->old_revision = rb->rev;
- pb->notify_func(pb->notify_baton, pb->notify, rb->pool);
+ /* ### TODO: Use proper scratch pool instead of pb->notify_pool */
+ svn_repos_notify_t *notify = svn_repos_notify_create(
+ svn_repos_notify_load_skipped_rev,
+ pb->notify_pool);
+
+ notify->old_revision = rb->rev;
+ pb->notify_func(pb->notify_baton, notify, pb->notify_pool);
+ svn_pool_clear(pb->notify_pool);
}
/* If we're parsing revision 0, only the revision are (possibly)
interesting to us: when loading the stream into an empty
filesystem, then we want new filesystem's revision 0 to have the
same props. Otherwise, we just ignore revision 0 in the stream. */
*revision_baton = rb;
return SVN_NO_ERROR;
}
/* Factorized helper func for new_node_record() */
static svn_error_t *
maybe_add_with_history(struct node_baton *nb,
struct revision_baton *rb,
apr_pool_t *pool)
{
struct parse_baton *pb = rb->pb;
if ((nb->copyfrom_path == NULL) || (! pb->use_history))
{
/* Add empty file or dir, without history. */
if (nb->kind == svn_node_file)
SVN_ERR(svn_fs_make_file(rb->txn_root, nb->path, pool));
else if (nb->kind == svn_node_dir)
SVN_ERR(svn_fs_make_dir(rb->txn_root, nb->path, pool));
}
else
{
/* Hunt down the source revision in this fs. */
svn_fs_root_t *copy_root;
svn_revnum_t copyfrom_rev;
/* Try to find the copyfrom revision in the revision map;
failing that, fall back to the revision offset approach. */
copyfrom_rev = get_revision_mapping(rb->pb->rev_map, nb->copyfrom_rev);
if (! SVN_IS_VALID_REVNUM(copyfrom_rev))
copyfrom_rev = nb->copyfrom_rev - rb->rev_offset;
if (! SVN_IS_VALID_REVNUM(copyfrom_rev))
return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
_("Relative source revision %ld is not"
" available in current repository"),
copyfrom_rev);
SVN_ERR(svn_fs_revision_root(&copy_root, pb->fs, copyfrom_rev, pool));
if (nb->copy_source_checksum)
{
svn_checksum_t *checksum;
SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, copy_root,
nb->copyfrom_path, TRUE, pool));
if (!svn_checksum_match(nb->copy_source_checksum, checksum))
return svn_checksum_mismatch_err(nb->copy_source_checksum,
checksum, pool,
_("Copy source checksum mismatch on copy from '%s'@%ld\n"
"to '%s' in rev based on r%ld"),
nb->copyfrom_path, copyfrom_rev, nb->path, rb->rev);
}
SVN_ERR(svn_fs_copy(copy_root, nb->copyfrom_path,
rb->txn_root, nb->path, pool));
if (pb->notify_func)
{
- pb->notify->action = svn_repos_notify_load_copied_node;
- pb->notify_func(pb->notify_baton, pb->notify, rb->pool);
+ /* ### TODO: Use proper scratch pool instead of pb->notify_pool */
+ svn_repos_notify_t *notify = svn_repos_notify_create(
+ svn_repos_notify_load_copied_node,
+ pb->notify_pool);
+
+ pb->notify_func(pb->notify_baton, notify, pb->notify_pool);
+ svn_pool_clear(pb->notify_pool);
}
}
return SVN_NO_ERROR;
}
static svn_error_t *
magic_header_record(int version,
void *parse_baton,
apr_pool_t *pool)
{
return SVN_NO_ERROR;
}
static svn_error_t *
uuid_record(const char *uuid,
void *parse_baton,
apr_pool_t *pool)
{
struct parse_baton *pb = parse_baton;
svn_revnum_t youngest_rev;
if (pb->uuid_action == svn_repos_load_uuid_ignore)
return SVN_NO_ERROR;
if (pb->uuid_action != svn_repos_load_uuid_force)
{
SVN_ERR(svn_fs_youngest_rev(&youngest_rev, pb->fs, pool));
if (youngest_rev != 0)
return SVN_NO_ERROR;
}
return svn_fs_set_uuid(pb->fs, uuid, pool);
}
static svn_error_t *
new_node_record(void **node_baton,
apr_hash_t *headers,
void *revision_baton,
apr_pool_t *pool)
{
struct revision_baton *rb = revision_baton;
struct parse_baton *pb = rb->pb;
struct node_baton *nb;
if (rb->rev == 0)
return svn_error_create(SVN_ERR_STREAM_MALFORMED_DATA, NULL,
_("Malformed dumpstream: "
"Revision 0 must not contain node records"));
SVN_ERR(make_node_baton(&nb, headers, rb, pool));
/* If we're skipping this revision, we're done here. */
if (rb->skipped)
{
*node_baton = nb;
return SVN_NO_ERROR;
}
/* Make sure we have an action we recognize. */
if (nb->action < svn_node_action_change
|| nb->action > svn_node_action_replace)
return svn_error_createf(SVN_ERR_STREAM_UNRECOGNIZED_DATA, NULL,
_("Unrecognized node-action on node '%s'"),
nb->path);
if (pb->notify_func)
{
- pb->notify->action = svn_repos_notify_load_node_start;
- pb->notify->node_action = nb->action;
- pb->notify->path = nb->path;
- pb->notify_func(pb->notify_baton, pb->notify, rb->pool);
+ /* ### TODO: Use proper scratch pool instead of pb->notify_pool */
+ svn_repos_notify_t *notify = svn_repos_notify_create(
+ svn_repos_notify_load_node_start,
+ pb->notify_pool);
+
+ notify->path = nb->path;
+ pb->notify_func(pb->notify_baton, notify, pb->notify_pool);
+ svn_pool_clear(pb->notify_pool);
}
switch (nb->action)
{
case svn_node_action_change:
break;
case svn_node_action_delete:
SVN_ERR(svn_fs_delete(rb->txn_root, nb->path, pool));
break;
case svn_node_action_add:
SVN_ERR(maybe_add_with_history(nb, rb, pool));
break;
case svn_node_action_replace:
SVN_ERR(svn_fs_delete(rb->txn_root, nb->path, pool));
SVN_ERR(maybe_add_with_history(nb, rb, pool));
break;
}
*node_baton = nb;
return SVN_NO_ERROR;
}
static svn_error_t *
set_revision_property(void *baton,
const char *name,
const svn_string_t *value)
{
struct revision_baton *rb = baton;
/* If we're skipping this revision, we're done here. */
if (rb->skipped)
return SVN_NO_ERROR;
if (rb->rev > 0)
{
if (rb->pb->validate_props)
SVN_ERR(svn_repos_fs_change_txn_prop(rb->txn, name, value, rb->pool));
else
SVN_ERR(svn_fs_change_txn_prop(rb->txn, name, value, rb->pool));
/* Remember any datestamp that passes through! (See comment in
close_revision() below.) */
if (! strcmp(name, SVN_PROP_REVISION_DATE))
rb->datestamp = svn_string_dup(value, rb->pool);
}
else if (rb->rev == 0)
{
/* Special case: set revision 0 properties when loading into an
'empty' filesystem. */
struct parse_baton *pb = rb->pb;
svn_revnum_t youngest_rev;
SVN_ERR(svn_fs_youngest_rev(&youngest_rev, pb->fs, rb->pool));
if (youngest_rev == 0)
SVN_ERR(change_rev_prop(pb->repos, 0, name, value,
pb->validate_props, rb->pool));
}
return SVN_NO_ERROR;
}
+/* Adjust mergeinfo:
+ * - normalize line endings (if all CRLF, change to LF; but error if mixed);
+ * - adjust revision numbers (see renumber_mergeinfo_revs());
+ * - adjust paths (see prefix_mergeinfo_paths()).
+ */
static svn_error_t *
+adjust_mergeinfo_property(struct revision_baton *rb,
+ svn_string_t **new_value_p,
+ const svn_string_t *old_value,
+ apr_pool_t *result_pool)
+{
+ struct parse_baton *pb = rb->pb;
+ svn_string_t prop_val = *old_value;
+
+ /* Tolerate mergeinfo with "\r\n" line endings because some
+ dumpstream sources might contain as much. If so normalize
+ the line endings to '\n' and make a notification to
+ PARSE_BATON->FEEDBACK_STREAM that we have made this
+ correction. */
+ if (strstr(prop_val.data, "\r"))
+ {
+ const char *prop_eol_normalized;
+
+ SVN_ERR(svn_subst_translate_cstring2(prop_val.data,
+ &prop_eol_normalized,
+ "\n", /* translate to LF */
+ FALSE, /* no repair */
+ NULL, /* no keywords */
+ FALSE, /* no expansion */
+ result_pool));
+ prop_val.data = prop_eol_normalized;
+ prop_val.len = strlen(prop_eol_normalized);
+
+ if (pb->notify_func)
+ {
+ /* ### TODO: Use proper scratch pool instead of pb->notify_pool */
+ svn_repos_notify_t *notify
+ = svn_repos_notify_create(
+ svn_repos_notify_load_normalized_mergeinfo,
+ pb->notify_pool);
+
+ pb->notify_func(pb->notify_baton, notify, pb->notify_pool);
+ svn_pool_clear(pb->notify_pool);
+ }
+ }
+
+ /* Renumber mergeinfo as appropriate. */
+ SVN_ERR(renumber_mergeinfo_revs(new_value_p, &prop_val, rb,
+ result_pool));
+ if (pb->parent_dir)
+ {
+ /* Prefix the merge source paths with PB->parent_dir. */
+ /* ASSUMPTION: All source paths are included in the dump stream. */
+ SVN_ERR(prefix_mergeinfo_paths(new_value_p, *new_value_p,
+ pb->parent_dir, result_pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+
+static svn_error_t *
set_node_property(void *baton,
const char *name,
const svn_string_t *value)
{
struct node_baton *nb = baton;
struct revision_baton *rb = nb->rb;
struct parse_baton *pb = rb->pb;
/* If we're skipping this revision, we're done here. */
if (rb->skipped)
return SVN_NO_ERROR;
+ /* Adjust mergeinfo. If this fails, presumably because the mergeinfo
+ property has an ill-formed value, then we must not fail to load
+ the repository (at least if it's a simple load with no revision
+ offset adjustments, path changes, etc.) so just warn and leave it
+ as it is. */
if (strcmp(name, SVN_PROP_MERGEINFO) == 0)
{
- svn_string_t *renumbered_mergeinfo;
- /* ### Need to cast away const. We cannot change the declaration of
- * ### this function since it is part of svn_repos_parse_fns2_t. */
- svn_string_t *prop_val = (svn_string_t *)value;
+ svn_string_t *new_value;
+ svn_error_t *err;
- /* Tolerate mergeinfo with "\r\n" line endings because some
- dumpstream sources might contain as much. If so normalize
- the line endings to '\n' and make a notification to
- PARSE_BATON->FEEDBACK_STREAM that we have made this
- correction. */
- if (strstr(prop_val->data, "\r"))
+ err = adjust_mergeinfo_property(rb, &new_value, value, nb->pool);
+ if (err)
{
- const char *prop_eol_normalized;
-
- SVN_ERR(svn_subst_translate_cstring2(prop_val->data,
- &prop_eol_normalized,
- "\n", /* translate to LF */
- FALSE, /* no repair */
- NULL, /* no keywords */
- FALSE, /* no expansion */
- nb->pool));
- prop_val->data = prop_eol_normalized;
- prop_val->len = strlen(prop_eol_normalized);
-
+ if (pb->validate_props)
+ {
+ return svn_error_quick_wrap(
+ err,
+ _("Invalid svn:mergeinfo value"));
+ }
if (pb->notify_func)
{
- pb->notify->action = svn_repos_notify_load_normalized_mergeinfo;
- pb->notify_func(pb->notify_baton, pb->notify, nb->pool);
+ svn_repos_notify_t *notify
+ = svn_repos_notify_create(svn_repos_notify_warning,
+ pb->notify_pool);
+
+ notify->warning = svn_repos__notify_warning_invalid_mergeinfo;
+ notify->warning_str = _("Invalid svn:mergeinfo value; "
+ "leaving unchanged");
+ pb->notify_func(pb->notify_baton, notify, pb->notify_pool);
+ svn_pool_clear(pb->notify_pool);
}
+ svn_error_clear(err);
}
-
- /* Renumber mergeinfo as appropriate. */
- SVN_ERR(renumber_mergeinfo_revs(&renumbered_mergeinfo, prop_val, rb,
- nb->pool));
- value = renumbered_mergeinfo;
- if (pb->parent_dir)
+ else
{
- /* Prefix the merge source paths with PB->parent_dir. */
- /* ASSUMPTION: All source paths are included in the dump stream. */
- svn_string_t *mergeinfo_val;
- SVN_ERR(prefix_mergeinfo_paths(&mergeinfo_val, value,
- pb->parent_dir, nb->pool));
- value = mergeinfo_val;
+ value = new_value;
}
}
return change_node_prop(rb->txn_root, nb->path, name, value,
pb->validate_props, nb->pool);
}
static svn_error_t *
delete_node_property(void *baton,
const char *name)
{
struct node_baton *nb = baton;
struct revision_baton *rb = nb->rb;
/* If we're skipping this revision, we're done here. */
if (rb->skipped)
return SVN_NO_ERROR;
return change_node_prop(rb->txn_root, nb->path, name, NULL,
rb->pb->validate_props, nb->pool);
}
static svn_error_t *
remove_node_props(void *baton)
{
struct node_baton *nb = baton;
struct revision_baton *rb = nb->rb;
apr_hash_t *proplist;
apr_hash_index_t *hi;
/* If we're skipping this revision, we're done here. */
if (rb->skipped)
return SVN_NO_ERROR;
SVN_ERR(svn_fs_node_proplist(&proplist,
rb->txn_root, nb->path, nb->pool));
for (hi = apr_hash_first(nb->pool, proplist); hi; hi = apr_hash_next(hi))
{
const void *key;
apr_hash_this(hi, &key, NULL, NULL);
SVN_ERR(change_node_prop(rb->txn_root, nb->path, key, NULL,
rb->pb->validate_props, nb->pool));
}
return SVN_NO_ERROR;
}
static svn_error_t *
apply_textdelta(svn_txdelta_window_handler_t *handler,
void **handler_baton,
void *node_baton)
{
struct node_baton *nb = node_baton;
struct revision_baton *rb = nb->rb;
/* If we're skipping this revision, we're done here. */
if (rb->skipped)
{
*handler = NULL;
return SVN_NO_ERROR;
}
return svn_fs_apply_textdelta(handler, handler_baton,
rb->txn_root, nb->path,
svn_checksum_to_cstring(nb->base_checksum,
nb->pool),
svn_checksum_to_cstring(nb->result_checksum,
nb->pool),
nb->pool);
}
static svn_error_t *
set_fulltext(svn_stream_t **stream,
void *node_baton)
{
struct node_baton *nb = node_baton;
struct revision_baton *rb = nb->rb;
/* If we're skipping this revision, we're done here. */
if (rb->skipped)
{
*stream = NULL;
return SVN_NO_ERROR;
}
return svn_fs_apply_text(stream,
rb->txn_root, nb->path,
svn_checksum_to_cstring(nb->result_checksum,
nb->pool),
nb->pool);
}
static svn_error_t *
close_node(void *baton)
{
struct node_baton *nb = baton;
struct revision_baton *rb = nb->rb;
struct parse_baton *pb = rb->pb;
/* If we're skipping this revision, we're done here. */
if (rb->skipped)
return SVN_NO_ERROR;
if (pb->notify_func)
{
- pb->notify->action = svn_repos_notify_load_node_done;
- pb->notify_func(pb->notify_baton, pb->notify, rb->pool);
+ /* ### TODO: Use proper scratch pool instead of pb->notify_pool */
+ svn_repos_notify_t *notify = svn_repos_notify_create(
+ svn_repos_notify_load_node_done,
+ pb->notify_pool);
+
+ pb->notify_func(pb->notify_baton, notify, pb->notify_pool);
+ svn_pool_clear(pb->notify_pool);
}
return SVN_NO_ERROR;
}
static svn_error_t *
close_revision(void *baton)
{
struct revision_baton *rb = baton;
struct parse_baton *pb = rb->pb;
const char *conflict_msg = NULL;
svn_revnum_t committed_rev;
svn_error_t *err;
const char *txn_name = NULL;
apr_hash_t *hooks_env;
/* If we're skipping this revision or it has an invalid revision
number, we're done here. */
if (rb->skipped || (rb->rev <= 0))
return SVN_NO_ERROR;
/* Get the txn name and hooks environment if they will be needed. */
if (pb->use_pre_commit_hook || pb->use_post_commit_hook)
{
SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, pb->repos->hooks_env_path,
rb->pool, rb->pool));
err = svn_fs_txn_name(&txn_name, rb->txn, rb->pool);
if (err)
{
svn_error_clear(svn_fs_abort_txn(rb->txn, rb->pool));
return svn_error_trace(err);
}
}
/* Run the pre-commit hook, if so commanded. */
if (pb->use_pre_commit_hook)
{
err = svn_repos__hooks_pre_commit(pb->repos, hooks_env,
txn_name, rb->pool);
if (err)
{
svn_error_clear(svn_fs_abort_txn(rb->txn, rb->pool));
return svn_error_trace(err);
}
}
/* Commit. */
err = svn_fs_commit_txn(&conflict_msg, &committed_rev, rb->txn, rb->pool);
if (SVN_IS_VALID_REVNUM(committed_rev))
{
if (err)
{
/* ### Log any error, but better yet is to rev
### close_revision()'s API to allow both committed_rev and err
### to be returned, see #3768. */
svn_error_clear(err);
}
}
else
{
svn_error_clear(svn_fs_abort_txn(rb->txn, rb->pool));
if (conflict_msg)
return svn_error_quick_wrap(err, conflict_msg);
else
return svn_error_trace(err);
}
/* Run post-commit hook, if so commanded. */
if (pb->use_post_commit_hook)
{
if ((err = svn_repos__hooks_post_commit(pb->repos, hooks_env,
committed_rev, txn_name,
rb->pool)))
return svn_error_create
(SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED, err,
_("Commit succeeded, but post-commit hook failed"));
}
/* After a successful commit, must record the dump-rev -> in-repos-rev
mapping, so that copyfrom instructions in the dump file can look up the
correct repository revision to copy from. */
set_revision_mapping(pb->rev_map, rb->rev, committed_rev);
/* If the incoming dump stream has non-contiguous revisions (e.g. from
using svndumpfilter --drop-empty-revs without --renumber-revs) then
we must account for the missing gaps in PB->REV_MAP. Otherwise we
might not be able to map all mergeinfo source revisions to the correct
revisions in the target repos. */
if ((pb->last_rev_mapped != SVN_INVALID_REVNUM)
&& (rb->rev != pb->last_rev_mapped + 1))
{
svn_revnum_t i;
for (i = pb->last_rev_mapped + 1; i < rb->rev; i++)
{
set_revision_mapping(pb->rev_map, i, pb->last_rev_mapped);
}
}
/* Update our "last revision mapped". */
pb->last_rev_mapped = rb->rev;
/* Deltify the predecessors of paths changed in this revision. */
SVN_ERR(svn_fs_deltify_revision(pb->fs, committed_rev, rb->pool));
/* Grrr, svn_fs_commit_txn rewrites the datestamp property to the
current clock-time. We don't want that, we want to preserve
history exactly. Good thing revision props aren't versioned!
Note that if rb->datestamp is NULL, that's fine -- if the dump
data doesn't carry a datestamp, we want to preserve that fact in
the load. */
SVN_ERR(change_rev_prop(pb->repos, committed_rev, SVN_PROP_REVISION_DATE,
rb->datestamp, pb->validate_props, rb->pool));
if (pb->notify_func)
{
- pb->notify->action = svn_repos_notify_load_txn_committed;
- pb->notify->new_revision = committed_rev;
- pb->notify->old_revision = ((committed_rev == rb->rev)
+ /* ### TODO: Use proper scratch pool instead of pb->notify_pool */
+ svn_repos_notify_t *notify = svn_repos_notify_create(
+ svn_repos_notify_load_txn_committed,
+ pb->notify_pool);
+
+ notify->new_revision = committed_rev;
+ notify->old_revision = ((committed_rev == rb->rev)
? SVN_INVALID_REVNUM
: rb->rev);
- pb->notify_func(pb->notify_baton, pb->notify, rb->pool);
+ pb->notify_func(pb->notify_baton, notify, pb->notify_pool);
+ svn_pool_clear(pb->notify_pool);
}
return SVN_NO_ERROR;
}
/*----------------------------------------------------------------------*/
/** The public routines **/
svn_error_t *
svn_repos_get_fs_build_parser4(const svn_repos_parse_fns3_t **callbacks,
void **parse_baton,
svn_repos_t *repos,
svn_revnum_t start_rev,
svn_revnum_t end_rev,
svn_boolean_t use_history,
svn_boolean_t validate_props,
enum svn_repos_load_uuid uuid_action,
const char *parent_dir,
svn_repos_notify_func_t notify_func,
void *notify_baton,
apr_pool_t *pool)
{
svn_repos_parse_fns3_t *parser = apr_pcalloc(pool, sizeof(*parser));
struct parse_baton *pb = apr_pcalloc(pool, sizeof(*pb));
if (parent_dir)
parent_dir = svn_relpath_canonicalize(parent_dir, pool);
SVN_ERR_ASSERT((SVN_IS_VALID_REVNUM(start_rev) &&
SVN_IS_VALID_REVNUM(end_rev))
|| ((! SVN_IS_VALID_REVNUM(start_rev)) &&
(! SVN_IS_VALID_REVNUM(end_rev))));
if (SVN_IS_VALID_REVNUM(start_rev))
SVN_ERR_ASSERT(start_rev <= end_rev);
parser->magic_header_record = magic_header_record;
parser->uuid_record = uuid_record;
parser->new_revision_record = new_revision_record;
parser->new_node_record = new_node_record;
parser->set_revision_property = set_revision_property;
parser->set_node_property = set_node_property;
parser->remove_node_props = remove_node_props;
parser->set_fulltext = set_fulltext;
parser->close_node = close_node;
parser->close_revision = close_revision;
parser->delete_node_property = delete_node_property;
parser->apply_textdelta = apply_textdelta;
pb->repos = repos;
pb->fs = svn_repos_fs(repos);
pb->use_history = use_history;
pb->validate_props = validate_props;
pb->notify_func = notify_func;
pb->notify_baton = notify_baton;
- pb->notify = svn_repos_notify_create(svn_repos_notify_load_txn_start, pool);
pb->uuid_action = uuid_action;
pb->parent_dir = parent_dir;
pb->pool = pool;
+ pb->notify_pool = svn_pool_create(pool);
pb->rev_map = apr_hash_make(pool);
pb->oldest_old_rev = SVN_INVALID_REVNUM;
pb->last_rev_mapped = SVN_INVALID_REVNUM;
pb->start_rev = start_rev;
pb->end_rev = end_rev;
*callbacks = parser;
*parse_baton = pb;
return SVN_NO_ERROR;
}
svn_error_t *
svn_repos_load_fs4(svn_repos_t *repos,
svn_stream_t *dumpstream,
svn_revnum_t start_rev,
svn_revnum_t end_rev,
enum svn_repos_load_uuid uuid_action,
const char *parent_dir,
svn_boolean_t use_pre_commit_hook,
svn_boolean_t use_post_commit_hook,
svn_boolean_t validate_props,
svn_repos_notify_func_t notify_func,
void *notify_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
const svn_repos_parse_fns3_t *parser;
void *parse_baton;
struct parse_baton *pb;
/* This is really simple. */
SVN_ERR(svn_repos_get_fs_build_parser4(&parser, &parse_baton,
repos,
start_rev, end_rev,
TRUE, /* look for copyfrom revs */
validate_props,
uuid_action,
parent_dir,
notify_func,
notify_baton,
pool));
/* Heh. We know this is a parse_baton. This file made it. So
cast away, and set our hook booleans. */
pb = parse_baton;
pb->use_pre_commit_hook = use_pre_commit_hook;
pb->use_post_commit_hook = use_post_commit_hook;
return svn_repos_parse_dumpstream3(dumpstream, parser, parse_baton, FALSE,
cancel_func, cancel_baton, pool);
}
Index: vendor/subversion/dist/subversion/libsvn_repos/rev_hunt.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_repos/rev_hunt.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_repos/rev_hunt.c (revision 286501)
@@ -1,1699 +1,1696 @@
/* rev_hunt.c --- routines to hunt down particular fs revisions and
* their properties.
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include <string.h>
#include "svn_compat.h"
#include "svn_private_config.h"
#include "svn_hash.h"
#include "svn_pools.h"
#include "svn_error.h"
#include "svn_error_codes.h"
#include "svn_fs.h"
#include "svn_repos.h"
#include "svn_string.h"
#include "svn_time.h"
#include "svn_sorts.h"
#include "svn_props.h"
#include "svn_mergeinfo.h"
#include "repos.h"
#include "private/svn_fspath.h"
/* Note: this binary search assumes that the datestamp properties on
each revision are in chronological order. That is if revision A >
revision B, then A's datestamp is younger then B's datestamp.
If someone comes along and sets a bogus datestamp, this routine
might not work right.
### todo: you know, we *could* have svn_fs_change_rev_prop() do
some semantic checking when it's asked to change special reserved
svn: properties. It could prevent such a problem. */
/* helper for svn_repos_dated_revision().
Set *TM to the apr_time_t datestamp on revision REV in FS. */
static svn_error_t *
get_time(apr_time_t *tm,
svn_fs_t *fs,
svn_revnum_t rev,
apr_pool_t *pool)
{
svn_string_t *date_str;
SVN_ERR(svn_fs_revision_prop(&date_str, fs, rev, SVN_PROP_REVISION_DATE,
pool));
if (! date_str)
return svn_error_createf
(SVN_ERR_FS_GENERAL, NULL,
_("Failed to find time on revision %ld"), rev);
return svn_time_from_cstring(tm, date_str->data, pool);
}
svn_error_t *
svn_repos_dated_revision(svn_revnum_t *revision,
svn_repos_t *repos,
apr_time_t tm,
apr_pool_t *pool)
{
svn_revnum_t rev_mid, rev_top, rev_bot, rev_latest;
apr_time_t this_time;
svn_fs_t *fs = repos->fs;
/* Initialize top and bottom values of binary search. */
SVN_ERR(svn_fs_youngest_rev(&rev_latest, fs, pool));
rev_bot = 0;
rev_top = rev_latest;
while (rev_bot <= rev_top)
{
rev_mid = (rev_top + rev_bot) / 2;
SVN_ERR(get_time(&this_time, fs, rev_mid, pool));
if (this_time > tm)/* we've overshot */
{
apr_time_t previous_time;
if ((rev_mid - 1) < 0)
{
*revision = 0;
break;
}
/* see if time falls between rev_mid and rev_mid-1: */
SVN_ERR(get_time(&previous_time, fs, rev_mid - 1, pool));
if (previous_time <= tm)
{
*revision = rev_mid - 1;
break;
}
rev_top = rev_mid - 1;
}
else if (this_time < tm) /* we've undershot */
{
apr_time_t next_time;
if ((rev_mid + 1) > rev_latest)
{
*revision = rev_latest;
break;
}
/* see if time falls between rev_mid and rev_mid+1: */
SVN_ERR(get_time(&next_time, fs, rev_mid + 1, pool));
if (next_time > tm)
{
*revision = rev_mid;
break;
}
rev_bot = rev_mid + 1;
}
else
{
*revision = rev_mid; /* exact match! */
break;
}
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_repos_get_committed_info(svn_revnum_t *committed_rev,
const char **committed_date,
const char **last_author,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
apr_hash_t *revprops;
svn_fs_t *fs = svn_fs_root_fs(root);
/* ### It might be simpler just to declare that revision
properties have char * (i.e., UTF-8) values, not arbitrary
binary values, hmmm. */
svn_string_t *committed_date_s, *last_author_s;
/* Get the CR field out of the node's skel. */
SVN_ERR(svn_fs_node_created_rev(committed_rev, root, path, pool));
/* Get the revision properties of this revision. */
SVN_ERR(svn_fs_revision_proplist(&revprops, fs, *committed_rev, pool));
/* Extract date and author from these revprops. */
committed_date_s = apr_hash_get(revprops,
SVN_PROP_REVISION_DATE,
sizeof(SVN_PROP_REVISION_DATE)-1);
last_author_s = apr_hash_get(revprops,
SVN_PROP_REVISION_AUTHOR,
sizeof(SVN_PROP_REVISION_AUTHOR)-1);
*committed_date = committed_date_s ? committed_date_s->data : NULL;
*last_author = last_author_s ? last_author_s->data : NULL;
return SVN_NO_ERROR;
}
svn_error_t *
svn_repos_history2(svn_fs_t *fs,
const char *path,
svn_repos_history_func_t history_func,
void *history_baton,
svn_repos_authz_func_t authz_read_func,
void *authz_read_baton,
svn_revnum_t start,
svn_revnum_t end,
svn_boolean_t cross_copies,
apr_pool_t *pool)
{
svn_fs_history_t *history;
apr_pool_t *oldpool = svn_pool_create(pool);
apr_pool_t *newpool = svn_pool_create(pool);
const char *history_path;
svn_revnum_t history_rev;
svn_fs_root_t *root;
/* Validate the revisions. */
if (! SVN_IS_VALID_REVNUM(start))
return svn_error_createf
(SVN_ERR_FS_NO_SUCH_REVISION, 0,
_("Invalid start revision %ld"), start);
if (! SVN_IS_VALID_REVNUM(end))
return svn_error_createf
(SVN_ERR_FS_NO_SUCH_REVISION, 0,
_("Invalid end revision %ld"), end);
/* Ensure that the input is ordered. */
if (start > end)
{
svn_revnum_t tmprev = start;
start = end;
end = tmprev;
}
/* Get a revision root for END, and an initial HISTORY baton. */
SVN_ERR(svn_fs_revision_root(&root, fs, end, pool));
if (authz_read_func)
{
svn_boolean_t readable;
SVN_ERR(authz_read_func(&readable, root, path,
authz_read_baton, pool));
if (! readable)
return svn_error_create(SVN_ERR_AUTHZ_UNREADABLE, NULL, NULL);
}
SVN_ERR(svn_fs_node_history(&history, root, path, oldpool));
/* Now, we loop over the history items, calling svn_fs_history_prev(). */
do
{
/* Note that we have to do some crazy pool work here. We can't
get rid of the old history until we use it to get the new, so
we alternate back and forth between our subpools. */
apr_pool_t *tmppool;
svn_error_t *err;
SVN_ERR(svn_fs_history_prev(&history, history, cross_copies, newpool));
/* Only continue if there is further history to deal with. */
if (! history)
break;
/* Fetch the location information for this history step. */
SVN_ERR(svn_fs_history_location(&history_path, &history_rev,
history, newpool));
/* If this history item predates our START revision, quit
here. */
if (history_rev < start)
break;
/* Is the history item readable? If not, quit. */
if (authz_read_func)
{
svn_boolean_t readable;
svn_fs_root_t *history_root;
SVN_ERR(svn_fs_revision_root(&history_root, fs,
history_rev, newpool));
SVN_ERR(authz_read_func(&readable, history_root, history_path,
authz_read_baton, newpool));
if (! readable)
break;
}
/* Call the user-provided callback function. */
err = history_func(history_baton, history_path, history_rev, newpool);
if (err)
{
if (err->apr_err == SVN_ERR_CEASE_INVOCATION)
{
svn_error_clear(err);
goto cleanup;
}
else
{
return svn_error_trace(err);
}
}
/* We're done with the old history item, so we can clear its
pool, and then toggle our notion of "the old pool". */
svn_pool_clear(oldpool);
tmppool = oldpool;
oldpool = newpool;
newpool = tmppool;
}
while (history); /* shouldn't hit this */
cleanup:
svn_pool_destroy(oldpool);
svn_pool_destroy(newpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_repos_deleted_rev(svn_fs_t *fs,
const char *path,
svn_revnum_t start,
svn_revnum_t end,
svn_revnum_t *deleted,
apr_pool_t *pool)
{
apr_pool_t *subpool;
svn_fs_root_t *root, *copy_root;
const char *copy_path;
svn_revnum_t mid_rev;
const svn_fs_id_t *start_node_id, *curr_node_id;
svn_error_t *err;
/* Validate the revision range. */
if (! SVN_IS_VALID_REVNUM(start))
return svn_error_createf
(SVN_ERR_FS_NO_SUCH_REVISION, 0,
_("Invalid start revision %ld"), start);
if (! SVN_IS_VALID_REVNUM(end))
return svn_error_createf
(SVN_ERR_FS_NO_SUCH_REVISION, 0,
_("Invalid end revision %ld"), end);
/* Ensure that the input is ordered. */
if (start > end)
{
svn_revnum_t tmprev = start;
start = end;
end = tmprev;
}
/* Ensure path exists in fs at start revision. */
SVN_ERR(svn_fs_revision_root(&root, fs, start, pool));
err = svn_fs_node_id(&start_node_id, root, path, pool);
if (err)
{
if (err->apr_err == SVN_ERR_FS_NOT_FOUND)
{
/* Path must exist in fs at start rev. */
*deleted = SVN_INVALID_REVNUM;
svn_error_clear(err);
return SVN_NO_ERROR;
}
return svn_error_trace(err);
}
/* Ensure path was deleted at or before end revision. */
SVN_ERR(svn_fs_revision_root(&root, fs, end, pool));
err = svn_fs_node_id(&curr_node_id, root, path, pool);
if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
{
svn_error_clear(err);
}
else if (err)
{
return svn_error_trace(err);
}
else
{
/* path exists in the end node and the end node is equivalent
or otherwise equivalent to the start node. This can mean
a few things:
1) The end node *is* simply the start node, uncopied
and unmodified in the start to end range.
2) The start node was modified, but never copied.
3) The start node was copied, but this copy occurred at
start or some rev *previous* to start, this is
effectively the same situation as 1 if the node was
never modified or 2 if it was.
In the first three cases the path was not deleted in
the specified range and we are done, but in the following
cases the start node must have been deleted at least once:
4) The start node was deleted and replaced by a copy of
itself at some rev between start and end. This copy
may itself have been replaced with copies of itself.
5) The start node was deleted and replaced by a node which
it does not share any history with.
*/
SVN_ERR(svn_fs_node_id(&curr_node_id, root, path, pool));
if (svn_fs_compare_ids(start_node_id, curr_node_id) != -1)
{
SVN_ERR(svn_fs_closest_copy(&copy_root, &copy_path, root,
path, pool));
if (!copy_root ||
(svn_fs_revision_root_revision(copy_root) <= start))
{
/* Case 1,2 or 3, nothing more to do. */
*deleted = SVN_INVALID_REVNUM;
return SVN_NO_ERROR;
}
}
}
/* If we get here we know that path exists in rev start and was deleted
at least once before rev end. To find the revision path was first
deleted we use a binary search. The rules for the determining if
the deletion comes before or after a given median revision are
described by this matrix:
| Most recent copy event that
| caused mid node to exist.
|-----------------------------------------------------
Compare path | | | |
at start and | Copied at | Copied at | Never copied |
mid nodes. | rev > start | rev <= start | |
| | | |
-------------------------------------------------------------------|
Mid node is | A) Start node | |
equivalent to | replaced with | E) Mid node == start node, |
start node | an unmodified | look HIGHER. |
| copy of | |
| itself, | |
| look LOWER. | |
-------------------------------------------------------------------|
Mid node is | B) Start node | |
otherwise | replaced with | F) Mid node is a modified |
related to | a modified | version of start node, |
start node | copy of | look HIGHER. |
| itself, | |
| look LOWER. | |
-------------------------------------------------------------------|
Mid node is | |
unrelated to | C) Start node replaced with unrelated mid node, |
start node | look LOWER. |
| |
-------------------------------------------------------------------|
Path doesn't | |
exist at mid | D) Start node deleted before mid node, |
node | look LOWER |
| |
--------------------------------------------------------------------
*/
mid_rev = (start + end) / 2;
subpool = svn_pool_create(pool);
while (1)
{
svn_pool_clear(subpool);
/* Get revision root and node id for mid_rev at that revision. */
SVN_ERR(svn_fs_revision_root(&root, fs, mid_rev, subpool));
err = svn_fs_node_id(&curr_node_id, root, path, subpool);
if (err)
{
if (err->apr_err == SVN_ERR_FS_NOT_FOUND)
{
/* Case D: Look lower in the range. */
svn_error_clear(err);
end = mid_rev;
mid_rev = (start + mid_rev) / 2;
}
else
return svn_error_trace(err);
}
else
{
/* Determine the relationship between the start node
and the current node. */
int cmp = svn_fs_compare_ids(start_node_id, curr_node_id);
SVN_ERR(svn_fs_closest_copy(&copy_root, &copy_path, root,
path, subpool));
if (cmp == -1 ||
(copy_root &&
(svn_fs_revision_root_revision(copy_root) > start)))
{
/* Cases A, B, C: Look at lower revs. */
end = mid_rev;
mid_rev = (start + mid_rev) / 2;
}
else if (end - mid_rev == 1)
{
/* Found the node path was deleted. */
*deleted = end;
break;
}
else
{
/* Cases E, F: Look at higher revs. */
start = mid_rev;
mid_rev = (start + end) / 2;
}
}
}
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
/* Helper func: return SVN_ERR_AUTHZ_UNREADABLE if ROOT/PATH is
unreadable. */
static svn_error_t *
check_readability(svn_fs_root_t *root,
const char *path,
svn_repos_authz_func_t authz_read_func,
void *authz_read_baton,
apr_pool_t *pool)
{
svn_boolean_t readable;
SVN_ERR(authz_read_func(&readable, root, path, authz_read_baton, pool));
if (! readable)
return svn_error_create(SVN_ERR_AUTHZ_UNREADABLE, NULL,
_("Unreadable path encountered; access denied"));
return SVN_NO_ERROR;
}
/* The purpose of this function is to discover if fs_path@future_rev
* is derived from fs_path@peg_rev. The return is placed in *is_ancestor. */
static svn_error_t *
check_ancestry_of_peg_path(svn_boolean_t *is_ancestor,
svn_fs_t *fs,
const char *fs_path,
svn_revnum_t peg_revision,
svn_revnum_t future_revision,
apr_pool_t *pool)
{
svn_fs_root_t *root;
svn_fs_history_t *history;
const char *path = NULL;
svn_revnum_t revision;
apr_pool_t *lastpool, *currpool;
lastpool = svn_pool_create(pool);
currpool = svn_pool_create(pool);
SVN_ERR(svn_fs_revision_root(&root, fs, future_revision, pool));
SVN_ERR(svn_fs_node_history(&history, root, fs_path, lastpool));
/* Since paths that are different according to strcmp may still be
equivalent (due to number of consecutive slashes and the fact that
"" is the same as "/"), we get the "canonical" path in the first
iteration below so that the comparison after the loop will work
correctly. */
fs_path = NULL;
while (1)
{
apr_pool_t *tmppool;
SVN_ERR(svn_fs_history_prev(&history, history, TRUE, currpool));
if (!history)
break;
SVN_ERR(svn_fs_history_location(&path, &revision, history, currpool));
if (!fs_path)
fs_path = apr_pstrdup(pool, path);
if (revision <= peg_revision)
break;
/* Clear old pool and flip. */
svn_pool_clear(lastpool);
tmppool = lastpool;
lastpool = currpool;
currpool = tmppool;
}
/* We must have had at least one iteration above where we
reassigned fs_path. Else, the path wouldn't have existed at
future_revision and svn_fs_history would have thrown. */
SVN_ERR_ASSERT(fs_path != NULL);
*is_ancestor = (history && strcmp(path, fs_path) == 0);
return SVN_NO_ERROR;
}
svn_error_t *
svn_repos__prev_location(svn_revnum_t *appeared_rev,
const char **prev_path,
svn_revnum_t *prev_rev,
svn_fs_t *fs,
svn_revnum_t revision,
const char *path,
apr_pool_t *pool)
{
svn_fs_root_t *root, *copy_root;
const char *copy_path, *copy_src_path, *remainder;
svn_revnum_t copy_src_rev;
/* Initialize return variables. */
if (appeared_rev)
*appeared_rev = SVN_INVALID_REVNUM;
if (prev_rev)
*prev_rev = SVN_INVALID_REVNUM;
if (prev_path)
*prev_path = NULL;
/* Ask about the most recent copy which affected PATH@REVISION. If
there was no such copy, we're done. */
SVN_ERR(svn_fs_revision_root(&root, fs, revision, pool));
SVN_ERR(svn_fs_closest_copy(&copy_root, &copy_path, root, path, pool));
if (! copy_root)
return SVN_NO_ERROR;
/* Ultimately, it's not the path of the closest copy's source that
we care about -- it's our own path's location in the copy source
revision. So we'll tack the relative path that expresses the
difference between the copy destination and our path in the copy
revision onto the copy source path to determine this information.
In other words, if our path is "/branches/my-branch/foo/bar", and
we know that the closest relevant copy was a copy of "/trunk" to
"/branches/my-branch", then that relative path under the copy
destination is "/foo/bar". Tacking that onto the copy source
path tells us that our path was located at "/trunk/foo/bar"
before the copy.
*/
SVN_ERR(svn_fs_copied_from(&copy_src_rev, &copy_src_path,
copy_root, copy_path, pool));
remainder = svn_fspath__skip_ancestor(copy_path, path);
if (prev_path)
*prev_path = svn_fspath__join(copy_src_path, remainder, pool);
if (appeared_rev)
*appeared_rev = svn_fs_revision_root_revision(copy_root);
if (prev_rev)
*prev_rev = copy_src_rev;
return SVN_NO_ERROR;
}
svn_error_t *
svn_repos_trace_node_locations(svn_fs_t *fs,
apr_hash_t **locations,
const char *fs_path,
svn_revnum_t peg_revision,
const apr_array_header_t *location_revisions_orig,
svn_repos_authz_func_t authz_read_func,
void *authz_read_baton,
apr_pool_t *pool)
{
apr_array_header_t *location_revisions;
svn_revnum_t *revision_ptr, *revision_ptr_end;
svn_fs_root_t *root;
const char *path;
svn_revnum_t revision;
svn_boolean_t is_ancestor;
apr_pool_t *lastpool, *currpool;
const svn_fs_id_t *id;
SVN_ERR_ASSERT(location_revisions_orig->elt_size == sizeof(svn_revnum_t));
/* Ensure that FS_PATH is absolute, because our path-math below will
depend on that being the case. */
if (*fs_path != '/')
fs_path = apr_pstrcat(pool, "/", fs_path, (char *)NULL);
/* Another sanity check. */
if (authz_read_func)
{
svn_fs_root_t *peg_root;
SVN_ERR(svn_fs_revision_root(&peg_root, fs, peg_revision, pool));
SVN_ERR(check_readability(peg_root, fs_path,
authz_read_func, authz_read_baton, pool));
}
*locations = apr_hash_make(pool);
/* We flip between two pools in the second loop below. */
lastpool = svn_pool_create(pool);
currpool = svn_pool_create(pool);
/* First - let's sort the array of the revisions from the greatest revision
* downward, so it will be easier to search on. */
location_revisions = apr_array_copy(pool, location_revisions_orig);
qsort(location_revisions->elts, location_revisions->nelts,
sizeof(*revision_ptr), svn_sort_compare_revisions);
revision_ptr = (svn_revnum_t *)location_revisions->elts;
revision_ptr_end = revision_ptr + location_revisions->nelts;
/* Ignore revisions R that are younger than the peg_revisions where
path@peg_revision is not an ancestor of path@R. */
is_ancestor = FALSE;
while (revision_ptr < revision_ptr_end && *revision_ptr > peg_revision)
{
svn_pool_clear(currpool);
SVN_ERR(check_ancestry_of_peg_path(&is_ancestor, fs, fs_path,
peg_revision, *revision_ptr,
currpool));
if (is_ancestor)
break;
++revision_ptr;
}
revision = is_ancestor ? *revision_ptr : peg_revision;
path = fs_path;
if (authz_read_func)
{
SVN_ERR(svn_fs_revision_root(&root, fs, revision, pool));
SVN_ERR(check_readability(root, fs_path, authz_read_func,
authz_read_baton, pool));
}
while (revision_ptr < revision_ptr_end)
{
apr_pool_t *tmppool;
svn_revnum_t appeared_rev, prev_rev;
const char *prev_path;
/* Find the target of the innermost copy relevant to path@revision.
The copy may be of path itself, or of a parent directory. */
SVN_ERR(svn_repos__prev_location(&appeared_rev, &prev_path, &prev_rev,
fs, revision, path, currpool));
if (! prev_path)
break;
- if (authz_read_func)
- {
- svn_boolean_t readable;
- svn_fs_root_t *tmp_root;
-
- SVN_ERR(svn_fs_revision_root(&tmp_root, fs, revision, currpool));
- SVN_ERR(authz_read_func(&readable, tmp_root, path,
- authz_read_baton, currpool));
- if (! readable)
- {
- svn_pool_destroy(lastpool);
- svn_pool_destroy(currpool);
-
- return SVN_NO_ERROR;
- }
- }
-
/* Assign the current path to all younger revisions until we reach
the copy target rev. */
while ((revision_ptr < revision_ptr_end)
&& (*revision_ptr >= appeared_rev))
{
/* *revision_ptr is allocated out of pool, so we can point
to in the hash table. */
apr_hash_set(*locations, revision_ptr, sizeof(*revision_ptr),
apr_pstrdup(pool, path));
revision_ptr++;
}
/* Ignore all revs between the copy target rev and the copy
source rev (non-inclusive). */
while ((revision_ptr < revision_ptr_end)
&& (*revision_ptr > prev_rev))
revision_ptr++;
/* State update. */
path = prev_path;
revision = prev_rev;
+
+ if (authz_read_func)
+ {
+ svn_boolean_t readable;
+ SVN_ERR(svn_fs_revision_root(&root, fs, revision, currpool));
+ SVN_ERR(authz_read_func(&readable, root, path,
+ authz_read_baton, currpool));
+ if (!readable)
+ {
+ svn_pool_destroy(lastpool);
+ svn_pool_destroy(currpool);
+ return SVN_NO_ERROR;
+ }
+ }
/* Clear last pool and switch. */
svn_pool_clear(lastpool);
tmppool = lastpool;
lastpool = currpool;
currpool = tmppool;
}
/* There are no copies relevant to path@revision. So any remaining
revisions either predate the creation of path@revision or have
the node existing at the same path. We will look up path@lrev
for each remaining location-revision and make sure it is related
to path@revision. */
SVN_ERR(svn_fs_revision_root(&root, fs, revision, currpool));
SVN_ERR(svn_fs_node_id(&id, root, path, pool));
while (revision_ptr < revision_ptr_end)
{
svn_node_kind_t kind;
const svn_fs_id_t *lrev_id;
svn_pool_clear(currpool);
SVN_ERR(svn_fs_revision_root(&root, fs, *revision_ptr, currpool));
SVN_ERR(svn_fs_check_path(&kind, root, path, currpool));
if (kind == svn_node_none)
break;
SVN_ERR(svn_fs_node_id(&lrev_id, root, path, currpool));
if (! svn_fs_check_related(id, lrev_id))
break;
/* The node exists at the same path; record that and advance. */
apr_hash_set(*locations, revision_ptr, sizeof(*revision_ptr),
apr_pstrdup(pool, path));
revision_ptr++;
}
/* Ignore any remaining location-revisions; they predate the
creation of path@revision. */
svn_pool_destroy(lastpool);
svn_pool_destroy(currpool);
return SVN_NO_ERROR;
}
/* Transmit SEGMENT through RECEIVER/RECEIVER_BATON iff a portion of
its revision range fits between END_REV and START_REV, possibly
cropping the range so that it fits *entirely* in that range. */
static svn_error_t *
maybe_crop_and_send_segment(svn_location_segment_t *segment,
svn_revnum_t start_rev,
svn_revnum_t end_rev,
svn_location_segment_receiver_t receiver,
void *receiver_baton,
apr_pool_t *pool)
{
/* We only want to transmit this segment if some portion of it
is between our END_REV and START_REV. */
if (! ((segment->range_start > start_rev)
|| (segment->range_end < end_rev)))
{
/* Correct our segment range when the range straddles one of
our requested revision boundaries. */
if (segment->range_start < end_rev)
segment->range_start = end_rev;
if (segment->range_end > start_rev)
segment->range_end = start_rev;
SVN_ERR(receiver(segment, receiver_baton, pool));
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_repos_node_location_segments(svn_repos_t *repos,
const char *path,
svn_revnum_t peg_revision,
svn_revnum_t start_rev,
svn_revnum_t end_rev,
svn_location_segment_receiver_t receiver,
void *receiver_baton,
svn_repos_authz_func_t authz_read_func,
void *authz_read_baton,
apr_pool_t *pool)
{
svn_fs_t *fs = svn_repos_fs(repos);
svn_stringbuf_t *current_path;
svn_revnum_t youngest_rev = SVN_INVALID_REVNUM, current_rev;
apr_pool_t *subpool;
/* No PEG_REVISION? We'll use HEAD. */
if (! SVN_IS_VALID_REVNUM(peg_revision))
{
SVN_ERR(svn_fs_youngest_rev(&youngest_rev, fs, pool));
peg_revision = youngest_rev;
}
/* No START_REV? We'll use HEAD (which we may have already fetched). */
if (! SVN_IS_VALID_REVNUM(start_rev))
{
if (SVN_IS_VALID_REVNUM(youngest_rev))
start_rev = youngest_rev;
else
SVN_ERR(svn_fs_youngest_rev(&start_rev, fs, pool));
}
/* No END_REV? We'll use 0. */
end_rev = SVN_IS_VALID_REVNUM(end_rev) ? end_rev : 0;
/* Are the revision properly ordered? They better be -- the API
demands it. */
SVN_ERR_ASSERT(end_rev <= start_rev);
SVN_ERR_ASSERT(start_rev <= peg_revision);
/* Ensure that PATH is absolute, because our path-math will depend
on that being the case. */
if (*path != '/')
path = apr_pstrcat(pool, "/", path, (char *)NULL);
/* Auth check. */
if (authz_read_func)
{
svn_fs_root_t *peg_root;
SVN_ERR(svn_fs_revision_root(&peg_root, fs, peg_revision, pool));
SVN_ERR(check_readability(peg_root, path,
authz_read_func, authz_read_baton, pool));
}
/* Okay, let's get searching! */
subpool = svn_pool_create(pool);
current_rev = peg_revision;
current_path = svn_stringbuf_create(path, pool);
while (current_rev >= end_rev)
{
svn_revnum_t appeared_rev, prev_rev;
const char *cur_path, *prev_path;
svn_location_segment_t *segment;
svn_pool_clear(subpool);
cur_path = apr_pstrmemdup(subpool, current_path->data,
current_path->len);
segment = apr_pcalloc(subpool, sizeof(*segment));
segment->range_end = current_rev;
segment->range_start = end_rev;
/* segment path should be absolute without leading '/'. */
segment->path = cur_path + 1;
SVN_ERR(svn_repos__prev_location(&appeared_rev, &prev_path, &prev_rev,
fs, current_rev, cur_path, subpool));
/* If there are no previous locations for this thing (meaning,
it originated at the current path), then we simply need to
find its revision of origin to populate our final segment.
Otherwise, the APPEARED_REV is the start of current segment's
range. */
if (! prev_path)
{
svn_fs_root_t *revroot;
SVN_ERR(svn_fs_revision_root(&revroot, fs, current_rev, subpool));
SVN_ERR(svn_fs_node_origin_rev(&(segment->range_start), revroot,
cur_path, subpool));
if (segment->range_start < end_rev)
segment->range_start = end_rev;
current_rev = SVN_INVALID_REVNUM;
}
else
{
segment->range_start = appeared_rev;
svn_stringbuf_set(current_path, prev_path);
current_rev = prev_rev;
}
/* Report our segment, providing it passes authz muster. */
if (authz_read_func)
{
svn_boolean_t readable;
svn_fs_root_t *cur_rev_root;
/* authz_read_func requires path to have a leading slash. */
const char *abs_path = apr_pstrcat(subpool, "/", segment->path,
(char *)NULL);
SVN_ERR(svn_fs_revision_root(&cur_rev_root, fs,
segment->range_end, subpool));
SVN_ERR(authz_read_func(&readable, cur_rev_root, abs_path,
authz_read_baton, subpool));
if (! readable)
return SVN_NO_ERROR;
}
/* Transmit the segment (if it's within the scope of our concern). */
SVN_ERR(maybe_crop_and_send_segment(segment, start_rev, end_rev,
receiver, receiver_baton, subpool));
/* If we've set CURRENT_REV to SVN_INVALID_REVNUM, we're done
(and didn't ever reach END_REV). */
if (! SVN_IS_VALID_REVNUM(current_rev))
break;
/* If there's a gap in the history, we need to report as much
(if the gap is within the scope of our concern). */
if (segment->range_start - current_rev > 1)
{
svn_location_segment_t *gap_segment;
gap_segment = apr_pcalloc(subpool, sizeof(*gap_segment));
gap_segment->range_end = segment->range_start - 1;
gap_segment->range_start = current_rev + 1;
gap_segment->path = NULL;
SVN_ERR(maybe_crop_and_send_segment(gap_segment, start_rev, end_rev,
receiver, receiver_baton,
subpool));
}
}
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
/* Get the mergeinfo for PATH in REPOS at REVNUM and store it in MERGEINFO. */
static svn_error_t *
get_path_mergeinfo(apr_hash_t **mergeinfo,
svn_fs_t *fs,
const char *path,
svn_revnum_t revnum,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_mergeinfo_catalog_t tmp_catalog;
svn_fs_root_t *root;
apr_array_header_t *paths = apr_array_make(scratch_pool, 1,
sizeof(const char *));
APR_ARRAY_PUSH(paths, const char *) = path;
SVN_ERR(svn_fs_revision_root(&root, fs, revnum, scratch_pool));
/* We do not need to call svn_repos_fs_get_mergeinfo() (which performs authz)
because we will filter out unreadable revisions in
find_interesting_revision(), above */
SVN_ERR(svn_fs_get_mergeinfo2(&tmp_catalog, root, paths,
svn_mergeinfo_inherited, FALSE, TRUE,
result_pool, scratch_pool));
*mergeinfo = svn_hash_gets(tmp_catalog, path);
if (!*mergeinfo)
*mergeinfo = apr_hash_make(result_pool);
return SVN_NO_ERROR;
}
static APR_INLINE svn_boolean_t
is_path_in_hash(apr_hash_t *duplicate_path_revs,
const char *path,
svn_revnum_t revision,
apr_pool_t *pool)
{
const char *key = apr_psprintf(pool, "%s:%ld", path, revision);
void *ptr;
ptr = svn_hash_gets(duplicate_path_revs, key);
return ptr != NULL;
}
struct path_revision
{
svn_revnum_t revnum;
const char *path;
/* Does this path_rev have merges to also be included? */
apr_hash_t *merged_mergeinfo;
/* Is this a merged revision? */
svn_boolean_t merged;
};
/* Check for merges in OLD_PATH_REV->PATH at OLD_PATH_REV->REVNUM. Store
the mergeinfo difference in *MERGED_MERGEINFO, allocated in POOL. The
returned *MERGED_MERGEINFO will be NULL if there are no changes. */
static svn_error_t *
get_merged_mergeinfo(apr_hash_t **merged_mergeinfo,
svn_repos_t *repos,
struct path_revision *old_path_rev,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_hash_t *curr_mergeinfo, *prev_mergeinfo, *deleted, *changed;
svn_error_t *err;
svn_fs_root_t *root;
apr_hash_t *changed_paths;
const char *path = old_path_rev->path;
/* Getting/parsing/diffing svn:mergeinfo is expensive, so only do it
if there is a property change. */
SVN_ERR(svn_fs_revision_root(&root, repos->fs, old_path_rev->revnum,
scratch_pool));
SVN_ERR(svn_fs_paths_changed2(&changed_paths, root, scratch_pool));
while (1)
{
svn_fs_path_change2_t *changed_path = svn_hash_gets(changed_paths, path);
if (changed_path && changed_path->prop_mod)
break;
if (svn_fspath__is_root(path, strlen(path)))
{
*merged_mergeinfo = NULL;
return SVN_NO_ERROR;
}
path = svn_fspath__dirname(path, scratch_pool);
}
/* First, find the mergeinfo difference for old_path_rev->revnum, and
old_path_rev->revnum - 1. */
err = get_path_mergeinfo(&curr_mergeinfo, repos->fs, old_path_rev->path,
old_path_rev->revnum, scratch_pool,
scratch_pool);
if (err)
{
if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
{
/* Issue #3896: If invalid mergeinfo is encountered the
best we can do is ignore it and act is if there are
no mergeinfo differences. */
svn_error_clear(err);
*merged_mergeinfo = NULL;
return SVN_NO_ERROR;
}
else
{
return svn_error_trace(err);
}
}
err = get_path_mergeinfo(&prev_mergeinfo, repos->fs, old_path_rev->path,
old_path_rev->revnum - 1, scratch_pool,
scratch_pool);
if (err && (err->apr_err == SVN_ERR_FS_NOT_FOUND
|| err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR))
{
/* If the path doesn't exist in the previous revision or it does exist
but has invalid mergeinfo (Issue #3896), assume no merges. */
svn_error_clear(err);
*merged_mergeinfo = NULL;
return SVN_NO_ERROR;
}
else
SVN_ERR(err);
/* Then calculate and merge the differences. */
SVN_ERR(svn_mergeinfo_diff2(&deleted, &changed, prev_mergeinfo,
curr_mergeinfo, FALSE, result_pool,
scratch_pool));
SVN_ERR(svn_mergeinfo_merge2(changed, deleted, result_pool, scratch_pool));
/* Store the result. */
if (apr_hash_count(changed))
*merged_mergeinfo = changed;
else
*merged_mergeinfo = NULL;
return SVN_NO_ERROR;
}
static svn_error_t *
find_interesting_revisions(apr_array_header_t *path_revisions,
svn_repos_t *repos,
const char *path,
svn_revnum_t start,
svn_revnum_t end,
svn_boolean_t include_merged_revisions,
svn_boolean_t mark_as_merged,
apr_hash_t *duplicate_path_revs,
svn_repos_authz_func_t authz_read_func,
void *authz_read_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_pool_t *iterpool, *last_pool;
svn_fs_history_t *history;
svn_fs_root_t *root;
svn_node_kind_t kind;
/* We switch between two pools while looping, since we need information from
the last iteration to be available. */
iterpool = svn_pool_create(scratch_pool);
last_pool = svn_pool_create(scratch_pool);
/* The path had better be a file in this revision. */
SVN_ERR(svn_fs_revision_root(&root, repos->fs, end, scratch_pool));
SVN_ERR(svn_fs_check_path(&kind, root, path, scratch_pool));
if (kind != svn_node_file)
return svn_error_createf
(SVN_ERR_FS_NOT_FILE, NULL, _("'%s' is not a file in revision %ld"),
path, end);
/* Open a history object. */
SVN_ERR(svn_fs_node_history(&history, root, path, scratch_pool));
while (1)
{
struct path_revision *path_rev;
svn_revnum_t tmp_revnum;
const char *tmp_path;
apr_pool_t *tmp_pool;
svn_pool_clear(iterpool);
/* Fetch the history object to walk through. */
SVN_ERR(svn_fs_history_prev(&history, history, TRUE, iterpool));
if (!history)
break;
SVN_ERR(svn_fs_history_location(&tmp_path, &tmp_revnum,
history, iterpool));
/* Check to see if we already saw this path (and it's ancestors) */
if (include_merged_revisions
&& is_path_in_hash(duplicate_path_revs, tmp_path,
tmp_revnum, iterpool))
break;
/* Check authorization. */
if (authz_read_func)
{
svn_boolean_t readable;
svn_fs_root_t *tmp_root;
SVN_ERR(svn_fs_revision_root(&tmp_root, repos->fs, tmp_revnum,
iterpool));
SVN_ERR(authz_read_func(&readable, tmp_root, tmp_path,
authz_read_baton, iterpool));
if (! readable)
break;
}
/* We didn't break, so we must really want this path-rev. */
path_rev = apr_palloc(result_pool, sizeof(*path_rev));
path_rev->path = apr_pstrdup(result_pool, tmp_path);
path_rev->revnum = tmp_revnum;
path_rev->merged = mark_as_merged;
APR_ARRAY_PUSH(path_revisions, struct path_revision *) = path_rev;
if (include_merged_revisions)
SVN_ERR(get_merged_mergeinfo(&path_rev->merged_mergeinfo, repos,
path_rev, result_pool, iterpool));
else
path_rev->merged_mergeinfo = NULL;
/* Add the path/rev pair to the hash, so we can filter out future
occurrences of it. We only care about this if including merged
revisions, 'cause that's the only time we can have duplicates. */
svn_hash_sets(duplicate_path_revs,
apr_psprintf(result_pool, "%s:%ld", path_rev->path,
path_rev->revnum),
(void *)0xdeadbeef);
if (path_rev->revnum <= start)
break;
/* Swap pools. */
tmp_pool = iterpool;
iterpool = last_pool;
last_pool = tmp_pool;
}
svn_pool_destroy(iterpool);
svn_pool_destroy(last_pool);
return SVN_NO_ERROR;
}
/* Comparison function to sort path/revisions in increasing order */
static int
compare_path_revisions(const void *a, const void *b)
{
struct path_revision *a_pr = *(struct path_revision *const *)a;
struct path_revision *b_pr = *(struct path_revision *const *)b;
if (a_pr->revnum == b_pr->revnum)
return 0;
return a_pr->revnum < b_pr->revnum ? 1 : -1;
}
static svn_error_t *
find_merged_revisions(apr_array_header_t **merged_path_revisions_out,
svn_revnum_t start,
const apr_array_header_t *mainline_path_revisions,
svn_repos_t *repos,
apr_hash_t *duplicate_path_revs,
svn_repos_authz_func_t authz_read_func,
void *authz_read_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const apr_array_header_t *old;
apr_array_header_t *new_merged_path_revs;
apr_pool_t *iterpool, *last_pool;
apr_array_header_t *merged_path_revisions =
apr_array_make(scratch_pool, 0, sizeof(struct path_revision *));
old = mainline_path_revisions;
iterpool = svn_pool_create(scratch_pool);
last_pool = svn_pool_create(scratch_pool);
do
{
int i;
apr_pool_t *temp_pool;
svn_pool_clear(iterpool);
new_merged_path_revs = apr_array_make(iterpool, 0,
sizeof(struct path_revision *));
/* Iterate over OLD, checking for non-empty mergeinfo. If found, gather
path_revisions for any merged revisions, and store those in NEW. */
for (i = 0; i < old->nelts; i++)
{
apr_pool_t *iterpool2;
apr_hash_index_t *hi;
struct path_revision *old_pr = APR_ARRAY_IDX(old, i,
struct path_revision *);
if (!old_pr->merged_mergeinfo)
continue;
iterpool2 = svn_pool_create(iterpool);
/* Determine and trace the merge sources. */
for (hi = apr_hash_first(iterpool, old_pr->merged_mergeinfo); hi;
hi = apr_hash_next(hi))
{
apr_pool_t *iterpool3;
svn_rangelist_t *rangelist;
const char *path;
int j;
svn_pool_clear(iterpool2);
iterpool3 = svn_pool_create(iterpool2);
apr_hash_this(hi, (void *) &path, NULL, (void *) &rangelist);
for (j = 0; j < rangelist->nelts; j++)
{
svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, j,
svn_merge_range_t *);
svn_node_kind_t kind;
svn_fs_root_t *root;
if (range->end < start)
continue;
svn_pool_clear(iterpool3);
SVN_ERR(svn_fs_revision_root(&root, repos->fs, range->end,
iterpool3));
SVN_ERR(svn_fs_check_path(&kind, root, path, iterpool3));
if (kind != svn_node_file)
continue;
/* Search and find revisions to add to the NEW list. */
SVN_ERR(find_interesting_revisions(new_merged_path_revs,
repos, path,
range->start, range->end,
TRUE, TRUE,
duplicate_path_revs,
authz_read_func,
authz_read_baton,
result_pool, iterpool3));
}
svn_pool_destroy(iterpool3);
}
svn_pool_destroy(iterpool2);
}
/* Append the newly found path revisions with the old ones. */
merged_path_revisions = apr_array_append(iterpool, merged_path_revisions,
new_merged_path_revs);
/* Swap data structures */
old = new_merged_path_revs;
temp_pool = last_pool;
last_pool = iterpool;
iterpool = temp_pool;
}
while (new_merged_path_revs->nelts > 0);
/* Sort MERGED_PATH_REVISIONS in increasing order by REVNUM. */
qsort(merged_path_revisions->elts, merged_path_revisions->nelts,
sizeof(struct path_revision *), compare_path_revisions);
/* Copy to the output array. */
*merged_path_revisions_out = apr_array_copy(result_pool,
merged_path_revisions);
svn_pool_destroy(iterpool);
svn_pool_destroy(last_pool);
return SVN_NO_ERROR;
}
struct send_baton
{
apr_pool_t *iterpool;
apr_pool_t *last_pool;
apr_hash_t *last_props;
const char *last_path;
svn_fs_root_t *last_root;
};
/* Send PATH_REV to HANDLER and HANDLER_BATON, using information provided by
SB. */
static svn_error_t *
send_path_revision(struct path_revision *path_rev,
svn_repos_t *repos,
struct send_baton *sb,
svn_file_rev_handler_t handler,
void *handler_baton)
{
apr_hash_t *rev_props;
apr_hash_t *props;
apr_array_header_t *prop_diffs;
svn_fs_root_t *root;
svn_txdelta_stream_t *delta_stream;
svn_txdelta_window_handler_t delta_handler = NULL;
void *delta_baton = NULL;
apr_pool_t *tmp_pool; /* For swapping */
svn_boolean_t contents_changed;
svn_pool_clear(sb->iterpool);
/* Get the revision properties. */
SVN_ERR(svn_fs_revision_proplist(&rev_props, repos->fs,
path_rev->revnum, sb->iterpool));
/* Open the revision root. */
SVN_ERR(svn_fs_revision_root(&root, repos->fs, path_rev->revnum,
sb->iterpool));
/* Get the file's properties for this revision and compute the diffs. */
SVN_ERR(svn_fs_node_proplist(&props, root, path_rev->path,
sb->iterpool));
SVN_ERR(svn_prop_diffs(&prop_diffs, props, sb->last_props,
sb->iterpool));
/* Check if the contents changed. */
/* Special case: In the first revision, we always provide a delta. */
if (sb->last_root)
SVN_ERR(svn_fs_contents_changed(&contents_changed, sb->last_root,
sb->last_path, root, path_rev->path,
sb->iterpool));
else
contents_changed = TRUE;
/* We have all we need, give to the handler. */
SVN_ERR(handler(handler_baton, path_rev->path, path_rev->revnum,
rev_props, path_rev->merged,
contents_changed ? &delta_handler : NULL,
contents_changed ? &delta_baton : NULL,
prop_diffs, sb->iterpool));
/* Compute and send delta if client asked for it.
Note that this was initialized to NULL, so if !contents_changed,
no deltas will be computed. */
if (delta_handler)
{
/* Get the content delta. */
SVN_ERR(svn_fs_get_file_delta_stream(&delta_stream,
sb->last_root, sb->last_path,
root, path_rev->path,
sb->iterpool));
/* And send. */
SVN_ERR(svn_txdelta_send_txstream(delta_stream,
delta_handler, delta_baton,
sb->iterpool));
}
/* Remember root, path and props for next iteration. */
sb->last_root = root;
sb->last_path = path_rev->path;
sb->last_props = props;
/* Swap the pools. */
tmp_pool = sb->iterpool;
sb->iterpool = sb->last_pool;
sb->last_pool = tmp_pool;
return SVN_NO_ERROR;
}
/* Similar to svn_repos_get_file_revs2() but returns paths while walking
history instead of after collecting all history.
This allows implementing clients to immediately start processing and
stop when they got the information they need. (E.g. all or a specific set
of lines were modified) */
static svn_error_t *
get_file_revs_backwards(svn_repos_t *repos,
const char *path,
svn_revnum_t start,
svn_revnum_t end,
svn_repos_authz_func_t authz_read_func,
void *authz_read_baton,
svn_file_rev_handler_t handler,
void *handler_baton,
apr_pool_t *scratch_pool)
{
apr_pool_t *iterpool, *last_pool;
svn_fs_history_t *history;
svn_fs_root_t *root;
svn_node_kind_t kind;
struct send_baton sb;
/* We switch between two pools while looping and so does the path-rev
handler for actually reported revisions. We do this as we
need just information from last iteration to be available. */
iterpool = svn_pool_create(scratch_pool);
last_pool = svn_pool_create(scratch_pool);
sb.iterpool = svn_pool_create(scratch_pool);
sb.last_pool = svn_pool_create(scratch_pool);
/* We want the first txdelta to be against the empty file. */
sb.last_root = NULL;
sb.last_path = NULL;
/* Create an empty hash table for the first property diff. */
sb.last_props = apr_hash_make(sb.last_pool);
/* The path had better be a file in this revision. */
SVN_ERR(svn_fs_revision_root(&root, repos->fs, end, scratch_pool));
SVN_ERR(svn_fs_check_path(&kind, root, path, scratch_pool));
if (kind != svn_node_file)
return svn_error_createf(SVN_ERR_FS_NOT_FILE,
NULL, _("'%s' is not a file in revision %ld"),
path, end);
/* Open a history object. */
SVN_ERR(svn_fs_node_history(&history, root, path, scratch_pool));
while (1)
{
struct path_revision *path_rev;
svn_revnum_t tmp_revnum;
const char *tmp_path;
svn_pool_clear(iterpool);
/* Fetch the history object to walk through. */
SVN_ERR(svn_fs_history_prev(&history, history, TRUE, iterpool));
if (!history)
break;
SVN_ERR(svn_fs_history_location(&tmp_path, &tmp_revnum,
history, iterpool));
/* Check authorization. */
if (authz_read_func)
{
svn_boolean_t readable;
svn_fs_root_t *tmp_root;
SVN_ERR(svn_fs_revision_root(&tmp_root, repos->fs, tmp_revnum,
iterpool));
SVN_ERR(authz_read_func(&readable, tmp_root, tmp_path,
authz_read_baton, iterpool));
if (! readable)
break;
}
/* We didn't break, so we must really want this path-rev. */
path_rev = apr_palloc(iterpool, sizeof(*path_rev));
path_rev->path = tmp_path;
path_rev->revnum = tmp_revnum;
path_rev->merged = FALSE;
SVN_ERR(send_path_revision(path_rev, repos, &sb,
handler, handler_baton));
if (path_rev->revnum <= start)
break;
/* Swap pools. */
{
apr_pool_t *tmp_pool = iterpool;
iterpool = last_pool;
last_pool = tmp_pool;
}
}
svn_pool_destroy(iterpool);
svn_pool_destroy(last_pool);
svn_pool_destroy(sb.last_pool);
svn_pool_destroy(sb.iterpool);
return SVN_NO_ERROR;
}
/* We don't yet support sending revisions in reverse order; the caller wait
* until we've traced back through the entire history, and then accept
* them from oldest to youngest. Someday this may change, but in the meantime,
* the general algorithm is thus:
*
* 1) Trace back through the history of an object, adding each revision
* found to the MAINLINE_PATH_REVISIONS array, marking any which were
* merges.
* 2) If INCLUDE_MERGED_REVISIONS is TRUE, we repeat Step 1 on each of the
* merged revisions, including them in the MERGED_PATH_REVISIONS, and using
* DUPLICATE_PATH_REVS to avoid tracing the same paths of history multiple
* times.
* 3) Send both MAINLINE_PATH_REVISIONS and MERGED_PATH_REVISIONS from
* oldest to youngest, interleaving as appropriate. This is implemented
* similar to an insertion sort, but instead of inserting into another
* array, we just call the appropriate handler.
*
* 2013-02: Added a very simple reverse for mainline only changes. Before this,
* this would return an error (path not found) or just the first
* revision before end.
*/
svn_error_t *
svn_repos_get_file_revs2(svn_repos_t *repos,
const char *path,
svn_revnum_t start,
svn_revnum_t end,
svn_boolean_t include_merged_revisions,
svn_repos_authz_func_t authz_read_func,
void *authz_read_baton,
svn_file_rev_handler_t handler,
void *handler_baton,
apr_pool_t *scratch_pool)
{
apr_array_header_t *mainline_path_revisions, *merged_path_revisions;
apr_hash_t *duplicate_path_revs;
struct send_baton sb;
int mainline_pos, merged_pos;
if (end < start)
{
if (include_merged_revisions)
return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL);
return svn_error_trace(
get_file_revs_backwards(repos, path,
end, start,
authz_read_func,
authz_read_baton,
handler,
handler_baton,
scratch_pool));
}
/* We switch between two pools while looping, since we need information from
the last iteration to be available. */
sb.iterpool = svn_pool_create(scratch_pool);
sb.last_pool = svn_pool_create(scratch_pool);
/* We want the first txdelta to be against the empty file. */
sb.last_root = NULL;
sb.last_path = NULL;
/* Create an empty hash table for the first property diff. */
sb.last_props = apr_hash_make(sb.last_pool);
/* Get the revisions we are interested in. */
duplicate_path_revs = apr_hash_make(scratch_pool);
mainline_path_revisions = apr_array_make(scratch_pool, 100,
sizeof(struct path_revision *));
SVN_ERR(find_interesting_revisions(mainline_path_revisions, repos, path,
start, end, include_merged_revisions,
FALSE, duplicate_path_revs,
authz_read_func, authz_read_baton,
scratch_pool, sb.iterpool));
/* If we are including merged revisions, go get those, too. */
if (include_merged_revisions)
SVN_ERR(find_merged_revisions(&merged_path_revisions, start,
mainline_path_revisions, repos,
duplicate_path_revs, authz_read_func,
authz_read_baton,
scratch_pool, sb.iterpool));
else
merged_path_revisions = apr_array_make(scratch_pool, 0,
sizeof(struct path_revision *));
/* We must have at least one revision to get. */
SVN_ERR_ASSERT(mainline_path_revisions->nelts > 0);
/* Walk through both mainline and merged revisions, and send them in
reverse chronological order, interleaving as appropriate. */
mainline_pos = mainline_path_revisions->nelts - 1;
merged_pos = merged_path_revisions->nelts - 1;
while (mainline_pos >= 0 && merged_pos >= 0)
{
struct path_revision *main_pr = APR_ARRAY_IDX(mainline_path_revisions,
mainline_pos,
struct path_revision *);
struct path_revision *merged_pr = APR_ARRAY_IDX(merged_path_revisions,
merged_pos,
struct path_revision *);
if (main_pr->revnum <= merged_pr->revnum)
{
SVN_ERR(send_path_revision(main_pr, repos, &sb, handler,
handler_baton));
mainline_pos -= 1;
}
else
{
SVN_ERR(send_path_revision(merged_pr, repos, &sb, handler,
handler_baton));
merged_pos -= 1;
}
}
/* Send any remaining revisions from the mainline list. */
for (; mainline_pos >= 0; mainline_pos -= 1)
{
struct path_revision *main_pr = APR_ARRAY_IDX(mainline_path_revisions,
mainline_pos,
struct path_revision *);
SVN_ERR(send_path_revision(main_pr, repos, &sb, handler, handler_baton));
}
/* Ditto for the merged list. */
for (; merged_pos >= 0; merged_pos -= 1)
{
struct path_revision *merged_pr = APR_ARRAY_IDX(merged_path_revisions,
merged_pos,
struct path_revision *);
SVN_ERR(send_path_revision(merged_pr, repos, &sb, handler,
handler_baton));
}
svn_pool_destroy(sb.last_pool);
svn_pool_destroy(sb.iterpool);
return SVN_NO_ERROR;
}
Index: vendor/subversion/dist/subversion/libsvn_subr/cache-membuffer.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_subr/cache-membuffer.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_subr/cache-membuffer.c (revision 286501)
@@ -1,2373 +1,2428 @@
/*
* cache-membuffer.c: in-memory caching for Subversion
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include <assert.h>
#include <apr_md5.h>
#include <apr_thread_rwlock.h>
#include "svn_pools.h"
#include "svn_checksum.h"
#include "md5.h"
#include "svn_private_config.h"
#include "cache.h"
#include "svn_string.h"
#include "private/svn_dep_compat.h"
#include "private/svn_mutex.h"
#include "private/svn_pseudo_md5.h"
/*
* This svn_cache__t implementation actually consists of two parts:
* a shared (per-process) singleton membuffer cache instance and shallow
* svn_cache__t front-end instances that each use different key spaces.
* For data management, they all forward to the singleton membuffer cache.
*
* A membuffer cache consists of two parts:
*
* 1. A linear data buffer containing cached items in a serialized
* representation. There may be arbitrary gaps between entries.
* 2. A directory of cache entries. This is organized similar to CPU
* data caches: for every possible key, there is exactly one group
* of entries that may contain the header info for an item with
* that given key. The result is a GROUP_SIZE-way associative cache.
*
* Only the start address of these two data parts are given as a native
* pointer. All other references are expressed as offsets to these pointers.
* With that design, it is relatively easy to share the same data structure
* between different processes and / or to persist them on disk. These
* out-of-process features have not been implemented, yet.
*
* The data buffer usage information is implicitly given by the directory
* entries. Every USED entry has a reference to the previous and the next
* used dictionary entry and this double-linked list is ordered by the
* offsets of their item data within the data buffer. So removing data,
* for instance, is done simply by unlinking it from the chain, implicitly
* marking the entry as well as the data buffer section previously
* associated to it as unused.
*
* Insertion can occur at only one, sliding position. It is marked by its
* offset in the data buffer plus the index of the first used entry at or
* behind that position. If this gap is too small to accommodate the new
* item, the insertion window is extended as described below. The new entry
* will always be inserted at the bottom end of the window and since the
* next used entry is known, properly sorted insertion is possible.
*
* To make the cache perform robustly in a wide range of usage scenarios,
* a randomized variant of LFU is used (see ensure_data_insertable for
* details). Every item holds a read hit counter and there is a global read
* hit counter. The more hits an entry has in relation to the average, the
* more it is likely to be kept using a rand()-based condition. The test is
* applied only to the entry following the insertion window. If it doesn't
* get evicted, it is moved to the begin of that window and the window is
* moved.
*
* Moreover, the entry's hits get halved to make that entry more likely to
* be removed the next time the sliding insertion / removal window comes by.
* As a result, frequently used entries are likely not to be dropped until
* they get not used for a while. Also, even a cache thrashing situation
* about 50% of the content survives every 50% of the cache being re-written
* with new entries. For details on the fine-tuning involved, see the
* comments in ensure_data_insertable().
*
* To limit the entry size and management overhead, not the actual item keys
* but only their MD5 checksums will not be stored. This is reasonably safe
* to do since users have only limited control over the full keys, even if
* these contain folder paths. So, it is very hard to deliberately construct
* colliding keys. Random checksum collisions can be shown to be extremely
* unlikely.
*
* All access to the cached data needs to be serialized. Because we want
* to scale well despite that bottleneck, we simply segment the cache into
* a number of independent caches (segments). Items will be multiplexed based
* on their hash key.
*/
+/* APR's read-write lock implementation on Windows is horribly inefficient.
+ * Even with very low contention a runtime overhead of 35% percent has been
+ * measured for 'svn-bench null-export' over ra_serf.
+ *
+ * Use a simple mutex on Windows. Because there is one mutex per segment,
+ * large machines should (and usually can) be configured with large caches
+ * such that read contention is kept low. This is basically the situation
+ * we head before 1.8.
+ */
+#ifdef WIN32
+# define USE_SIMPLE_MUTEX 1
+#else
+# define USE_SIMPLE_MUTEX 0
+#endif
+
/* A 16-way associative cache seems to be a good compromise between
* performance (worst-case lookups) and efficiency-loss due to collisions.
*
* This value may be changed to any positive integer.
*/
#define GROUP_SIZE 16
/* For more efficient copy operations, let's align all data items properly.
* Must be a power of 2.
*/
#define ITEM_ALIGNMENT 16
/* By default, don't create cache segments smaller than this value unless
* the total cache size itself is smaller.
*/
#define DEFAULT_MIN_SEGMENT_SIZE APR_UINT64_C(0x2000000)
/* The minimum segment size we will allow for multi-segmented caches
*/
#define MIN_SEGMENT_SIZE APR_UINT64_C(0x10000)
/* The maximum number of segments allowed. Larger numbers reduce the size
* of each segment, in turn reducing the max size of a cachable item.
* Also, each segment gets its own lock object. The actual number supported
* by the OS may therefore be lower and svn_cache__membuffer_cache_create
* may return an error.
*/
#define MAX_SEGMENT_COUNT 0x10000
/* As of today, APR won't allocate chunks of 4GB or more. So, limit the
* segment size to slightly below that.
*/
#define MAX_SEGMENT_SIZE APR_UINT64_C(0xffff0000)
/* We don't mark the initialization status for every group but initialize
* a number of groups at once. That will allow for a very small init flags
* vector that is likely to fit into the CPU caches even for fairly large
* membuffer caches. For instance, the default of 32 means 8x32 groups per
* byte, i.e. 8 flags/byte x 32 groups/flag x 8 entries/group x 40 index
* bytes/entry x 8 cache bytes/index byte = 1kB init vector / 640MB cache.
*/
#define GROUP_INIT_GRANULARITY 32
/* Invalid index reference value. Equivalent to APR_UINT32_T(-1)
*/
#define NO_INDEX APR_UINT32_MAX
/* To save space in our group structure, we only use 32 bit size values
* and, therefore, limit the size of each entry to just below 4GB.
* Supporting larger items is not a good idea as the data transfer
* to and from the cache would block other threads for a very long time.
*/
#define MAX_ITEM_SIZE ((apr_uint32_t)(0 - ITEM_ALIGNMENT))
/* A 16 byte key type. We use that to identify cache entries.
* The notation as just two integer values will cause many compilers
* to create better code.
*/
typedef apr_uint64_t entry_key_t[2];
/* Debugging / corruption detection support.
* If you define this macro, the getter functions will performed expensive
* checks on the item data, requested keys and entry types. If there is
* a mismatch found in any of them when being compared with the values
* remembered in the setter function, an error will be returned.
*/
#ifdef SVN_DEBUG_CACHE_MEMBUFFER
/* The prefix passed to svn_cache__create_membuffer_cache() effectively
* defines the type of all items stored by that cache instance. We'll take
* the last 7 bytes + \0 as plaintext for easy identification by the dev.
*/
#define PREFIX_TAIL_LEN 8
/* This record will be attached to any cache entry. It tracks item data
* (content), key and type as hash values and is the baseline against which
* the getters will compare their results to detect inconsistencies.
*/
typedef struct entry_tag_t
{
/* MD5 checksum over the serialized the item data.
*/
unsigned char content_hash [APR_MD5_DIGESTSIZE];
/* Hash value of the svn_cache_t instance that wrote the item
* (i.e. a combination of type and repository)
*/
unsigned char prefix_hash [APR_MD5_DIGESTSIZE];
/* Note that this only covers the variable part of the key,
* i.e. it will be different from the full key hash used for
* cache indexing.
*/
unsigned char key_hash [APR_MD5_DIGESTSIZE];
/* Last letters from of the key in human readable format
* (ends with the type identifier, e.g. "DAG")
*/
char prefix_tail[PREFIX_TAIL_LEN];
/* Length of the variable key part.
*/
apr_size_t key_len;
} entry_tag_t;
/* Per svn_cache_t instance initialization helper.
*/
static void get_prefix_tail(const char *prefix, char *prefix_tail)
{
apr_size_t len = strlen(prefix);
apr_size_t to_copy = len > PREFIX_TAIL_LEN-1 ? PREFIX_TAIL_LEN-1 : len;
memset(prefix_tail, 0, PREFIX_TAIL_LEN);
memcpy(prefix_tail, prefix + len - to_copy, to_copy);
}
/* Initialize all members of TAG except for the content hash.
*/
static svn_error_t *store_key_part(entry_tag_t *tag,
entry_key_t prefix_hash,
char *prefix_tail,
const void *key,
apr_size_t key_len,
apr_pool_t *pool)
{
svn_checksum_t *checksum;
SVN_ERR(svn_checksum(&checksum,
svn_checksum_md5,
key,
key_len,
pool));
memcpy(tag->prefix_hash, prefix_hash, sizeof(tag->prefix_hash));
memcpy(tag->key_hash, checksum->digest, sizeof(tag->key_hash));
memcpy(tag->prefix_tail, prefix_tail, sizeof(tag->prefix_tail));
tag->key_len = key_len;
return SVN_NO_ERROR;
}
/* Initialize the content hash member of TAG.
*/
static svn_error_t* store_content_part(entry_tag_t *tag,
const char *data,
apr_size_t size,
apr_pool_t *pool)
{
svn_checksum_t *checksum;
SVN_ERR(svn_checksum(&checksum,
svn_checksum_md5,
data,
size,
pool));
memcpy(tag->content_hash, checksum->digest, sizeof(tag->content_hash));
return SVN_NO_ERROR;
}
/* Compare two tags and fail with an assertion upon differences.
*/
static svn_error_t* assert_equal_tags(const entry_tag_t *lhs,
const entry_tag_t *rhs)
{
SVN_ERR_ASSERT(memcmp(lhs->content_hash, rhs->content_hash,
sizeof(lhs->content_hash)) == 0);
SVN_ERR_ASSERT(memcmp(lhs->prefix_hash, rhs->prefix_hash,
sizeof(lhs->prefix_hash)) == 0);
SVN_ERR_ASSERT(memcmp(lhs->key_hash, rhs->key_hash,
sizeof(lhs->key_hash)) == 0);
SVN_ERR_ASSERT(memcmp(lhs->prefix_tail, rhs->prefix_tail,
sizeof(lhs->prefix_tail)) == 0);
SVN_ERR_ASSERT(lhs->key_len == rhs->key_len);
return SVN_NO_ERROR;
}
/* Reoccurring code snippets.
*/
#define DEBUG_CACHE_MEMBUFFER_TAG_ARG entry_tag_t *tag,
#define DEBUG_CACHE_MEMBUFFER_TAG tag,
#define DEBUG_CACHE_MEMBUFFER_INIT_TAG \
entry_tag_t _tag; \
entry_tag_t *tag = &_tag; \
SVN_ERR(store_key_part(tag, \
cache->prefix, \
cache->prefix_tail, \
key, \
cache->key_len == APR_HASH_KEY_STRING \
? strlen((const char *) key) \
: cache->key_len, \
cache->pool));
#else
/* Don't generate any checks if consistency checks have not been enabled.
*/
#define DEBUG_CACHE_MEMBUFFER_TAG_ARG
#define DEBUG_CACHE_MEMBUFFER_TAG
#define DEBUG_CACHE_MEMBUFFER_INIT_TAG
#endif /* SVN_DEBUG_CACHE_MEMBUFFER */
/* A single dictionary entry. Since all entries will be allocated once
* during cache creation, those entries might be either used or unused.
* An entry is used if and only if it is contained in the doubly-linked
* list of used entries.
*/
typedef struct entry_t
{
/* Identifying the data item. Only valid for used entries.
*/
entry_key_t key;
/* The offset of the cached item's serialized data within the data buffer.
*/
apr_uint64_t offset;
/* Size of the serialized item data. May be 0.
* Only valid for used entries.
*/
apr_size_t size;
/* Number of (read) hits for this entry. Will be reset upon write.
* Only valid for used entries.
*/
apr_uint32_t hit_count;
/* Reference to the next used entry in the order defined by offset.
* NO_INDEX indicates the end of the list; this entry must be referenced
* by the caches membuffer_cache_t.last member. NO_INDEX also implies
* that the data buffer is not used beyond offset+size.
* Only valid for used entries.
*/
apr_uint32_t next;
/* Reference to the previous used entry in the order defined by offset.
* NO_INDEX indicates the end of the list; this entry must be referenced
* by the caches membuffer_cache_t.first member.
* Only valid for used entries.
*/
apr_uint32_t previous;
#ifdef SVN_DEBUG_CACHE_MEMBUFFER
/* Remember type, content and key hashes.
*/
entry_tag_t tag;
#endif
} entry_t;
/* We group dictionary entries to make this GROUP-SIZE-way associative.
*/
typedef struct entry_group_t
{
/* number of entries used [0 .. USED-1] */
apr_uint32_t used;
/* the actual entries */
entry_t entries[GROUP_SIZE];
} entry_group_t;
/* The cache header structure.
*/
struct svn_membuffer_t
{
/* Number of cache segments. Must be a power of 2.
Please note that this structure represents only one such segment
and that all segments must / will report the same values here. */
apr_uint32_t segment_count;
/* The dictionary, GROUP_SIZE * group_count entries long. Never NULL.
*/
entry_group_t *directory;
/* Flag array with group_count / GROUP_INIT_GRANULARITY _bit_ elements.
* Allows for efficiently marking groups as "not initialized".
*/
unsigned char *group_initialized;
/* Size of dictionary in groups. Must be > 0.
*/
apr_uint32_t group_count;
/* Reference to the first (defined by the order content in the data
* buffer) dictionary entry used by any data item.
* NO_INDEX for an empty cache.
*/
apr_uint32_t first;
/* Reference to the last (defined by the order content in the data
* buffer) dictionary entry used by any data item.
* NO_INDEX for an empty cache.
*/
apr_uint32_t last;
/* Reference to the first (defined by the order content in the data
* buffer) used dictionary entry behind the insertion position
* (current_data). If NO_INDEX, the data buffer is free starting at the
* current_data offset.
*/
apr_uint32_t next;
/* Pointer to the data buffer, data_size bytes long. Never NULL.
*/
unsigned char *data;
/* Size of data buffer in bytes. Must be > 0.
*/
apr_uint64_t data_size;
/* Offset in the data buffer where the next insertion shall occur.
*/
apr_uint64_t current_data;
/* Total number of data buffer bytes in use.
*/
apr_uint64_t data_used;
/* Largest entry size that we would accept. For total cache sizes
* less than 4TB (sic!), this is determined by the total cache size.
*/
apr_uint64_t max_entry_size;
/* Number of used dictionary entries, i.e. number of cached items.
* In conjunction with hit_count, this is used calculate the average
* hit count as part of the randomized LFU algorithm.
*/
apr_uint32_t used_entries;
/* Sum of (read) hit counts of all used dictionary entries.
* In conjunction used_entries used_entries, this is used calculate
* the average hit count as part of the randomized LFU algorithm.
*/
apr_uint64_t hit_count;
/* Total number of calls to membuffer_cache_get.
* Purely statistical information that may be used for profiling.
*/
apr_uint64_t total_reads;
/* Total number of calls to membuffer_cache_set.
* Purely statistical information that may be used for profiling.
*/
apr_uint64_t total_writes;
/* Total number of hits since the cache's creation.
* Purely statistical information that may be used for profiling.
*/
apr_uint64_t total_hits;
#if APR_HAS_THREADS
/* A lock for intra-process synchronization to the cache, or NULL if
* the cache's creator doesn't feel the cache needs to be
* thread-safe.
*/
+# if USE_SIMPLE_MUTEX
+ svn_mutex__t *lock;
+# else
apr_thread_rwlock_t *lock;
+# endif
/* If set, write access will wait until they get exclusive access.
* Otherwise, they will become no-ops if the segment is currently
- * read-locked.
+ * read-locked. Only used when LOCK is an r/w lock.
*/
svn_boolean_t allow_blocking_writes;
#endif
};
/* Align integer VALUE to the next ITEM_ALIGNMENT boundary.
*/
#define ALIGN_VALUE(value) (((value) + ITEM_ALIGNMENT-1) & -ITEM_ALIGNMENT)
/* Align POINTER value to the next ITEM_ALIGNMENT boundary.
*/
#define ALIGN_POINTER(pointer) ((void*)ALIGN_VALUE((apr_size_t)(char*)(pointer)))
/* If locking is supported for CACHE, acquire a read lock for it.
*/
static svn_error_t *
read_lock_cache(svn_membuffer_t *cache)
{
#if APR_HAS_THREADS
+# if USE_SIMPLE_MUTEX
+ return svn_mutex__lock(cache->lock);
+# else
if (cache->lock)
{
apr_status_t status = apr_thread_rwlock_rdlock(cache->lock);
if (status)
return svn_error_wrap_apr(status, _("Can't lock cache mutex"));
}
+# endif
#endif
return SVN_NO_ERROR;
}
/* If locking is supported for CACHE, acquire a write lock for it.
*/
static svn_error_t *
write_lock_cache(svn_membuffer_t *cache, svn_boolean_t *success)
{
#if APR_HAS_THREADS
+# if USE_SIMPLE_MUTEX
+
+ return svn_mutex__lock(cache->lock);
+
+# else
+
if (cache->lock)
{
apr_status_t status;
if (cache->allow_blocking_writes)
{
status = apr_thread_rwlock_wrlock(cache->lock);
}
else
{
status = apr_thread_rwlock_trywrlock(cache->lock);
if (SVN_LOCK_IS_BUSY(status))
{
*success = FALSE;
status = APR_SUCCESS;
}
}
if (status)
return svn_error_wrap_apr(status,
_("Can't write-lock cache mutex"));
}
+
+# endif
#endif
return SVN_NO_ERROR;
}
/* If locking is supported for CACHE, acquire an unconditional write lock
* for it.
*/
static svn_error_t *
force_write_lock_cache(svn_membuffer_t *cache)
{
#if APR_HAS_THREADS
+# if USE_SIMPLE_MUTEX
+
+ return svn_mutex__lock(cache->lock);
+
+# else
+
apr_status_t status = apr_thread_rwlock_wrlock(cache->lock);
if (status)
return svn_error_wrap_apr(status,
_("Can't write-lock cache mutex"));
+
+# endif
#endif
return SVN_NO_ERROR;
}
/* If locking is supported for CACHE, release the current lock
* (read or write).
*/
static svn_error_t *
unlock_cache(svn_membuffer_t *cache, svn_error_t *err)
{
#if APR_HAS_THREADS
+# if USE_SIMPLE_MUTEX
+
+ return svn_mutex__unlock(cache->lock, err);
+
+# else
+
if (cache->lock)
{
apr_status_t status = apr_thread_rwlock_unlock(cache->lock);
if (err)
return err;
if (status)
return svn_error_wrap_apr(status, _("Can't unlock cache mutex"));
}
+
+# endif
#endif
return err;
}
/* If supported, guard the execution of EXPR with a read lock to cache.
* Macro has been modeled after SVN_MUTEX__WITH_LOCK.
*/
#define WITH_READ_LOCK(cache, expr) \
do { \
SVN_ERR(read_lock_cache(cache)); \
SVN_ERR(unlock_cache(cache, (expr))); \
} while (0)
/* If supported, guard the execution of EXPR with a write lock to cache.
* Macro has been modeled after SVN_MUTEX__WITH_LOCK.
*
* The write lock process is complicated if we don't allow to wait for
* the lock: If we didn't get the lock, we may still need to remove an
* existing entry for the given key because that content is now stale.
* Once we discovered such an entry, we unconditionally do a blocking
* wait for the write lock. In case no old content could be found, a
* failing lock attempt is simply a no-op and we exit the macro.
*/
#define WITH_WRITE_LOCK(cache, expr) \
do { \
svn_boolean_t got_lock = TRUE; \
SVN_ERR(write_lock_cache(cache, &got_lock)); \
if (!got_lock) \
{ \
svn_boolean_t exists; \
SVN_ERR(entry_exists(cache, group_index, key, &exists)); \
if (exists) \
SVN_ERR(force_write_lock_cache(cache)); \
else \
break; \
} \
SVN_ERR(unlock_cache(cache, (expr))); \
} while (0)
/* Resolve a dictionary entry reference, i.e. return the entry
* for the given IDX.
*/
static APR_INLINE entry_t *
get_entry(svn_membuffer_t *cache, apr_uint32_t idx)
{
return &cache->directory[idx / GROUP_SIZE].entries[idx % GROUP_SIZE];
}
/* Get the entry references for the given ENTRY.
*/
static APR_INLINE apr_uint32_t
get_index(svn_membuffer_t *cache, entry_t *entry)
{
apr_size_t group_index
= ((char *)entry - (char *)cache->directory) / sizeof(entry_group_t);
return (apr_uint32_t)group_index * GROUP_SIZE
+ (apr_uint32_t)(entry - cache->directory[group_index].entries);
}
/* Remove the used ENTRY from the CACHE, i.e. make it "unused".
* In contrast to insertion, removal is possible for any entry.
*/
static void
drop_entry(svn_membuffer_t *cache, entry_t *entry)
{
/* the group that ENTRY belongs to plus a number of useful index values
*/
apr_uint32_t idx = get_index(cache, entry);
apr_uint32_t group_index = idx / GROUP_SIZE;
entry_group_t *group = &cache->directory[group_index];
apr_uint32_t last_in_group = group_index * GROUP_SIZE + group->used - 1;
/* Only valid to be called for used entries.
*/
assert(idx <= last_in_group);
/* update global cache usage counters
*/
cache->used_entries--;
cache->hit_count -= entry->hit_count;
cache->data_used -= entry->size;
/* extend the insertion window, if the entry happens to border it
*/
if (idx == cache->next)
cache->next = entry->next;
else
if (entry->next == cache->next)
{
/* insertion window starts right behind the entry to remove
*/
if (entry->previous == NO_INDEX)
{
/* remove the first entry -> insertion may start at pos 0, now */
cache->current_data = 0;
}
else
{
/* insertion may start right behind the previous entry */
entry_t *previous = get_entry(cache, entry->previous);
cache->current_data = ALIGN_VALUE( previous->offset
+ previous->size);
}
}
/* unlink it from the chain of used entries
*/
if (entry->previous == NO_INDEX)
cache->first = entry->next;
else
get_entry(cache, entry->previous)->next = entry->next;
if (entry->next == NO_INDEX)
cache->last = entry->previous;
else
get_entry(cache, entry->next)->previous = entry->previous;
/* Move last entry into hole (if the removed one is not the last used).
* We need to do this since all used entries are at the beginning of
* the group's entries array.
*/
if (idx < last_in_group)
{
/* copy the last used entry to the removed entry's index
*/
*entry = group->entries[group->used-1];
/* update foreign links to new index
*/
if (last_in_group == cache->next)
cache->next = idx;
if (entry->previous == NO_INDEX)
cache->first = idx;
else
get_entry(cache, entry->previous)->next = idx;
if (entry->next == NO_INDEX)
cache->last = idx;
else
get_entry(cache, entry->next)->previous = idx;
}
/* Update the number of used entries.
*/
group->used--;
}
/* Insert ENTRY into the chain of used dictionary entries. The entry's
* offset and size members must already have been initialized. Also,
* the offset must match the beginning of the insertion window.
*/
static void
insert_entry(svn_membuffer_t *cache, entry_t *entry)
{
/* the group that ENTRY belongs to plus a number of useful index values
*/
apr_uint32_t idx = get_index(cache, entry);
apr_uint32_t group_index = idx / GROUP_SIZE;
entry_group_t *group = &cache->directory[group_index];
entry_t *next = cache->next == NO_INDEX
? NULL
: get_entry(cache, cache->next);
/* The entry must start at the beginning of the insertion window.
* It must also be the first unused entry in the group.
*/
assert(entry->offset == cache->current_data);
assert(idx == group_index * GROUP_SIZE + group->used);
cache->current_data = ALIGN_VALUE(entry->offset + entry->size);
/* update usage counters
*/
cache->used_entries++;
cache->data_used += entry->size;
entry->hit_count = 0;
group->used++;
/* update entry chain
*/
entry->next = cache->next;
if (cache->first == NO_INDEX)
{
/* insert as the first entry and only in the chain
*/
entry->previous = NO_INDEX;
cache->last = idx;
cache->first = idx;
}
else if (next == NULL)
{
/* insert as the last entry in the chain.
* Note that it cannot also be at the beginning of the chain.
*/
entry->previous = cache->last;
get_entry(cache, cache->last)->next = idx;
cache->last = idx;
}
else
{
/* insert either at the start of a non-empty list or
* somewhere in the middle
*/
entry->previous = next->previous;
next->previous = idx;
if (entry->previous != NO_INDEX)
get_entry(cache, entry->previous)->next = idx;
else
cache->first = idx;
}
/* The current insertion position must never point outside our
* data buffer.
*/
assert(cache->current_data <= cache->data_size);
}
/* Map a KEY of 16 bytes to the CACHE and group that shall contain the
* respective item.
*/
static apr_uint32_t
get_group_index(svn_membuffer_t **cache,
entry_key_t key)
{
svn_membuffer_t *segment0 = *cache;
/* select the cache segment to use. they have all the same group_count */
*cache = &segment0[key[0] & (segment0->segment_count -1)];
return key[1] % segment0->group_count;
}
/* Reduce the hit count of ENTRY and update the accumulated hit info
* in CACHE accordingly.
*/
static APR_INLINE void
let_entry_age(svn_membuffer_t *cache, entry_t *entry)
{
apr_uint32_t hits_removed = (entry->hit_count + 1) >> 1;
cache->hit_count -= hits_removed;
entry->hit_count -= hits_removed;
}
/* Returns 0 if the entry group identified by GROUP_INDEX in CACHE has not
* been initialized, yet. In that case, this group can not data. Otherwise,
* a non-zero value is returned.
*/
static APR_INLINE unsigned char
is_group_initialized(svn_membuffer_t *cache, apr_uint32_t group_index)
{
unsigned char flags
= cache->group_initialized[group_index / (8 * GROUP_INIT_GRANULARITY)];
unsigned char bit_mask
= (unsigned char)(1 << ((group_index / GROUP_INIT_GRANULARITY) % 8));
return flags & bit_mask;
}
/* Initializes the section of the directory in CACHE that contains
* the entry group identified by GROUP_INDEX. */
static void
initialize_group(svn_membuffer_t *cache, apr_uint32_t group_index)
{
unsigned char bit_mask;
apr_uint32_t i;
/* range of groups to initialize due to GROUP_INIT_GRANULARITY */
apr_uint32_t first_index =
(group_index / GROUP_INIT_GRANULARITY) * GROUP_INIT_GRANULARITY;
apr_uint32_t last_index = first_index + GROUP_INIT_GRANULARITY;
if (last_index > cache->group_count)
last_index = cache->group_count;
for (i = first_index; i < last_index; ++i)
cache->directory[i].used = 0;
/* set the "initialized" bit for these groups */
bit_mask
= (unsigned char)(1 << ((group_index / GROUP_INIT_GRANULARITY) % 8));
cache->group_initialized[group_index / (8 * GROUP_INIT_GRANULARITY)]
|= bit_mask;
}
/* Given the GROUP_INDEX that shall contain an entry with the hash key
* TO_FIND, find that entry in the specified group.
*
* If FIND_EMPTY is not set, this function will return the one used entry
* that actually matches the hash or NULL, if no such entry exists.
*
* If FIND_EMPTY has been set, this function will drop the one used entry
* that actually matches the hash (i.e. make it fit to be replaced with
* new content), an unused entry or a forcibly removed entry (if all
* group entries are currently in use). The entries' hash value will be
* initialized with TO_FIND.
*/
static entry_t *
find_entry(svn_membuffer_t *cache,
apr_uint32_t group_index,
const apr_uint64_t to_find[2],
svn_boolean_t find_empty)
{
entry_group_t *group;
entry_t *entry = NULL;
apr_size_t i;
/* get the group that *must* contain the entry
*/
group = &cache->directory[group_index];
/* If the entry group has not been initialized, yet, there is no data.
*/
if (! is_group_initialized(cache, group_index))
{
if (find_empty)
{
initialize_group(cache, group_index);
entry = &group->entries[0];
/* initialize entry for the new key */
entry->key[0] = to_find[0];
entry->key[1] = to_find[1];
}
return entry;
}
/* try to find the matching entry
*/
for (i = 0; i < group->used; ++i)
if ( to_find[0] == group->entries[i].key[0]
&& to_find[1] == group->entries[i].key[1])
{
/* found it
*/
entry = &group->entries[i];
if (find_empty)
drop_entry(cache, entry);
else
return entry;
}
/* None found. Are we looking for a free entry?
*/
if (find_empty)
{
/* if there is no empty entry, delete the oldest entry
*/
if (group->used == GROUP_SIZE)
{
/* every entry gets the same chance of being removed.
* Otherwise, we free the first entry, fill it and
* remove it again on the next occasion without considering
* the other entries in this group.
*/
entry = &group->entries[rand() % GROUP_SIZE];
for (i = 1; i < GROUP_SIZE; ++i)
if (entry->hit_count > group->entries[i].hit_count)
entry = &group->entries[i];
/* for the entries that don't have been removed,
* reduce their hit counts to put them at a relative
* disadvantage the next time.
*/
for (i = 0; i < GROUP_SIZE; ++i)
if (entry != &group->entries[i])
let_entry_age(cache, entry);
drop_entry(cache, entry);
}
/* initialize entry for the new key
*/
entry = &group->entries[group->used];
entry->key[0] = to_find[0];
entry->key[1] = to_find[1];
}
return entry;
}
/* Move a surviving ENTRY from just behind the insertion window to
* its beginning and move the insertion window up accordingly.
*/
static void
move_entry(svn_membuffer_t *cache, entry_t *entry)
{
apr_size_t size = ALIGN_VALUE(entry->size);
/* This entry survived this cleansing run. Reset half of its
* hit count so that its removal gets more likely in the next
* run unless someone read / hit this entry in the meantime.
*/
let_entry_age(cache, entry);
/* Move the entry to the start of the empty / insertion section
* (if it isn't there already). Size-aligned moves are legal
* since all offsets and block sizes share this same alignment.
* Size-aligned moves tend to be faster than non-aligned ones
* because no "odd" bytes at the end need to special treatment.
*/
if (entry->offset != cache->current_data)
{
memmove(cache->data + cache->current_data,
cache->data + entry->offset,
size);
entry->offset = cache->current_data;
}
/* The insertion position is now directly behind this entry.
*/
cache->current_data = entry->offset + size;
cache->next = entry->next;
/* The current insertion position must never point outside our
* data buffer.
*/
assert(cache->current_data <= cache->data_size);
}
/* If necessary, enlarge the insertion window until it is at least
* SIZE bytes long. SIZE must not exceed the data buffer size.
* Return TRUE if enough room could be found or made. A FALSE result
* indicates that the respective item shall not be added.
*/
static svn_boolean_t
ensure_data_insertable(svn_membuffer_t *cache, apr_size_t size)
{
entry_t *entry;
apr_uint64_t average_hit_value;
apr_uint64_t threshold;
/* accumulated size of the entries that have been removed to make
* room for the new one.
*/
apr_size_t drop_size = 0;
/* This loop will eventually terminate because every cache entry
* would get dropped eventually:
* - hit counts become 0 after the got kept for 32 full scans
* - larger elements get dropped as soon as their hit count is 0
* - smaller and smaller elements get removed as the average
* entry size drops (average drops by a factor of 8 per scan)
* - after no more than 43 full scans, all elements would be removed
*
* Since size is < 4th of the cache size and about 50% of all
* entries get removed by a scan, it is very unlikely that more
* than a fractional scan will be necessary.
*/
while (1)
{
/* first offset behind the insertion window
*/
apr_uint64_t end = cache->next == NO_INDEX
? cache->data_size
: get_entry(cache, cache->next)->offset;
/* leave function as soon as the insertion window is large enough
*/
if (end >= size + cache->current_data)
return TRUE;
/* Don't be too eager to cache data. Smaller items will fit into
* the cache after dropping a single item. Of the larger ones, we
* will only accept about 50%. They are also likely to get evicted
* soon due to their notoriously low hit counts.
*
* As long as enough similarly or even larger sized entries already
* exist in the cache, much less insert requests will be rejected.
*/
if (2 * drop_size > size)
return FALSE;
/* try to enlarge the insertion window
*/
if (cache->next == NO_INDEX)
{
/* We reached the end of the data buffer; restart at the beginning.
* Due to the randomized nature of our LFU implementation, very
* large data items may require multiple passes. Therefore, SIZE
* should be restricted to significantly less than data_size.
*/
cache->current_data = 0;
cache->next = cache->first;
}
else
{
entry = get_entry(cache, cache->next);
/* Keep entries that are very small. Those are likely to be data
* headers or similar management structures. So, they are probably
* important while not occupying much space.
* But keep them only as long as they are a minority.
*/
if ( (apr_uint64_t)entry->size * cache->used_entries
< cache->data_used / 8)
{
move_entry(cache, entry);
}
else
{
svn_boolean_t keep;
if (cache->hit_count > cache->used_entries)
{
/* Roll the dice and determine a threshold somewhere from 0 up
* to 2 times the average hit count.
*/
average_hit_value = cache->hit_count / cache->used_entries;
threshold = (average_hit_value+1) * (rand() % 4096) / 2048;
keep = entry->hit_count >= threshold;
}
else
{
/* general hit count is low. Keep everything that got hit
* at all and assign some 50% survival chance to everything
* else.
*/
keep = (entry->hit_count > 0) || (rand() & 1);
}
/* keepers or destroyers? */
if (keep)
{
move_entry(cache, entry);
}
else
{
/* Drop the entry from the end of the insertion window, if it
* has been hit less than the threshold. Otherwise, keep it and
* move the insertion window one entry further.
*/
drop_size += entry->size;
drop_entry(cache, entry);
}
}
}
}
/* This will never be reached. But if it was, "can't insert" was the
* right answer. */
}
/* Mimic apr_pcalloc in APR_POOL_DEBUG mode, i.e. handle failed allocations
* (e.g. OOM) properly: Allocate at least SIZE bytes from POOL and zero
* the content of the allocated memory if ZERO has been set. Return NULL
* upon failed allocations.
*
* Also, satisfy our buffer alignment needs for performance reasons.
*/
static void* secure_aligned_alloc(apr_pool_t *pool,
apr_size_t size,
svn_boolean_t zero)
{
void* memory = apr_palloc(pool, size + ITEM_ALIGNMENT);
if (memory != NULL)
{
memory = ALIGN_POINTER(memory);
if (zero)
memset(memory, 0, size);
}
return memory;
}
svn_error_t *
svn_cache__membuffer_cache_create(svn_membuffer_t **cache,
apr_size_t total_size,
apr_size_t directory_size,
apr_size_t segment_count,
svn_boolean_t thread_safe,
svn_boolean_t allow_blocking_writes,
apr_pool_t *pool)
{
svn_membuffer_t *c;
apr_uint32_t seg;
apr_uint32_t group_count;
apr_uint32_t group_init_size;
apr_uint64_t data_size;
apr_uint64_t max_entry_size;
/* Limit the total size (only relevant if we can address > 4GB)
*/
#if APR_SIZEOF_VOIDP > 4
if (total_size > MAX_SEGMENT_SIZE * MAX_SEGMENT_COUNT)
total_size = MAX_SEGMENT_SIZE * MAX_SEGMENT_COUNT;
#endif
/* Limit the segment count
*/
if (segment_count > MAX_SEGMENT_COUNT)
segment_count = MAX_SEGMENT_COUNT;
if (segment_count * MIN_SEGMENT_SIZE > total_size)
segment_count = total_size / MIN_SEGMENT_SIZE;
/* The segment count must be a power of two. Round it down as necessary.
*/
while ((segment_count & (segment_count-1)) != 0)
segment_count &= segment_count-1;
/* if the caller hasn't provided a reasonable segment count or the above
* limitations set it to 0, derive one from the absolute cache size
*/
if (segment_count < 1)
{
/* Determine a reasonable number of cache segments. Segmentation is
* only useful for multi-threaded / multi-core servers as it reduces
* lock contention on these systems.
*
* But on these systems, we can assume that ample memory has been
* allocated to this cache. Smaller caches should not be segmented
* as this severely limits the maximum size of cachable items.
*
* Segments should not be smaller than 32MB and max. cachable item
* size should grow as fast as segmentation.
*/
apr_uint32_t segment_count_shift = 0;
while (((2 * DEFAULT_MIN_SEGMENT_SIZE) << (2 * segment_count_shift))
< total_size)
++segment_count_shift;
segment_count = (apr_size_t)1 << segment_count_shift;
}
/* If we have an extremely large cache (>512 GB), the default segment
* size may exceed the amount allocatable as one chunk. In that case,
* increase segmentation until we are under the threshold.
*/
while ( total_size / segment_count > MAX_SEGMENT_SIZE
&& segment_count < MAX_SEGMENT_COUNT)
segment_count *= 2;
/* allocate cache as an array of segments / cache objects */
c = apr_palloc(pool, segment_count * sizeof(*c));
/* Split total cache size into segments of equal size
*/
total_size /= segment_count;
directory_size /= segment_count;
/* prevent pathological conditions: ensure a certain minimum cache size
*/
if (total_size < 2 * sizeof(entry_group_t))
total_size = 2 * sizeof(entry_group_t);
/* adapt the dictionary size accordingly, if necessary:
* It must hold at least one group and must not exceed the cache size.
*/
if (directory_size > total_size - sizeof(entry_group_t))
directory_size = total_size - sizeof(entry_group_t);
if (directory_size < sizeof(entry_group_t))
directory_size = sizeof(entry_group_t);
/* limit the data size to what we can address.
* Note that this cannot overflow since all values are of size_t.
* Also, make it a multiple of the item placement granularity to
* prevent subtle overflows.
*/
data_size = ALIGN_VALUE(total_size - directory_size + 1) - ITEM_ALIGNMENT;
/* For cache sizes > 4TB, individual cache segments will be larger
* than 16GB allowing for >4GB entries. But caching chunks larger
* than 4GB is simply not supported.
*/
max_entry_size = data_size / 4 > MAX_ITEM_SIZE
? MAX_ITEM_SIZE
: data_size / 4;
/* to keep the entries small, we use 32 bit indexes only
* -> we need to ensure that no more then 4G entries exist.
*
* Note, that this limit could only be exceeded in a very
* theoretical setup with about 1EB of cache.
*/
group_count = directory_size / sizeof(entry_group_t)
>= (APR_UINT32_MAX / GROUP_SIZE)
? (APR_UINT32_MAX / GROUP_SIZE) - 1
: (apr_uint32_t)(directory_size / sizeof(entry_group_t));
group_init_size = 1 + group_count / (8 * GROUP_INIT_GRANULARITY);
for (seg = 0; seg < segment_count; ++seg)
{
/* allocate buffers and initialize cache members
*/
c[seg].segment_count = (apr_uint32_t)segment_count;
c[seg].group_count = group_count;
c[seg].directory = apr_pcalloc(pool,
group_count * sizeof(entry_group_t));
/* Allocate and initialize directory entries as "not initialized",
hence "unused" */
c[seg].group_initialized = apr_pcalloc(pool, group_init_size);
c[seg].first = NO_INDEX;
c[seg].last = NO_INDEX;
c[seg].next = NO_INDEX;
c[seg].data_size = data_size;
c[seg].data = secure_aligned_alloc(pool, (apr_size_t)data_size, FALSE);
c[seg].current_data = 0;
c[seg].data_used = 0;
c[seg].max_entry_size = max_entry_size;
c[seg].used_entries = 0;
c[seg].hit_count = 0;
c[seg].total_reads = 0;
c[seg].total_writes = 0;
c[seg].total_hits = 0;
/* were allocations successful?
* If not, initialize a minimal cache structure.
*/
if (c[seg].data == NULL || c[seg].directory == NULL)
{
/* We are OOM. There is no need to proceed with "half a cache".
*/
return svn_error_wrap_apr(APR_ENOMEM, "OOM");
}
#if APR_HAS_THREADS
/* A lock for intra-process synchronization to the cache, or NULL if
* the cache's creator doesn't feel the cache needs to be
* thread-safe.
*/
+# if USE_SIMPLE_MUTEX
+
+ SVN_ERR(svn_mutex__init(&c[seg].lock, thread_safe, pool));
+
+# else
+
c[seg].lock = NULL;
if (thread_safe)
{
apr_status_t status =
apr_thread_rwlock_create(&(c[seg].lock), pool);
if (status)
return svn_error_wrap_apr(status, _("Can't create cache mutex"));
}
+
+# endif
/* Select the behavior of write operations.
*/
c[seg].allow_blocking_writes = allow_blocking_writes;
#endif
}
/* done here
*/
*cache = c;
return SVN_NO_ERROR;
}
/* Look for the cache entry in group GROUP_INDEX of CACHE, identified
* by the hash value TO_FIND and set *FOUND accordingly.
*
* Note: This function requires the caller to serialize access.
* Don't call it directly, call entry_exists instead.
*/
static svn_error_t *
entry_exists_internal(svn_membuffer_t *cache,
apr_uint32_t group_index,
entry_key_t to_find,
svn_boolean_t *found)
{
*found = find_entry(cache, group_index, to_find, FALSE) != NULL;
return SVN_NO_ERROR;
}
/* Look for the cache entry in group GROUP_INDEX of CACHE, identified
* by the hash value TO_FIND and set *FOUND accordingly.
*/
static svn_error_t *
entry_exists(svn_membuffer_t *cache,
apr_uint32_t group_index,
entry_key_t to_find,
svn_boolean_t *found)
{
WITH_READ_LOCK(cache,
entry_exists_internal(cache,
group_index,
to_find,
found));
return SVN_NO_ERROR;
}
/* Try to insert the serialized item given in BUFFER with SIZE into
* the group GROUP_INDEX of CACHE and uniquely identify it by hash
* value TO_FIND.
*
* However, there is no guarantee that it will actually be put into
* the cache. If there is already some data associated with TO_FIND,
* it will be removed from the cache even if the new data cannot
* be inserted.
*
* Note: This function requires the caller to serialization access.
* Don't call it directly, call membuffer_cache_get_partial instead.
*/
static svn_error_t *
membuffer_cache_set_internal(svn_membuffer_t *cache,
entry_key_t to_find,
apr_uint32_t group_index,
char *buffer,
apr_size_t size,
DEBUG_CACHE_MEMBUFFER_TAG_ARG
apr_pool_t *scratch_pool)
{
/* first, look for a previous entry for the given key */
entry_t *entry = find_entry(cache, group_index, to_find, FALSE);
/* if there is an old version of that entry and the new data fits into
* the old spot, just re-use that space. */
if (entry && ALIGN_VALUE(entry->size) >= size && buffer)
{
/* Careful! We need to cast SIZE to the full width of CACHE->DATA_USED
* lest we run into trouble with 32 bit underflow *not* treated as a
* negative value.
*/
cache->data_used += (apr_uint64_t)size - entry->size;
entry->size = size;
#ifdef SVN_DEBUG_CACHE_MEMBUFFER
/* Remember original content, type and key (hashes)
*/
SVN_ERR(store_content_part(tag, buffer, size, scratch_pool));
memcpy(&entry->tag, tag, sizeof(*tag));
#endif
if (size)
memcpy(cache->data + entry->offset, buffer, size);
cache->total_writes++;
return SVN_NO_ERROR;
}
/* if necessary, enlarge the insertion window.
*/
if ( buffer != NULL
&& cache->max_entry_size >= size
&& ensure_data_insertable(cache, size))
{
/* Remove old data for this key, if that exists.
* Get an unused entry for the key and and initialize it with
* the serialized item's (future) position within data buffer.
*/
entry = find_entry(cache, group_index, to_find, TRUE);
entry->size = size;
entry->offset = cache->current_data;
#ifdef SVN_DEBUG_CACHE_MEMBUFFER
/* Remember original content, type and key (hashes)
*/
SVN_ERR(store_content_part(tag, buffer, size, scratch_pool));
memcpy(&entry->tag, tag, sizeof(*tag));
#endif
/* Link the entry properly.
*/
insert_entry(cache, entry);
/* Copy the serialized item data into the cache.
*/
if (size)
memcpy(cache->data + entry->offset, buffer, size);
cache->total_writes++;
}
else
{
/* if there is already an entry for this key, drop it.
* Since ensure_data_insertable may have removed entries from
* ENTRY's group, re-do the lookup.
*/
entry = find_entry(cache, group_index, to_find, FALSE);
if (entry)
drop_entry(cache, entry);
}
return SVN_NO_ERROR;
}
/* Try to insert the ITEM and use the KEY to uniquely identify it.
* However, there is no guarantee that it will actually be put into
* the cache. If there is already some data associated to the KEY,
* it will be removed from the cache even if the new data cannot
* be inserted.
*
* The SERIALIZER is called to transform the ITEM into a single,
* flat data buffer. Temporary allocations may be done in POOL.
*/
static svn_error_t *
membuffer_cache_set(svn_membuffer_t *cache,
entry_key_t key,
void *item,
svn_cache__serialize_func_t serializer,
DEBUG_CACHE_MEMBUFFER_TAG_ARG
apr_pool_t *scratch_pool)
{
apr_uint32_t group_index;
void *buffer = NULL;
apr_size_t size = 0;
/* find the entry group that will hold the key.
*/
group_index = get_group_index(&cache, key);
/* Serialize data data.
*/
if (item)
SVN_ERR(serializer(&buffer, &size, item, scratch_pool));
/* The actual cache data access needs to sync'ed
*/
WITH_WRITE_LOCK(cache,
membuffer_cache_set_internal(cache,
key,
group_index,
buffer,
size,
DEBUG_CACHE_MEMBUFFER_TAG
scratch_pool));
return SVN_NO_ERROR;
}
/* Look for the cache entry in group GROUP_INDEX of CACHE, identified
* by the hash value TO_FIND. If no item has been stored for KEY,
* *BUFFER will be NULL. Otherwise, return a copy of the serialized
* data in *BUFFER and return its size in *ITEM_SIZE. Allocations will
* be done in POOL.
*
* Note: This function requires the caller to serialization access.
* Don't call it directly, call membuffer_cache_get_partial instead.
*/
static svn_error_t *
membuffer_cache_get_internal(svn_membuffer_t *cache,
apr_uint32_t group_index,
entry_key_t to_find,
char **buffer,
apr_size_t *item_size,
DEBUG_CACHE_MEMBUFFER_TAG_ARG
apr_pool_t *result_pool)
{
entry_t *entry;
apr_size_t size;
/* The actual cache data access needs to sync'ed
*/
entry = find_entry(cache, group_index, to_find, FALSE);
cache->total_reads++;
if (entry == NULL)
{
/* no such entry found.
*/
*buffer = NULL;
*item_size = 0;
return SVN_NO_ERROR;
}
size = ALIGN_VALUE(entry->size);
*buffer = ALIGN_POINTER(apr_palloc(result_pool, size + ITEM_ALIGNMENT-1));
memcpy(*buffer, (const char*)cache->data + entry->offset, size);
#ifdef SVN_DEBUG_CACHE_MEMBUFFER
/* Check for overlapping entries.
*/
SVN_ERR_ASSERT(entry->next == NO_INDEX ||
entry->offset + size
<= get_entry(cache, entry->next)->offset);
/* Compare original content, type and key (hashes)
*/
SVN_ERR(store_content_part(tag, *buffer, entry->size, result_pool));
SVN_ERR(assert_equal_tags(&entry->tag, tag));
#endif
/* update hit statistics
*/
entry->hit_count++;
cache->hit_count++;
cache->total_hits++;
*item_size = entry->size;
return SVN_NO_ERROR;
}
/* Look for the *ITEM identified by KEY. If no item has been stored
* for KEY, *ITEM will be NULL. Otherwise, the DESERIALIZER is called
* re-construct the proper object from the serialized data.
* Allocations will be done in POOL.
*/
static svn_error_t *
membuffer_cache_get(svn_membuffer_t *cache,
entry_key_t key,
void **item,
svn_cache__deserialize_func_t deserializer,
DEBUG_CACHE_MEMBUFFER_TAG_ARG
apr_pool_t *result_pool)
{
apr_uint32_t group_index;
char *buffer;
apr_size_t size;
/* find the entry group that will hold the key.
*/
group_index = get_group_index(&cache, key);
WITH_READ_LOCK(cache,
membuffer_cache_get_internal(cache,
group_index,
key,
&buffer,
&size,
DEBUG_CACHE_MEMBUFFER_TAG
result_pool));
/* re-construct the original data object from its serialized form.
*/
if (buffer == NULL)
{
*item = NULL;
return SVN_NO_ERROR;
}
return deserializer(item, buffer, size, result_pool);
}
/* Look for the cache entry in group GROUP_INDEX of CACHE, identified
* by the hash value TO_FIND. FOUND indicates whether that entry exists.
* If not found, *ITEM will be NULL.
*
* Otherwise, the DESERIALIZER is called with that entry and the BATON
* provided and will extract the desired information. The result is set
* in *ITEM. Allocations will be done in POOL.
*
* Note: This function requires the caller to serialization access.
* Don't call it directly, call membuffer_cache_get_partial instead.
*/
static svn_error_t *
membuffer_cache_get_partial_internal(svn_membuffer_t *cache,
apr_uint32_t group_index,
entry_key_t to_find,
void **item,
svn_boolean_t *found,
svn_cache__partial_getter_func_t deserializer,
void *baton,
DEBUG_CACHE_MEMBUFFER_TAG_ARG
apr_pool_t *result_pool)
{
entry_t *entry = find_entry(cache, group_index, to_find, FALSE);
cache->total_reads++;
if (entry == NULL)
{
*item = NULL;
*found = FALSE;
return SVN_NO_ERROR;
}
else
{
*found = TRUE;
entry->hit_count++;
cache->hit_count++;
cache->total_hits++;
#ifdef SVN_DEBUG_CACHE_MEMBUFFER
/* Check for overlapping entries.
*/
SVN_ERR_ASSERT(entry->next == NO_INDEX ||
entry->offset + entry->size
<= get_entry(cache, entry->next)->offset);
/* Compare original content, type and key (hashes)
*/
SVN_ERR(store_content_part(tag,
(const char*)cache->data + entry->offset,
entry->size,
result_pool));
SVN_ERR(assert_equal_tags(&entry->tag, tag));
#endif
return deserializer(item,
(const char*)cache->data + entry->offset,
entry->size,
baton,
result_pool);
}
}
/* Look for the cache entry identified by KEY. FOUND indicates
* whether that entry exists. If not found, *ITEM will be NULL. Otherwise,
* the DESERIALIZER is called with that entry and the BATON provided
* and will extract the desired information. The result is set in *ITEM.
* Allocations will be done in POOL.
*/
static svn_error_t *
membuffer_cache_get_partial(svn_membuffer_t *cache,
entry_key_t key,
void **item,
svn_boolean_t *found,
svn_cache__partial_getter_func_t deserializer,
void *baton,
DEBUG_CACHE_MEMBUFFER_TAG_ARG
apr_pool_t *result_pool)
{
apr_uint32_t group_index = get_group_index(&cache, key);
WITH_READ_LOCK(cache,
membuffer_cache_get_partial_internal
(cache, group_index, key, item, found,
deserializer, baton, DEBUG_CACHE_MEMBUFFER_TAG
result_pool));
return SVN_NO_ERROR;
}
/* Look for the cache entry in group GROUP_INDEX of CACHE, identified
* by the hash value TO_FIND. If no entry has been found, the function
* returns without modifying the cache.
*
* Otherwise, FUNC is called with that entry and the BATON provided
* and may modify the cache entry. Allocations will be done in POOL.
*
* Note: This function requires the caller to serialization access.
* Don't call it directly, call membuffer_cache_set_partial instead.
*/
static svn_error_t *
membuffer_cache_set_partial_internal(svn_membuffer_t *cache,
apr_uint32_t group_index,
entry_key_t to_find,
svn_cache__partial_setter_func_t func,
void *baton,
DEBUG_CACHE_MEMBUFFER_TAG_ARG
apr_pool_t *scratch_pool)
{
/* cache item lookup
*/
entry_t *entry = find_entry(cache, group_index, to_find, FALSE);
cache->total_reads++;
/* this function is a no-op if the item is not in cache
*/
if (entry != NULL)
{
svn_error_t *err;
/* access the serialized cache item */
char *data = (char*)cache->data + entry->offset;
char *orig_data = data;
apr_size_t size = entry->size;
entry->hit_count++;
cache->hit_count++;
cache->total_writes++;
#ifdef SVN_DEBUG_CACHE_MEMBUFFER
/* Check for overlapping entries.
*/
SVN_ERR_ASSERT(entry->next == NO_INDEX ||
entry->offset + size
<= get_entry(cache, entry->next)->offset);
/* Compare original content, type and key (hashes)
*/
SVN_ERR(store_content_part(tag, data, size, scratch_pool));
SVN_ERR(assert_equal_tags(&entry->tag, tag));
#endif
/* modify it, preferably in-situ.
*/
err = func((void **)&data, &size, baton, scratch_pool);
if (err)
{
/* Something somewhere when wrong while FUNC was modifying the
* changed item. Thus, it might have become invalid /corrupted.
* We better drop that.
*/
drop_entry(cache, entry);
}
else
{
/* if the modification caused a re-allocation, we need to remove
* the old entry and to copy the new data back into cache.
*/
if (data != orig_data)
{
/* Remove the old entry and try to make space for the new one.
*/
drop_entry(cache, entry);
if ( (cache->max_entry_size >= size)
&& ensure_data_insertable(cache, size))
{
/* Write the new entry.
*/
entry = find_entry(cache, group_index, to_find, TRUE);
entry->size = size;
entry->offset = cache->current_data;
if (size)
memcpy(cache->data + entry->offset, data, size);
/* Link the entry properly.
*/
insert_entry(cache, entry);
}
}
#ifdef SVN_DEBUG_CACHE_MEMBUFFER
/* Remember original content, type and key (hashes)
*/
SVN_ERR(store_content_part(tag, data, size, scratch_pool));
memcpy(&entry->tag, tag, sizeof(*tag));
#endif
}
}
return SVN_NO_ERROR;
}
/* Look for the cache entry identified by KEY. If no entry
* has been found, the function returns without modifying the cache.
* Otherwise, FUNC is called with that entry and the BATON provided
* and may modify the cache entry. Allocations will be done in POOL.
*/
static svn_error_t *
membuffer_cache_set_partial(svn_membuffer_t *cache,
entry_key_t key,
svn_cache__partial_setter_func_t func,
void *baton,
DEBUG_CACHE_MEMBUFFER_TAG_ARG
apr_pool_t *scratch_pool)
{
/* cache item lookup
*/
apr_uint32_t group_index = get_group_index(&cache, key);
WITH_WRITE_LOCK(cache,
membuffer_cache_set_partial_internal
(cache, group_index, key, func, baton,
DEBUG_CACHE_MEMBUFFER_TAG
scratch_pool));
/* done here -> unlock the cache
*/
return SVN_NO_ERROR;
}
/* Implement the svn_cache__t interface on top of a shared membuffer cache.
*
* Because membuffer caches tend to be very large, there will be rather few
* of them (usually only one). Thus, the same instance shall be used as the
* backend to many application-visible svn_cache__t instances. This should
* also achieve global resource usage fairness.
*
* To accommodate items from multiple resources, the individual keys must be
* unique over all sources. This is achieved by simply adding a prefix key
* that unambiguously identifies the item's context (e.g. path to the
* respective repository). The prefix will be set upon construction of the
* svn_cache__t instance.
*/
/* Internal cache structure (used in svn_cache__t.cache_internal) basically
* holding the additional parameters needed to call the respective membuffer
* functions.
*/
typedef struct svn_membuffer_cache_t
{
/* this is where all our data will end up in
*/
svn_membuffer_t *membuffer;
/* use this conversion function when inserting an item into the memcache
*/
svn_cache__serialize_func_t serializer;
/* use this conversion function when reading an item from the memcache
*/
svn_cache__deserialize_func_t deserializer;
/* Prepend this byte sequence to any key passed to us.
* This makes (very likely) our keys different from all keys used
* by other svn_membuffer_cache_t instances.
*/
entry_key_t prefix;
/* A copy of the unmodified prefix. It is being used as a user-visible
* ID for this cache instance.
*/
const char* full_prefix;
/* length of the keys that will be passed to us through the
* svn_cache_t interface. May be APR_HASH_KEY_STRING.
*/
apr_ssize_t key_len;
/* Temporary buffer containing the hash key for the current access
*/
entry_key_t combined_key;
/* a pool for temporary allocations during get() and set()
*/
apr_pool_t *pool;
/* an internal counter that is used to clear the pool from time to time
* but not too frequently.
*/
int alloc_counter;
/* if enabled, this will serialize the access to this instance.
*/
svn_mutex__t *mutex;
#ifdef SVN_DEBUG_CACHE_MEMBUFFER
/* Invariant tag info for all items stored by this cache instance.
*/
char prefix_tail[PREFIX_TAIL_LEN];
#endif
} svn_membuffer_cache_t;
/* After an estimated ALLOCATIONS_PER_POOL_CLEAR allocations, we should
* clear the svn_membuffer_cache_t.pool to keep memory consumption in check.
*/
#define ALLOCATIONS_PER_POOL_CLEAR 10
/* Basically calculate a hash value for KEY of length KEY_LEN, combine it
* with the CACHE->PREFIX and write the result in CACHE->COMBINED_KEY.
*/
static void
combine_key(svn_membuffer_cache_t *cache,
const void *key,
apr_ssize_t key_len)
{
if (key_len == APR_HASH_KEY_STRING)
key_len = strlen((const char *) key);
if (key_len < 16)
{
apr_uint32_t data[4] = { 0 };
memcpy(data, key, key_len);
svn__pseudo_md5_15((apr_uint32_t *)cache->combined_key, data);
}
else if (key_len < 32)
{
apr_uint32_t data[8] = { 0 };
memcpy(data, key, key_len);
svn__pseudo_md5_31((apr_uint32_t *)cache->combined_key, data);
}
else if (key_len < 64)
{
apr_uint32_t data[16] = { 0 };
memcpy(data, key, key_len);
svn__pseudo_md5_63((apr_uint32_t *)cache->combined_key, data);
}
else
{
apr_md5((unsigned char*)cache->combined_key, key, key_len);
}
cache->combined_key[0] ^= cache->prefix[0];
cache->combined_key[1] ^= cache->prefix[1];
}
/* Implement svn_cache__vtable_t.get (not thread-safe)
*/
static svn_error_t *
svn_membuffer_cache_get(void **value_p,
svn_boolean_t *found,
void *cache_void,
const void *key,
apr_pool_t *result_pool)
{
svn_membuffer_cache_t *cache = cache_void;
DEBUG_CACHE_MEMBUFFER_INIT_TAG
/* special case */
if (key == NULL)
{
*value_p = NULL;
*found = FALSE;
return SVN_NO_ERROR;
}
/* construct the full, i.e. globally unique, key by adding
* this cache instances' prefix
*/
combine_key(cache, key, cache->key_len);
/* Look the item up. */
SVN_ERR(membuffer_cache_get(cache->membuffer,
cache->combined_key,
value_p,
cache->deserializer,
DEBUG_CACHE_MEMBUFFER_TAG
result_pool));
/* return result */
*found = *value_p != NULL;
return SVN_NO_ERROR;
}
/* Implement svn_cache__vtable_t.set (not thread-safe)
*/
static svn_error_t *
svn_membuffer_cache_set(void *cache_void,
const void *key,
void *value,
apr_pool_t *scratch_pool)
{
svn_membuffer_cache_t *cache = cache_void;
DEBUG_CACHE_MEMBUFFER_INIT_TAG
/* special case */
if (key == NULL)
return SVN_NO_ERROR;
/* we do some allocations below, so increase the allocation counter
* by a slightly larger amount. Free allocated memory every now and then.
*/
cache->alloc_counter += 3;
if (cache->alloc_counter > ALLOCATIONS_PER_POOL_CLEAR)
{
svn_pool_clear(cache->pool);
cache->alloc_counter = 0;
}
/* construct the full, i.e. globally unique, key by adding
* this cache instances' prefix
*/
combine_key(cache, key, cache->key_len);
/* (probably) add the item to the cache. But there is no real guarantee
* that the item will actually be cached afterwards.
*/
return membuffer_cache_set(cache->membuffer,
cache->combined_key,
value,
cache->serializer,
DEBUG_CACHE_MEMBUFFER_TAG
cache->pool);
}
/* Implement svn_cache__vtable_t.iter as "not implemented"
*/
static svn_error_t *
svn_membuffer_cache_iter(svn_boolean_t *completed,
void *cache_void,
svn_iter_apr_hash_cb_t user_cb,
void *user_baton,
apr_pool_t *scratch_pool)
{
return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Can't iterate a membuffer-based cache"));
}
/* Implement svn_cache__vtable_t.get_partial (not thread-safe)
*/
static svn_error_t *
svn_membuffer_cache_get_partial(void **value_p,
svn_boolean_t *found,
void *cache_void,
const void *key,
svn_cache__partial_getter_func_t func,
void *baton,
apr_pool_t *result_pool)
{
svn_membuffer_cache_t *cache = cache_void;
DEBUG_CACHE_MEMBUFFER_INIT_TAG
if (key == NULL)
{
*value_p = NULL;
*found = FALSE;
return SVN_NO_ERROR;
}
combine_key(cache, key, cache->key_len);
SVN_ERR(membuffer_cache_get_partial(cache->membuffer,
cache->combined_key,
value_p,
found,
func,
baton,
DEBUG_CACHE_MEMBUFFER_TAG
result_pool));
return SVN_NO_ERROR;
}
/* Implement svn_cache__vtable_t.set_partial (not thread-safe)
*/
static svn_error_t *
svn_membuffer_cache_set_partial(void *cache_void,
const void *key,
svn_cache__partial_setter_func_t func,
void *baton,
apr_pool_t *scratch_pool)
{
svn_membuffer_cache_t *cache = cache_void;
DEBUG_CACHE_MEMBUFFER_INIT_TAG
if (key != NULL)
{
combine_key(cache, key, cache->key_len);
SVN_ERR(membuffer_cache_set_partial(cache->membuffer,
cache->combined_key,
func,
baton,
DEBUG_CACHE_MEMBUFFER_TAG
scratch_pool));
}
return SVN_NO_ERROR;
}
/* Implement svn_cache__vtable_t.is_cachable
* (thread-safe even without mutex)
*/
static svn_boolean_t
svn_membuffer_cache_is_cachable(void *cache_void, apr_size_t size)
{
/* Don't allow extremely large element sizes. Otherwise, the cache
* might by thrashed by a few extremely large entries. And the size
* must be small enough to be stored in a 32 bit value.
*/
svn_membuffer_cache_t *cache = cache_void;
return size <= cache->membuffer->max_entry_size;
}
/* Add statistics of SEGMENT to INFO.
*/
static svn_error_t *
svn_membuffer_get_segment_info(svn_membuffer_t *segment,
svn_cache__info_t *info)
{
info->data_size += segment->data_size;
info->used_size += segment->data_used;
info->total_size += segment->data_size +
segment->group_count * GROUP_SIZE * sizeof(entry_t);
info->used_entries += segment->used_entries;
info->total_entries += segment->group_count * GROUP_SIZE;
return SVN_NO_ERROR;
}
/* Implement svn_cache__vtable_t.get_info
* (thread-safe even without mutex)
*/
static svn_error_t *
svn_membuffer_cache_get_info(void *cache_void,
svn_cache__info_t *info,
svn_boolean_t reset,
apr_pool_t *result_pool)
{
svn_membuffer_cache_t *cache = cache_void;
apr_uint32_t i;
/* cache front-end specific data */
info->id = apr_pstrdup(result_pool, cache->full_prefix);
/* collect info from shared cache back-end */
info->data_size = 0;
info->used_size = 0;
info->total_size = 0;
info->used_entries = 0;
info->total_entries = 0;
for (i = 0; i < cache->membuffer->segment_count; ++i)
{
svn_membuffer_t *segment = cache->membuffer + i;
WITH_READ_LOCK(segment,
svn_membuffer_get_segment_info(segment, info));
}
return SVN_NO_ERROR;
}
/* the v-table for membuffer-based caches (single-threaded access)
*/
static svn_cache__vtable_t membuffer_cache_vtable = {
svn_membuffer_cache_get,
svn_membuffer_cache_set,
svn_membuffer_cache_iter,
svn_membuffer_cache_is_cachable,
svn_membuffer_cache_get_partial,
svn_membuffer_cache_set_partial,
svn_membuffer_cache_get_info
};
/* Implement svn_cache__vtable_t.get and serialize all cache access.
*/
static svn_error_t *
svn_membuffer_cache_get_synced(void **value_p,
svn_boolean_t *found,
void *cache_void,
const void *key,
apr_pool_t *result_pool)
{
svn_membuffer_cache_t *cache = cache_void;
SVN_MUTEX__WITH_LOCK(cache->mutex,
svn_membuffer_cache_get(value_p,
found,
cache_void,
key,
result_pool));
return SVN_NO_ERROR;
}
/* Implement svn_cache__vtable_t.set and serialize all cache access.
*/
static svn_error_t *
svn_membuffer_cache_set_synced(void *cache_void,
const void *key,
void *value,
apr_pool_t *scratch_pool)
{
svn_membuffer_cache_t *cache = cache_void;
SVN_MUTEX__WITH_LOCK(cache->mutex,
svn_membuffer_cache_set(cache_void,
key,
value,
scratch_pool));
return SVN_NO_ERROR;
}
/* Implement svn_cache__vtable_t.get_partial and serialize all cache access.
*/
static svn_error_t *
svn_membuffer_cache_get_partial_synced(void **value_p,
svn_boolean_t *found,
void *cache_void,
const void *key,
svn_cache__partial_getter_func_t func,
void *baton,
apr_pool_t *result_pool)
{
svn_membuffer_cache_t *cache = cache_void;
SVN_MUTEX__WITH_LOCK(cache->mutex,
svn_membuffer_cache_get_partial(value_p,
found,
cache_void,
key,
func,
baton,
result_pool));
return SVN_NO_ERROR;
}
/* Implement svn_cache__vtable_t.set_partial and serialize all cache access.
*/
static svn_error_t *
svn_membuffer_cache_set_partial_synced(void *cache_void,
const void *key,
svn_cache__partial_setter_func_t func,
void *baton,
apr_pool_t *scratch_pool)
{
svn_membuffer_cache_t *cache = cache_void;
SVN_MUTEX__WITH_LOCK(cache->mutex,
svn_membuffer_cache_set_partial(cache_void,
key,
func,
baton,
scratch_pool));
return SVN_NO_ERROR;
}
/* the v-table for membuffer-based caches with multi-threading support)
*/
static svn_cache__vtable_t membuffer_cache_synced_vtable = {
svn_membuffer_cache_get_synced,
svn_membuffer_cache_set_synced,
svn_membuffer_cache_iter, /* no sync required */
svn_membuffer_cache_is_cachable, /* no sync required */
svn_membuffer_cache_get_partial_synced,
svn_membuffer_cache_set_partial_synced,
svn_membuffer_cache_get_info /* no sync required */
};
/* standard serialization function for svn_stringbuf_t items.
* Implements svn_cache__serialize_func_t.
*/
static svn_error_t *
serialize_svn_stringbuf(void **buffer,
apr_size_t *buffer_size,
void *item,
apr_pool_t *result_pool)
{
svn_stringbuf_t *value_str = item;
*buffer = value_str->data;
*buffer_size = value_str->len + 1;
return SVN_NO_ERROR;
}
/* standard de-serialization function for svn_stringbuf_t items.
* Implements svn_cache__deserialize_func_t.
*/
static svn_error_t *
deserialize_svn_stringbuf(void **item,
void *buffer,
apr_size_t buffer_size,
apr_pool_t *result_pool)
{
svn_stringbuf_t *value_str = apr_palloc(result_pool, sizeof(svn_stringbuf_t));
value_str->pool = result_pool;
value_str->blocksize = buffer_size;
value_str->data = buffer;
value_str->len = buffer_size-1;
*item = value_str;
return SVN_NO_ERROR;
}
/* Construct a svn_cache__t object on top of a shared memcache.
*/
svn_error_t *
svn_cache__create_membuffer_cache(svn_cache__t **cache_p,
svn_membuffer_t *membuffer,
svn_cache__serialize_func_t serializer,
svn_cache__deserialize_func_t deserializer,
apr_ssize_t klen,
const char *prefix,
svn_boolean_t thread_safe,
apr_pool_t *pool)
{
svn_checksum_t *checksum;
/* allocate the cache header structures
*/
svn_cache__t *wrapper = apr_pcalloc(pool, sizeof(*wrapper));
svn_membuffer_cache_t *cache = apr_palloc(pool, sizeof(*cache));
/* initialize our internal cache header
*/
cache->membuffer = membuffer;
cache->serializer = serializer
? serializer
: serialize_svn_stringbuf;
cache->deserializer = deserializer
? deserializer
: deserialize_svn_stringbuf;
cache->full_prefix = apr_pstrdup(pool, prefix);
cache->key_len = klen;
cache->pool = svn_pool_create(pool);
cache->alloc_counter = 0;
SVN_ERR(svn_mutex__init(&cache->mutex, thread_safe, pool));
/* for performance reasons, we don't actually store the full prefix but a
* hash value of it
*/
SVN_ERR(svn_checksum(&checksum,
svn_checksum_md5,
prefix,
strlen(prefix),
pool));
memcpy(cache->prefix, checksum->digest, sizeof(cache->prefix));
#ifdef SVN_DEBUG_CACHE_MEMBUFFER
/* Initialize cache debugging support.
*/
get_prefix_tail(prefix, cache->prefix_tail);
#endif
/* initialize the generic cache wrapper
*/
wrapper->vtable = thread_safe ? &membuffer_cache_synced_vtable
: &membuffer_cache_vtable;
wrapper->cache_internal = cache;
wrapper->error_handler = 0;
wrapper->error_baton = 0;
*cache_p = wrapper;
return SVN_NO_ERROR;
}
Index: vendor/subversion/dist/subversion/libsvn_subr/config.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_subr/config.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_subr/config.c (revision 286501)
@@ -1,1208 +1,1209 @@
/*
* config.c : reading configuration information
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#define APR_WANT_STRFUNC
#define APR_WANT_MEMFUNC
#include <apr_want.h>
#include <apr_general.h>
#include <apr_lib.h>
#include "svn_hash.h"
#include "svn_error.h"
#include "svn_pools.h"
#include "config_impl.h"
#include "svn_private_config.h"
#include "private/svn_dep_compat.h"
/* Section table entries. */
typedef struct cfg_section_t cfg_section_t;
struct cfg_section_t
{
/* The section name. */
const char *name;
/* Table of cfg_option_t's. */
apr_hash_t *options;
};
/* Option table entries. */
typedef struct cfg_option_t cfg_option_t;
struct cfg_option_t
{
/* The option name. */
const char *name;
/* The option name, converted into a hash key. */
const char *hash_key;
/* The unexpanded option value. */
const char *value;
/* The expanded option value. */
const char *x_value;
/* Expansion flag. If this is TRUE, this value has already been expanded.
In this case, if x_value is NULL, no expansions were necessary,
and value should be used directly. */
svn_boolean_t expanded;
};
svn_error_t *
svn_config_create2(svn_config_t **cfgp,
svn_boolean_t section_names_case_sensitive,
svn_boolean_t option_names_case_sensitive,
apr_pool_t *result_pool)
{
svn_config_t *cfg = apr_palloc(result_pool, sizeof(*cfg));
cfg->sections = apr_hash_make(result_pool);
cfg->pool = result_pool;
cfg->x_pool = svn_pool_create(result_pool);
cfg->x_values = FALSE;
cfg->tmp_key = svn_stringbuf_create_empty(result_pool);
cfg->tmp_value = svn_stringbuf_create_empty(result_pool);
cfg->section_names_case_sensitive = section_names_case_sensitive;
cfg->option_names_case_sensitive = option_names_case_sensitive;
*cfgp = cfg;
return SVN_NO_ERROR;
}
svn_error_t *
svn_config_read3(svn_config_t **cfgp, const char *file,
svn_boolean_t must_exist,
svn_boolean_t section_names_case_sensitive,
svn_boolean_t option_names_case_sensitive,
apr_pool_t *result_pool)
{
svn_config_t *cfg;
svn_error_t *err;
SVN_ERR(svn_config_create2(&cfg,
section_names_case_sensitive,
option_names_case_sensitive,
result_pool));
/* Yes, this is platform-specific code in Subversion, but there's no
practical way to migrate it into APR, as it's simultaneously
Subversion-specific and Windows-specific. Even if we eventually
want to have APR offer a generic config-reading interface, it
makes sense to test it here first and migrate it later. */
#ifdef WIN32
if (0 == strncmp(file, SVN_REGISTRY_PREFIX, SVN_REGISTRY_PREFIX_LEN))
err = svn_config__parse_registry(cfg, file + SVN_REGISTRY_PREFIX_LEN,
must_exist, result_pool);
else
#endif /* WIN32 */
err = svn_config__parse_file(cfg, file, must_exist, result_pool);
if (err != SVN_NO_ERROR)
return err;
else
*cfgp = cfg;
return SVN_NO_ERROR;
}
svn_error_t *
svn_config_parse(svn_config_t **cfgp, svn_stream_t *stream,
svn_boolean_t section_names_case_sensitive,
svn_boolean_t option_names_case_sensitive,
apr_pool_t *result_pool)
{
svn_config_t *cfg;
svn_error_t *err;
apr_pool_t *scratch_pool = svn_pool_create(result_pool);
err = svn_config_create2(&cfg,
section_names_case_sensitive,
option_names_case_sensitive,
result_pool);
if (err == SVN_NO_ERROR)
err = svn_config__parse_stream(cfg, stream, result_pool, scratch_pool);
if (err == SVN_NO_ERROR)
*cfgp = cfg;
svn_pool_destroy(scratch_pool);
return err;
}
/* Read various configuration sources into *CFGP, in this order, with
* later reads overriding the results of earlier ones:
*
* 1. SYS_REGISTRY_PATH (only on Win32, but ignored if NULL)
*
* 2. SYS_FILE_PATH (everywhere, but ignored if NULL)
*
* 3. USR_REGISTRY_PATH (only on Win32, but ignored if NULL)
*
* 4. USR_FILE_PATH (everywhere, but ignored if NULL)
*
* Allocate *CFGP in POOL. Even if no configurations are read,
* allocate an empty *CFGP.
*/
static svn_error_t *
read_all(svn_config_t **cfgp,
const char *sys_registry_path,
const char *usr_registry_path,
const char *sys_file_path,
const char *usr_file_path,
apr_pool_t *pool)
{
svn_boolean_t red_config = FALSE; /* "red" is the past tense of "read" */
/*** Read system-wide configurations first... ***/
#ifdef WIN32
if (sys_registry_path)
{
SVN_ERR(svn_config_read2(cfgp, sys_registry_path, FALSE, FALSE, pool));
red_config = TRUE;
}
#endif /* WIN32 */
if (sys_file_path)
{
if (red_config)
SVN_ERR(svn_config_merge(*cfgp, sys_file_path, FALSE));
else
{
SVN_ERR(svn_config_read3(cfgp, sys_file_path,
FALSE, FALSE, FALSE, pool));
red_config = TRUE;
}
}
/*** ...followed by per-user configurations. ***/
#ifdef WIN32
if (usr_registry_path)
{
if (red_config)
SVN_ERR(svn_config_merge(*cfgp, usr_registry_path, FALSE));
else
{
SVN_ERR(svn_config_read2(cfgp, usr_registry_path,
FALSE, FALSE, pool));
red_config = TRUE;
}
}
#endif /* WIN32 */
if (usr_file_path)
{
if (red_config)
SVN_ERR(svn_config_merge(*cfgp, usr_file_path, FALSE));
else
{
SVN_ERR(svn_config_read3(cfgp, usr_file_path,
FALSE, FALSE, FALSE, pool));
red_config = TRUE;
}
}
if (! red_config)
SVN_ERR(svn_config_create2(cfgp, FALSE, FALSE, pool));
return SVN_NO_ERROR;
}
/* CONFIG_DIR provides an override for the default behavior of reading
the default set of overlay files described by read_all()'s doc
string. */
static svn_error_t *
get_category_config(svn_config_t **cfg,
const char *config_dir,
const char *category,
apr_pool_t *pool)
{
const char *usr_reg_path = NULL, *sys_reg_path = NULL;
const char *usr_cfg_path, *sys_cfg_path;
svn_error_t *err = NULL;
*cfg = NULL;
if (! config_dir)
{
#ifdef WIN32
sys_reg_path = apr_pstrcat(pool, SVN_REGISTRY_SYS_CONFIG_PATH,
category, NULL);
usr_reg_path = apr_pstrcat(pool, SVN_REGISTRY_USR_CONFIG_PATH,
category, NULL);
#endif /* WIN32 */
err = svn_config__sys_config_path(&sys_cfg_path, category, pool);
if ((err) && (err->apr_err == SVN_ERR_BAD_FILENAME))
{
sys_cfg_path = NULL;
svn_error_clear(err);
}
else if (err)
return err;
}
else
sys_cfg_path = NULL;
SVN_ERR(svn_config_get_user_config_path(&usr_cfg_path, config_dir, category,
pool));
return read_all(cfg, sys_reg_path, usr_reg_path,
sys_cfg_path, usr_cfg_path, pool);
}
svn_error_t *
svn_config_get_config(apr_hash_t **cfg_hash,
const char *config_dir,
apr_pool_t *pool)
{
svn_config_t *cfg;
*cfg_hash = apr_hash_make(pool);
#define CATLEN (sizeof(SVN_CONFIG_CATEGORY_SERVERS) - 1)
SVN_ERR(get_category_config(&cfg, config_dir, SVN_CONFIG_CATEGORY_SERVERS,
pool));
if (cfg)
apr_hash_set(*cfg_hash, SVN_CONFIG_CATEGORY_SERVERS, CATLEN, cfg);
#undef CATLEN
#define CATLEN (sizeof(SVN_CONFIG_CATEGORY_CONFIG) - 1)
SVN_ERR(get_category_config(&cfg, config_dir, SVN_CONFIG_CATEGORY_CONFIG,
pool));
if (cfg)
apr_hash_set(*cfg_hash, SVN_CONFIG_CATEGORY_CONFIG, CATLEN, cfg);
#undef CATLEN
return SVN_NO_ERROR;
}
/* Iterate through CFG, passing BATON to CALLBACK for every (SECTION, OPTION)
pair. Stop if CALLBACK returns TRUE. Allocate from POOL. */
static void
for_each_option(svn_config_t *cfg, void *baton, apr_pool_t *pool,
svn_boolean_t callback(void *same_baton,
cfg_section_t *section,
cfg_option_t *option))
{
apr_hash_index_t *sec_ndx;
for (sec_ndx = apr_hash_first(pool, cfg->sections);
sec_ndx != NULL;
sec_ndx = apr_hash_next(sec_ndx))
{
void *sec_ptr;
cfg_section_t *sec;
apr_hash_index_t *opt_ndx;
apr_hash_this(sec_ndx, NULL, NULL, &sec_ptr);
sec = sec_ptr;
for (opt_ndx = apr_hash_first(pool, sec->options);
opt_ndx != NULL;
opt_ndx = apr_hash_next(opt_ndx))
{
void *opt_ptr;
cfg_option_t *opt;
apr_hash_this(opt_ndx, NULL, NULL, &opt_ptr);
opt = opt_ptr;
if (callback(baton, sec, opt))
return;
}
}
}
static svn_boolean_t
merge_callback(void *baton, cfg_section_t *section, cfg_option_t *option)
{
svn_config_set(baton, section->name, option->name, option->value);
return FALSE;
}
svn_error_t *
svn_config_merge(svn_config_t *cfg, const char *file,
svn_boolean_t must_exist)
{
/* The original config hash shouldn't change if there's an error
while reading the confguration, so read into a temporary table.
### We could use a tmp subpool for this, since merge_cfg is going
to be tossed afterwards. Premature optimization, though? */
svn_config_t *merge_cfg;
SVN_ERR(svn_config_read3(&merge_cfg, file, must_exist,
cfg->section_names_case_sensitive,
cfg->option_names_case_sensitive,
cfg->pool));
/* Now copy the new options into the original table. */
for_each_option(merge_cfg, cfg, merge_cfg->pool, merge_callback);
return SVN_NO_ERROR;
}
/* Remove variable expansions from CFG. Walk through the options tree,
killing all expanded values, then clear the expanded value pool. */
static svn_boolean_t
rmex_callback(void *baton, cfg_section_t *section, cfg_option_t *option)
{
/* Only clear the `expanded' flag if the value actually contains
variable expansions. */
if (option->expanded && option->x_value != NULL)
{
option->x_value = NULL;
option->expanded = FALSE;
}
return FALSE;
}
static void
remove_expansions(svn_config_t *cfg)
{
if (!cfg->x_values)
return;
for_each_option(cfg, NULL, cfg->x_pool, rmex_callback);
svn_pool_clear(cfg->x_pool);
cfg->x_values = FALSE;
}
/* Canonicalize a string for hashing. Modifies KEY in place. */
static APR_INLINE char *
make_hash_key(char *key)
{
register char *p;
for (p = key; *p != 0; ++p)
*p = (char)apr_tolower(*p);
return key;
}
/* Return a pointer to an option in CFG, or NULL if it doesn't exist.
if SECTIONP is non-null, return a pointer to the option's section.
OPTION may be NULL. */
static cfg_option_t *
find_option(svn_config_t *cfg, const char *section, const char *option,
cfg_section_t **sectionp)
{
void *sec_ptr;
/* Canonicalize the hash key */
svn_stringbuf_set(cfg->tmp_key, section);
if (! cfg->section_names_case_sensitive)
make_hash_key(cfg->tmp_key->data);
sec_ptr = apr_hash_get(cfg->sections, cfg->tmp_key->data,
cfg->tmp_key->len);
if (sectionp != NULL)
*sectionp = sec_ptr;
if (sec_ptr != NULL && option != NULL)
{
cfg_section_t *sec = sec_ptr;
cfg_option_t *opt;
/* Canonicalize the option key */
svn_stringbuf_set(cfg->tmp_key, option);
if (! cfg->option_names_case_sensitive)
make_hash_key(cfg->tmp_key->data);
opt = apr_hash_get(sec->options, cfg->tmp_key->data,
cfg->tmp_key->len);
/* NOTE: ConfigParser's sections are case sensitive. */
if (opt == NULL
&& apr_strnatcasecmp(section, SVN_CONFIG__DEFAULT_SECTION) != 0)
/* Options which aren't found in the requested section are
also sought after in the default section. */
opt = find_option(cfg, SVN_CONFIG__DEFAULT_SECTION, option, &sec);
return opt;
}
return NULL;
}
/* Has a bi-directional dependency with make_string_from_option(). */
static void
expand_option_value(svn_config_t *cfg, cfg_section_t *section,
const char *opt_value, const char **opt_x_valuep,
apr_pool_t *x_pool);
/* Set *VALUEP according to the OPT's value. A value for X_POOL must
only ever be passed into this function by expand_option_value(). */
static void
make_string_from_option(const char **valuep, svn_config_t *cfg,
cfg_section_t *section, cfg_option_t *opt,
apr_pool_t* x_pool)
{
/* Expand the option value if necessary. */
if (!opt->expanded)
{
/* before attempting to expand an option, check for the placeholder.
* If none is there, there is no point in calling expand_option_value.
*/
if (opt->value && strchr(opt->value, '%'))
{
apr_pool_t *tmp_pool = (x_pool ? x_pool : svn_pool_create(cfg->x_pool));
expand_option_value(cfg, section, opt->value, &opt->x_value, tmp_pool);
opt->expanded = TRUE;
- if (!x_pool)
+ if (x_pool != cfg->x_pool)
{
/* Grab the fully expanded value from tmp_pool before its
disappearing act. */
if (opt->x_value)
opt->x_value = apr_pstrmemdup(cfg->x_pool, opt->x_value,
strlen(opt->x_value));
- svn_pool_destroy(tmp_pool);
+ if (!x_pool)
+ svn_pool_destroy(tmp_pool);
}
}
else
{
opt->expanded = TRUE;
}
}
if (opt->x_value)
*valuep = opt->x_value;
else
*valuep = opt->value;
}
/* Start of variable-replacement placeholder */
#define FMT_START "%("
#define FMT_START_LEN (sizeof(FMT_START) - 1)
/* End of variable-replacement placeholder */
#define FMT_END ")s"
#define FMT_END_LEN (sizeof(FMT_END) - 1)
/* Expand OPT_VALUE (which may be NULL) in SECTION into *OPT_X_VALUEP.
If no variable replacements are done, set *OPT_X_VALUEP to
NULL. Allocate from X_POOL. */
static void
expand_option_value(svn_config_t *cfg, cfg_section_t *section,
const char *opt_value, const char **opt_x_valuep,
apr_pool_t *x_pool)
{
svn_stringbuf_t *buf = NULL;
const char *parse_from = opt_value;
const char *copy_from = parse_from;
const char *name_start, *name_end;
while (parse_from != NULL
&& *parse_from != '\0'
&& (name_start = strstr(parse_from, FMT_START)) != NULL)
{
name_start += FMT_START_LEN;
if (*name_start == '\0')
/* FMT_START at end of opt_value. */
break;
name_end = strstr(name_start, FMT_END);
if (name_end != NULL)
{
cfg_option_t *x_opt;
apr_size_t len = name_end - name_start;
char *name = apr_pstrmemdup(x_pool, name_start, len);
x_opt = find_option(cfg, section->name, name, NULL);
if (x_opt != NULL)
{
const char *cstring;
/* Pass back the sub-pool originally provided by
make_string_from_option() as an indication of when it
should terminate. */
make_string_from_option(&cstring, cfg, section, x_opt, x_pool);
/* Append the plain text preceding the expansion. */
len = name_start - FMT_START_LEN - copy_from;
if (buf == NULL)
{
buf = svn_stringbuf_ncreate(copy_from, len, x_pool);
cfg->x_values = TRUE;
}
else
svn_stringbuf_appendbytes(buf, copy_from, len);
/* Append the expansion and adjust parse pointers. */
svn_stringbuf_appendcstr(buf, cstring);
parse_from = name_end + FMT_END_LEN;
copy_from = parse_from;
}
else
/* Though ConfigParser considers the failure to resolve
the requested expansion an exception condition, we
consider it to be plain text, and look for the start of
the next one. */
parse_from = name_end + FMT_END_LEN;
}
else
/* Though ConfigParser treats unterminated format specifiers
as an exception condition, we consider them to be plain
text. The fact that there are no more format specifier
endings means we're done parsing. */
parse_from = NULL;
}
if (buf != NULL)
{
/* Copy the remainder of the plain text. */
svn_stringbuf_appendcstr(buf, copy_from);
*opt_x_valuep = buf->data;
}
else
*opt_x_valuep = NULL;
}
static cfg_section_t *
svn_config_addsection(svn_config_t *cfg,
const char *section)
{
cfg_section_t *s;
const char *hash_key;
s = apr_palloc(cfg->pool, sizeof(cfg_section_t));
s->name = apr_pstrdup(cfg->pool, section);
if(cfg->section_names_case_sensitive)
hash_key = s->name;
else
hash_key = make_hash_key(apr_pstrdup(cfg->pool, section));
s->options = apr_hash_make(cfg->pool);
svn_hash_sets(cfg->sections, hash_key, s);
return s;
}
static void
svn_config_create_option(cfg_option_t **opt,
const char *option,
const char *value,
svn_boolean_t option_names_case_sensitive,
apr_pool_t *pool)
{
cfg_option_t *o;
o = apr_palloc(pool, sizeof(cfg_option_t));
o->name = apr_pstrdup(pool, option);
if(option_names_case_sensitive)
o->hash_key = o->name;
else
o->hash_key = make_hash_key(apr_pstrdup(pool, option));
o->value = apr_pstrdup(pool, value);
o->x_value = NULL;
o->expanded = FALSE;
*opt = o;
}
void
svn_config_get(svn_config_t *cfg, const char **valuep,
const char *section, const char *option,
const char *default_value)
{
*valuep = default_value;
if (cfg)
{
cfg_section_t *sec;
cfg_option_t *opt = find_option(cfg, section, option, &sec);
if (opt != NULL)
{
make_string_from_option(valuep, cfg, sec, opt, NULL);
}
else
/* before attempting to expand an option, check for the placeholder.
* If none is there, there is no point in calling expand_option_value.
*/
if (default_value && strchr(default_value, '%'))
{
apr_pool_t *tmp_pool = svn_pool_create(cfg->x_pool);
const char *x_default;
expand_option_value(cfg, sec, default_value, &x_default, tmp_pool);
if (x_default)
{
svn_stringbuf_set(cfg->tmp_value, x_default);
*valuep = cfg->tmp_value->data;
}
svn_pool_destroy(tmp_pool);
}
}
}
void
svn_config_set(svn_config_t *cfg,
const char *section, const char *option,
const char *value)
{
cfg_section_t *sec;
cfg_option_t *opt;
remove_expansions(cfg);
opt = find_option(cfg, section, option, &sec);
if (opt != NULL)
{
/* Replace the option's value. */
opt->value = apr_pstrdup(cfg->pool, value);
opt->expanded = FALSE;
return;
}
/* Create a new option */
svn_config_create_option(&opt, option, value,
cfg->option_names_case_sensitive,
cfg->pool);
if (sec == NULL)
{
/* Even the section doesn't exist. Create it. */
sec = svn_config_addsection(cfg, section);
}
svn_hash_sets(sec->options, opt->hash_key, opt);
}
/* Set *BOOLP to true or false depending (case-insensitively) on INPUT.
If INPUT is null, set *BOOLP to DEFAULT_VALUE.
INPUT is a string indicating truth or falsehood in any of the usual
ways: "true"/"yes"/"on"/etc, "false"/"no"/"off"/etc.
If INPUT is neither NULL nor a recognized string, return an error
with code SVN_ERR_BAD_CONFIG_VALUE; use SECTION and OPTION in
constructing the error string. */
static svn_error_t *
get_bool(svn_boolean_t *boolp, const char *input, svn_boolean_t default_value,
const char *section, const char *option)
{
svn_tristate_t value = svn_tristate__from_word(input);
if (value == svn_tristate_true)
*boolp = TRUE;
else if (value == svn_tristate_false)
*boolp = FALSE;
else if (input == NULL) /* no value provided */
*boolp = default_value;
else if (section) /* unrecognized value */
return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
_("Config error: invalid boolean "
"value '%s' for '[%s] %s'"),
input, section, option);
else
return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
_("Config error: invalid boolean "
"value '%s' for '%s'"),
input, option);
return SVN_NO_ERROR;
}
svn_error_t *
svn_config_get_bool(svn_config_t *cfg, svn_boolean_t *valuep,
const char *section, const char *option,
svn_boolean_t default_value)
{
const char *tmp_value;
svn_config_get(cfg, &tmp_value, section, option, NULL);
return get_bool(valuep, tmp_value, default_value, section, option);
}
void
svn_config_set_bool(svn_config_t *cfg,
const char *section, const char *option,
svn_boolean_t value)
{
svn_config_set(cfg, section, option,
(value ? SVN_CONFIG_TRUE : SVN_CONFIG_FALSE));
}
svn_error_t *
svn_config_get_int64(svn_config_t *cfg,
apr_int64_t *valuep,
const char *section,
const char *option,
apr_int64_t default_value)
{
const char *tmp_value;
svn_config_get(cfg, &tmp_value, section, option, NULL);
if (tmp_value)
return svn_cstring_strtoi64(valuep, tmp_value,
APR_INT64_MIN, APR_INT64_MAX, 10);
*valuep = default_value;
return SVN_NO_ERROR;
}
void
svn_config_set_int64(svn_config_t *cfg,
const char *section,
const char *option,
apr_int64_t value)
{
svn_config_set(cfg, section, option,
apr_psprintf(cfg->pool, "%" APR_INT64_T_FMT, value));
}
svn_error_t *
svn_config_get_yes_no_ask(svn_config_t *cfg, const char **valuep,
const char *section, const char *option,
const char* default_value)
{
const char *tmp_value;
svn_config_get(cfg, &tmp_value, section, option, NULL);
if (! tmp_value)
tmp_value = default_value;
if (tmp_value && (0 == svn_cstring_casecmp(tmp_value, SVN_CONFIG_ASK)))
{
*valuep = SVN_CONFIG_ASK;
}
else
{
svn_boolean_t bool_val;
/* We already incorporated default_value into tmp_value if
necessary, so the FALSE below will be ignored unless the
caller is doing something it shouldn't be doing. */
SVN_ERR(get_bool(&bool_val, tmp_value, FALSE, section, option));
*valuep = bool_val ? SVN_CONFIG_TRUE : SVN_CONFIG_FALSE;
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_config_get_tristate(svn_config_t *cfg, svn_tristate_t *valuep,
const char *section, const char *option,
const char *unknown_value,
svn_tristate_t default_value)
{
const char *tmp_value;
svn_config_get(cfg, &tmp_value, section, option, NULL);
if (! tmp_value)
{
*valuep = default_value;
}
else if (0 == svn_cstring_casecmp(tmp_value, unknown_value))
{
*valuep = svn_tristate_unknown;
}
else
{
svn_boolean_t bool_val;
/* We already incorporated default_value into tmp_value if
necessary, so the FALSE below will be ignored unless the
caller is doing something it shouldn't be doing. */
SVN_ERR(get_bool(&bool_val, tmp_value, FALSE, section, option));
*valuep = bool_val ? svn_tristate_true : svn_tristate_false;
}
return SVN_NO_ERROR;
}
int
svn_config_enumerate_sections(svn_config_t *cfg,
svn_config_section_enumerator_t callback,
void *baton)
{
apr_hash_index_t *sec_ndx;
int count = 0;
apr_pool_t *subpool = svn_pool_create(cfg->x_pool);
for (sec_ndx = apr_hash_first(subpool, cfg->sections);
sec_ndx != NULL;
sec_ndx = apr_hash_next(sec_ndx))
{
void *sec_ptr;
cfg_section_t *sec;
apr_hash_this(sec_ndx, NULL, NULL, &sec_ptr);
sec = sec_ptr;
++count;
if (!callback(sec->name, baton))
break;
}
svn_pool_destroy(subpool);
return count;
}
int
svn_config_enumerate_sections2(svn_config_t *cfg,
svn_config_section_enumerator2_t callback,
void *baton, apr_pool_t *pool)
{
apr_hash_index_t *sec_ndx;
apr_pool_t *iteration_pool;
int count = 0;
iteration_pool = svn_pool_create(pool);
for (sec_ndx = apr_hash_first(pool, cfg->sections);
sec_ndx != NULL;
sec_ndx = apr_hash_next(sec_ndx))
{
void *sec_ptr;
cfg_section_t *sec;
apr_hash_this(sec_ndx, NULL, NULL, &sec_ptr);
sec = sec_ptr;
++count;
svn_pool_clear(iteration_pool);
if (!callback(sec->name, baton, iteration_pool))
break;
}
svn_pool_destroy(iteration_pool);
return count;
}
int
svn_config_enumerate(svn_config_t *cfg, const char *section,
svn_config_enumerator_t callback, void *baton)
{
cfg_section_t *sec;
apr_hash_index_t *opt_ndx;
int count;
apr_pool_t *subpool;
find_option(cfg, section, NULL, &sec);
if (sec == NULL)
return 0;
subpool = svn_pool_create(cfg->x_pool);
count = 0;
for (opt_ndx = apr_hash_first(subpool, sec->options);
opt_ndx != NULL;
opt_ndx = apr_hash_next(opt_ndx))
{
void *opt_ptr;
cfg_option_t *opt;
const char *temp_value;
apr_hash_this(opt_ndx, NULL, NULL, &opt_ptr);
opt = opt_ptr;
++count;
make_string_from_option(&temp_value, cfg, sec, opt, NULL);
if (!callback(opt->name, temp_value, baton))
break;
}
svn_pool_destroy(subpool);
return count;
}
int
svn_config_enumerate2(svn_config_t *cfg, const char *section,
svn_config_enumerator2_t callback, void *baton,
apr_pool_t *pool)
{
cfg_section_t *sec;
apr_hash_index_t *opt_ndx;
apr_pool_t *iteration_pool;
int count;
find_option(cfg, section, NULL, &sec);
if (sec == NULL)
return 0;
iteration_pool = svn_pool_create(pool);
count = 0;
for (opt_ndx = apr_hash_first(pool, sec->options);
opt_ndx != NULL;
opt_ndx = apr_hash_next(opt_ndx))
{
void *opt_ptr;
cfg_option_t *opt;
const char *temp_value;
apr_hash_this(opt_ndx, NULL, NULL, &opt_ptr);
opt = opt_ptr;
++count;
make_string_from_option(&temp_value, cfg, sec, opt, NULL);
svn_pool_clear(iteration_pool);
if (!callback(opt->name, temp_value, baton, iteration_pool))
break;
}
svn_pool_destroy(iteration_pool);
return count;
}
/* Baton for search_groups() */
struct search_groups_baton
{
const char *key; /* Provided by caller of svn_config_find_group */
const char *match; /* Filled in by search_groups */
apr_pool_t *pool;
};
/* This is an `svn_config_enumerator_t' function, and BATON is a
* `struct search_groups_baton *'.
*/
static svn_boolean_t search_groups(const char *name,
const char *value,
void *baton,
apr_pool_t *pool)
{
struct search_groups_baton *b = baton;
apr_array_header_t *list;
list = svn_cstring_split(value, ",", TRUE, pool);
if (svn_cstring_match_glob_list(b->key, list))
{
/* Fill in the match and return false, to stop enumerating. */
b->match = apr_pstrdup(b->pool, name);
return FALSE;
}
else
return TRUE;
}
const char *svn_config_find_group(svn_config_t *cfg, const char *key,
const char *master_section,
apr_pool_t *pool)
{
struct search_groups_baton gb;
gb.key = key;
gb.match = NULL;
gb.pool = pool;
(void) svn_config_enumerate2(cfg, master_section, search_groups, &gb, pool);
return gb.match;
}
const char*
svn_config_get_server_setting(svn_config_t *cfg,
const char* server_group,
const char* option_name,
const char* default_value)
{
const char *retval;
svn_config_get(cfg, &retval, SVN_CONFIG_SECTION_GLOBAL,
option_name, default_value);
if (server_group)
{
svn_config_get(cfg, &retval, server_group, option_name, retval);
}
return retval;
}
svn_error_t *
svn_config_dup(svn_config_t **cfgp,
svn_config_t *src,
apr_pool_t *pool)
{
apr_hash_index_t *sectidx;
apr_hash_index_t *optidx;
*cfgp = 0;
SVN_ERR(svn_config_create2(cfgp, FALSE, FALSE, pool));
(*cfgp)->x_values = src->x_values;
(*cfgp)->section_names_case_sensitive = src->section_names_case_sensitive;
(*cfgp)->option_names_case_sensitive = src->option_names_case_sensitive;
for (sectidx = apr_hash_first(pool, src->sections);
sectidx != NULL;
sectidx = apr_hash_next(sectidx))
{
const void *sectkey;
void *sectval;
apr_ssize_t sectkeyLength;
cfg_section_t * srcsect;
cfg_section_t * destsec;
apr_hash_this(sectidx, &sectkey, &sectkeyLength, &sectval);
srcsect = sectval;
destsec = svn_config_addsection(*cfgp, srcsect->name);
for (optidx = apr_hash_first(pool, srcsect->options);
optidx != NULL;
optidx = apr_hash_next(optidx))
{
const void *optkey;
void *optval;
apr_ssize_t optkeyLength;
cfg_option_t *srcopt;
cfg_option_t *destopt;
apr_hash_this(optidx, &optkey, &optkeyLength, &optval);
srcopt = optval;
svn_config_create_option(&destopt, srcopt->name, srcopt->value,
(*cfgp)->option_names_case_sensitive,
pool);
destopt->value = apr_pstrdup(pool, srcopt->value);
destopt->x_value = apr_pstrdup(pool, srcopt->x_value);
destopt->expanded = srcopt->expanded;
apr_hash_set(destsec->options,
apr_pstrdup(pool, (const char*)optkey),
optkeyLength, destopt);
}
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_config_copy_config(apr_hash_t **cfg_hash,
apr_hash_t *src_hash,
apr_pool_t *pool)
{
apr_hash_index_t *cidx;
*cfg_hash = apr_hash_make(pool);
for (cidx = apr_hash_first(pool, src_hash);
cidx != NULL;
cidx = apr_hash_next(cidx))
{
const void *ckey;
void *cval;
apr_ssize_t ckeyLength;
svn_config_t * srcconfig;
svn_config_t * destconfig;
apr_hash_this(cidx, &ckey, &ckeyLength, &cval);
srcconfig = cval;
SVN_ERR(svn_config_dup(&destconfig, srcconfig, pool));
apr_hash_set(*cfg_hash,
apr_pstrdup(pool, (const char*)ckey),
ckeyLength, destconfig);
}
return SVN_NO_ERROR;
}
svn_error_t*
svn_config_get_server_setting_int(svn_config_t *cfg,
const char *server_group,
const char *option_name,
apr_int64_t default_value,
apr_int64_t *result_value,
apr_pool_t *pool)
{
const char* tmp_value;
char *end_pos;
tmp_value = svn_config_get_server_setting(cfg, server_group,
option_name, NULL);
if (tmp_value == NULL)
*result_value = default_value;
else
{
/* read tmp_value as an int now */
*result_value = apr_strtoi64(tmp_value, &end_pos, 0);
if (*end_pos != 0)
{
return svn_error_createf
(SVN_ERR_BAD_CONFIG_VALUE, NULL,
_("Config error: invalid integer value '%s'"),
tmp_value);
}
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_config_get_server_setting_bool(svn_config_t *cfg,
svn_boolean_t *valuep,
const char *server_group,
const char *option_name,
svn_boolean_t default_value)
{
const char* tmp_value;
tmp_value = svn_config_get_server_setting(cfg, server_group,
option_name, NULL);
return get_bool(valuep, tmp_value, default_value,
server_group, option_name);
}
svn_boolean_t
svn_config_has_section(svn_config_t *cfg, const char *section)
{
cfg_section_t *sec;
/* Canonicalize the hash key */
svn_stringbuf_set(cfg->tmp_key, section);
if (! cfg->section_names_case_sensitive)
make_hash_key(cfg->tmp_key->data);
sec = svn_hash_gets(cfg->sections, cfg->tmp_key->data);
return sec != NULL;
}
Index: vendor/subversion/dist/subversion/libsvn_subr/dso.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_subr/dso.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_subr/dso.c (revision 286501)
@@ -1,117 +1,126 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include <apr_thread_mutex.h>
#include <apr_hash.h>
#include "svn_hash.h"
#include "svn_dso.h"
#include "svn_pools.h"
#include "svn_private_config.h"
#include "private/svn_mutex.h"
+#include "private/svn_atomic.h"
/* A mutex to protect our global pool and cache. */
static svn_mutex__t *dso_mutex = NULL;
/* Global pool to allocate DSOs in. */
static apr_pool_t *dso_pool;
/* Global cache for storing DSO objects. */
static apr_hash_t *dso_cache;
/* Just an arbitrary location in memory... */
static int not_there_sentinel;
+static volatile svn_atomic_t atomic_init_status = 0;
+
/* A specific value we store in the dso_cache to indicate that the
library wasn't found. This keeps us from allocating extra memory
from dso_pool when trying to find libraries we already know aren't
there. */
#define NOT_THERE ((void *) &not_there_sentinel)
-svn_error_t *
-svn_dso_initialize2(void)
+static svn_error_t *
+atomic_init_func(void *baton,
+ apr_pool_t *pool)
{
- if (dso_pool)
- return SVN_NO_ERROR;
-
dso_pool = svn_pool_create(NULL);
SVN_ERR(svn_mutex__init(&dso_mutex, TRUE, dso_pool));
dso_cache = apr_hash_make(dso_pool);
return SVN_NO_ERROR;
}
+svn_error_t *
+svn_dso_initialize2(void)
+{
+ SVN_ERR(svn_atomic__init_once(&atomic_init_status, atomic_init_func,
+ NULL, NULL));
+
+ return SVN_NO_ERROR;
+}
+
#if APR_HAS_DSO
static svn_error_t *
svn_dso_load_internal(apr_dso_handle_t **dso, const char *fname)
{
*dso = svn_hash_gets(dso_cache, fname);
/* First check to see if we've been through this before... We do this
to avoid calling apr_dso_load multiple times for a given library,
which would result in wasting small amounts of memory each time. */
if (*dso == NOT_THERE)
{
*dso = NULL;
return SVN_NO_ERROR;
}
/* If we got nothing back from the cache, try and load the library. */
if (! *dso)
{
apr_status_t status = apr_dso_load(dso, fname, dso_pool);
if (status)
{
#ifdef SVN_DEBUG_DSO
char buf[1024];
fprintf(stderr,
"Dynamic loading of '%s' failed with the following error:\n%s\n",
fname,
apr_dso_error(*dso, buf, 1024));
#endif
*dso = NULL;
/* It wasn't found, so set the special "we didn't find it" value. */
svn_hash_sets(dso_cache, apr_pstrdup(dso_pool, fname), NOT_THERE);
return SVN_NO_ERROR;
}
/* Stash the dso so we can use it next time. */
svn_hash_sets(dso_cache, apr_pstrdup(dso_pool, fname), *dso);
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_dso_load(apr_dso_handle_t **dso, const char *fname)
{
- if (! dso_pool)
- SVN_ERR(svn_dso_initialize2());
+ SVN_ERR(svn_dso_initialize2());
SVN_MUTEX__WITH_LOCK(dso_mutex, svn_dso_load_internal(dso, fname));
return SVN_NO_ERROR;
}
#endif /* APR_HAS_DSO */
Index: vendor/subversion/dist/subversion/libsvn_subr/error.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_subr/error.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_subr/error.c (revision 286501)
@@ -1,800 +1,804 @@
/* error.c: common exception handling for Subversion
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include <stdarg.h>
#include <apr_general.h>
#include <apr_pools.h>
#include <apr_strings.h>
#include <zlib.h>
#ifndef SVN_ERR__TRACING
#define SVN_ERR__TRACING
#endif
#include "svn_cmdline.h"
#include "svn_error.h"
#include "svn_pools.h"
#include "svn_utf.h"
#ifdef SVN_DEBUG
/* XXX FIXME: These should be protected by a thread mutex.
svn_error__locate and make_error_internal should cooperate
in locking and unlocking it. */
/* XXX TODO: Define mutex here #if APR_HAS_THREADS */
static const char * volatile error_file = NULL;
static long volatile error_line = -1;
/* file_line for the non-debug case. */
static const char SVN_FILE_LINE_UNDEFINED[] = "svn:<undefined>";
#endif /* SVN_DEBUG */
#include "svn_private_config.h"
#include "private/svn_error_private.h"
/*
* Undefine the helpers for creating errors.
*
* *NOTE*: Any use of these functions in any other function may need
* to call svn_error__locate() because the macro that would otherwise
* do this is being undefined and the filename and line number will
* not be properly set in the static error_file and error_line
* variables.
*/
#undef svn_error_create
#undef svn_error_createf
#undef svn_error_quick_wrap
#undef svn_error_wrap_apr
/* Note: Although this is a "__" function, it was historically in the
* public ABI, so we can never change it or remove its signature, even
* though it is now only used in SVN_DEBUG mode. */
void
svn_error__locate(const char *file, long line)
{
#if defined(SVN_DEBUG)
/* XXX TODO: Lock mutex here */
error_file = file;
error_line = line;
#endif
}
/* Cleanup function for errors. svn_error_clear () removes this so
errors that are properly handled *don't* hit this code. */
#if defined(SVN_DEBUG)
static apr_status_t err_abort(void *data)
{
svn_error_t *err = data; /* For easy viewing in a debugger */
err = err; /* Fake a use for the variable to avoid compiler warnings */
if (!getenv("SVN_DBG_NO_ABORT_ON_ERROR_LEAK"))
abort();
return APR_SUCCESS;
}
#endif
static svn_error_t *
make_error_internal(apr_status_t apr_err,
svn_error_t *child)
{
apr_pool_t *pool;
svn_error_t *new_error;
/* Reuse the child's pool, or create our own. */
if (child)
pool = child->pool;
else
{
if (apr_pool_create(&pool, NULL))
abort();
}
/* Create the new error structure */
new_error = apr_pcalloc(pool, sizeof(*new_error));
/* Fill 'er up. */
new_error->apr_err = apr_err;
new_error->child = child;
new_error->pool = pool;
#if defined(SVN_DEBUG)
new_error->file = error_file;
new_error->line = error_line;
/* XXX TODO: Unlock mutex here */
if (! child)
apr_pool_cleanup_register(pool, new_error,
err_abort,
apr_pool_cleanup_null);
#endif
return new_error;
}
/*** Creating and destroying errors. ***/
svn_error_t *
svn_error_create(apr_status_t apr_err,
svn_error_t *child,
const char *message)
{
svn_error_t *err;
err = make_error_internal(apr_err, child);
if (message)
err->message = apr_pstrdup(err->pool, message);
return err;
}
svn_error_t *
svn_error_createf(apr_status_t apr_err,
svn_error_t *child,
const char *fmt,
...)
{
svn_error_t *err;
va_list ap;
err = make_error_internal(apr_err, child);
va_start(ap, fmt);
err->message = apr_pvsprintf(err->pool, fmt, ap);
va_end(ap);
return err;
}
svn_error_t *
svn_error_wrap_apr(apr_status_t status,
const char *fmt,
...)
{
svn_error_t *err, *utf8_err;
va_list ap;
char errbuf[255];
const char *msg_apr, *msg;
err = make_error_internal(status, NULL);
if (fmt)
{
/* Grab the APR error message. */
apr_strerror(status, errbuf, sizeof(errbuf));
utf8_err = svn_utf_cstring_to_utf8(&msg_apr, errbuf, err->pool);
if (utf8_err)
msg_apr = NULL;
svn_error_clear(utf8_err);
/* Append it to the formatted message. */
va_start(ap, fmt);
msg = apr_pvsprintf(err->pool, fmt, ap);
va_end(ap);
if (msg_apr)
{
err->message = apr_pstrcat(err->pool, msg, ": ", msg_apr, NULL);
}
else
{
err->message = msg;
}
}
return err;
}
svn_error_t *
svn_error_quick_wrap(svn_error_t *child, const char *new_msg)
{
if (child == SVN_NO_ERROR)
return SVN_NO_ERROR;
return svn_error_create(child->apr_err,
child,
new_msg);
}
/* Messages in tracing errors all point to this static string. */
static const char error_tracing_link[] = "traced call";
svn_error_t *
svn_error__trace(const char *file, long line, svn_error_t *err)
{
#ifndef SVN_DEBUG
/* We shouldn't even be here, but whatever. Just return the error as-is. */
return err;
#else
/* Only do the work when an error occurs. */
if (err)
{
svn_error_t *trace;
svn_error__locate(file, line);
trace = make_error_internal(err->apr_err, err);
trace->message = error_tracing_link;
return trace;
}
return SVN_NO_ERROR;
#endif
}
svn_error_t *
svn_error_compose_create(svn_error_t *err1,
svn_error_t *err2)
{
if (err1 && err2)
{
svn_error_compose(err1,
svn_error_quick_wrap(err2,
_("Additional errors:")));
return err1;
}
return err1 ? err1 : err2;
}
void
svn_error_compose(svn_error_t *chain, svn_error_t *new_err)
{
apr_pool_t *pool = chain->pool;
apr_pool_t *oldpool = new_err->pool;
while (chain->child)
chain = chain->child;
#if defined(SVN_DEBUG)
/* Kill existing handler since the end of the chain is going to change */
apr_pool_cleanup_kill(pool, chain, err_abort);
#endif
/* Copy the new error chain into the old chain's pool. */
while (new_err)
{
chain->child = apr_palloc(pool, sizeof(*chain->child));
chain = chain->child;
*chain = *new_err;
if (chain->message)
chain->message = apr_pstrdup(pool, new_err->message);
+ if (chain->file)
+ chain->file = apr_pstrdup(pool, new_err->file);
chain->pool = pool;
#if defined(SVN_DEBUG)
if (! new_err->child)
apr_pool_cleanup_kill(oldpool, new_err, err_abort);
#endif
new_err = new_err->child;
}
#if defined(SVN_DEBUG)
apr_pool_cleanup_register(pool, chain,
err_abort,
apr_pool_cleanup_null);
#endif
/* Destroy the new error chain. */
svn_pool_destroy(oldpool);
}
svn_error_t *
svn_error_root_cause(svn_error_t *err)
{
while (err)
{
if (err->child)
err = err->child;
else
break;
}
return err;
}
svn_error_t *
svn_error_find_cause(svn_error_t *err, apr_status_t apr_err)
{
svn_error_t *child;
for (child = err; child; child = child->child)
if (child->apr_err == apr_err)
return child;
return SVN_NO_ERROR;
}
svn_error_t *
svn_error_dup(svn_error_t *err)
{
apr_pool_t *pool;
svn_error_t *new_err = NULL, *tmp_err = NULL;
if (apr_pool_create(&pool, NULL))
abort();
for (; err; err = err->child)
{
if (! new_err)
{
new_err = apr_palloc(pool, sizeof(*new_err));
tmp_err = new_err;
}
else
{
tmp_err->child = apr_palloc(pool, sizeof(*tmp_err->child));
tmp_err = tmp_err->child;
}
*tmp_err = *err;
tmp_err->pool = pool;
if (tmp_err->message)
tmp_err->message = apr_pstrdup(pool, tmp_err->message);
+ if (tmp_err->file)
+ tmp_err->file = apr_pstrdup(pool, tmp_err->file);
}
#if defined(SVN_DEBUG)
apr_pool_cleanup_register(pool, tmp_err,
err_abort,
apr_pool_cleanup_null);
#endif
return new_err;
}
void
svn_error_clear(svn_error_t *err)
{
if (err)
{
#if defined(SVN_DEBUG)
while (err->child)
err = err->child;
apr_pool_cleanup_kill(err->pool, err, err_abort);
#endif
svn_pool_destroy(err->pool);
}
}
svn_boolean_t
svn_error__is_tracing_link(svn_error_t *err)
{
#ifdef SVN_ERR__TRACING
/* ### A strcmp()? Really? I think it's the best we can do unless
### we add a boolean field to svn_error_t that's set only for
### these "placeholder error chain" items. Not such a bad idea,
### really... */
return (err && err->message && !strcmp(err->message, error_tracing_link));
#else
return FALSE;
#endif
}
svn_error_t *
svn_error_purge_tracing(svn_error_t *err)
{
#ifdef SVN_ERR__TRACING
svn_error_t *new_err = NULL, *new_err_leaf = NULL;
if (! err)
return SVN_NO_ERROR;
do
{
svn_error_t *tmp_err;
/* Skip over any trace-only links. */
while (err && svn_error__is_tracing_link(err))
err = err->child;
/* The link must be a real link in the error chain, otherwise an
error chain with trace only links would map into SVN_NO_ERROR. */
if (! err)
return svn_error_create(
SVN_ERR_ASSERTION_ONLY_TRACING_LINKS,
svn_error_compose_create(
svn_error__malfunction(TRUE, __FILE__, __LINE__,
NULL /* ### say something? */),
err),
NULL);
/* Copy the current error except for its child error pointer
into the new error. Share any message and source filename
strings from the error. */
tmp_err = apr_palloc(err->pool, sizeof(*tmp_err));
*tmp_err = *err;
tmp_err->child = NULL;
/* Add a new link to the new chain (creating the chain if necessary). */
if (! new_err)
{
new_err = tmp_err;
new_err_leaf = tmp_err;
}
else
{
new_err_leaf->child = tmp_err;
new_err_leaf = tmp_err;
}
/* Advance to the next link in the original chain. */
err = err->child;
} while (err);
return new_err;
#else /* SVN_ERR__TRACING */
return err;
#endif /* SVN_ERR__TRACING */
}
/* ### The logic around omitting (sic) apr_err= in maintainer mode is tightly
### coupled to the current sole caller.*/
static void
print_error(svn_error_t *err, FILE *stream, const char *prefix)
{
char errbuf[256];
const char *err_string;
svn_error_t *temp_err = NULL; /* ensure initialized even if
err->file == NULL */
/* Pretty-print the error */
/* Note: we can also log errors here someday. */
#ifdef SVN_DEBUG
/* Note: err->file is _not_ in UTF-8, because it's expanded from
the __FILE__ preprocessor macro. */
const char *file_utf8;
if (err->file
&& !(temp_err = svn_utf_cstring_to_utf8(&file_utf8, err->file,
err->pool)))
svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
"%s:%ld", err->file, err->line));
else
{
svn_error_clear(svn_cmdline_fputs(SVN_FILE_LINE_UNDEFINED,
stream, err->pool));
svn_error_clear(temp_err);
}
{
const char *symbolic_name;
if (svn_error__is_tracing_link(err))
/* Skip it; the error code will be printed by the real link. */
svn_error_clear(svn_cmdline_fprintf(stream, err->pool, ",\n"));
else if ((symbolic_name = svn_error_symbolic_name(err->apr_err)))
svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
": (apr_err=%s)\n", symbolic_name));
else
svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
": (apr_err=%d)\n", err->apr_err));
}
#endif /* SVN_DEBUG */
/* "traced call" */
if (svn_error__is_tracing_link(err))
{
/* Skip it. We already printed the file-line coordinates. */
}
/* Only print the same APR error string once. */
else if (err->message)
{
svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
"%sE%06d: %s\n",
prefix, err->apr_err, err->message));
}
else
{
/* Is this a Subversion-specific error code? */
if ((err->apr_err > APR_OS_START_USEERR)
&& (err->apr_err <= APR_OS_START_CANONERR))
err_string = svn_strerror(err->apr_err, errbuf, sizeof(errbuf));
/* Otherwise, this must be an APR error code. */
else if ((temp_err = svn_utf_cstring_to_utf8
(&err_string, apr_strerror(err->apr_err, errbuf,
sizeof(errbuf)), err->pool)))
{
svn_error_clear(temp_err);
err_string = _("Can't recode error string from APR");
}
svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
"%sE%06d: %s\n",
prefix, err->apr_err, err_string));
}
}
void
svn_handle_error(svn_error_t *err, FILE *stream, svn_boolean_t fatal)
{
svn_handle_error2(err, stream, fatal, "svn: ");
}
void
svn_handle_error2(svn_error_t *err,
FILE *stream,
svn_boolean_t fatal,
const char *prefix)
{
/* In a long error chain, there may be multiple errors with the same
error code and no custom message. We only want to print the
default message for that code once; printing it multiple times
would add no useful information. The 'empties' array below
remembers the codes of empty errors already seen in the chain.
We could allocate it in err->pool, but there's no telling how
long err will live or how many times it will get handled. So we
use a subpool. */
apr_pool_t *subpool;
apr_array_header_t *empties;
svn_error_t *tmp_err;
/* ### The rest of this file carefully avoids using svn_pool_*(),
preferring apr_pool_*() instead. I can't remember why -- it may
be an artifact of r843793, or it may be for some deeper reason --
but I'm playing it safe and using apr_pool_*() here too. */
apr_pool_create(&subpool, err->pool);
empties = apr_array_make(subpool, 0, sizeof(apr_status_t));
tmp_err = err;
while (tmp_err)
{
svn_boolean_t printed_already = FALSE;
if (! tmp_err->message)
{
int i;
for (i = 0; i < empties->nelts; i++)
{
if (tmp_err->apr_err == APR_ARRAY_IDX(empties, i, apr_status_t) )
{
printed_already = TRUE;
break;
}
}
}
if (! printed_already)
{
print_error(tmp_err, stream, prefix);
if (! tmp_err->message)
{
APR_ARRAY_PUSH(empties, apr_status_t) = tmp_err->apr_err;
}
}
tmp_err = tmp_err->child;
}
svn_pool_destroy(subpool);
fflush(stream);
if (fatal)
{
/* Avoid abort()s in maintainer mode. */
svn_error_clear(err);
/* We exit(1) here instead of abort()ing so that atexit handlers
get called. */
exit(EXIT_FAILURE);
}
}
void
svn_handle_warning(FILE *stream, svn_error_t *err)
{
svn_handle_warning2(stream, err, "svn: ");
}
void
svn_handle_warning2(FILE *stream, svn_error_t *err, const char *prefix)
{
char buf[256];
svn_error_clear(svn_cmdline_fprintf
(stream, err->pool,
_("%swarning: W%06d: %s\n"),
prefix, err->apr_err,
svn_err_best_message(err, buf, sizeof(buf))));
fflush(stream);
}
const char *
svn_err_best_message(svn_error_t *err, char *buf, apr_size_t bufsize)
{
/* Skip over any trace records. */
while (svn_error__is_tracing_link(err))
err = err->child;
if (err->message)
return err->message;
else
return svn_strerror(err->apr_err, buf, bufsize);
}
/* svn_strerror() and helpers */
/* Duplicate of the same typedef in tests/libsvn_subr/error-code-test.c */
typedef struct err_defn {
svn_errno_t errcode; /* 160004 */
const char *errname; /* SVN_ERR_FS_CORRUPT */
const char *errdesc; /* default message */
} err_defn;
/* To understand what is going on here, read svn_error_codes.h. */
#define SVN_ERROR_BUILD_ARRAY
#include "svn_error_codes.h"
char *
svn_strerror(apr_status_t statcode, char *buf, apr_size_t bufsize)
{
const err_defn *defn;
for (defn = error_table; defn->errdesc != NULL; ++defn)
if (defn->errcode == (svn_errno_t)statcode)
{
apr_cpystrn(buf, _(defn->errdesc), bufsize);
return buf;
}
return apr_strerror(statcode, buf, bufsize);
}
const char *
svn_error_symbolic_name(apr_status_t statcode)
{
const err_defn *defn;
for (defn = error_table; defn->errdesc != NULL; ++defn)
if (defn->errcode == (svn_errno_t)statcode)
return defn->errname;
/* "No error" is not in error_table. */
if (statcode == SVN_NO_ERROR)
return "SVN_NO_ERROR";
return NULL;
}
/* Malfunctions. */
svn_error_t *
svn_error_raise_on_malfunction(svn_boolean_t can_return,
const char *file, int line,
const char *expr)
{
if (!can_return)
abort(); /* Nothing else we can do as a library */
/* The filename and line number of the error source needs to be set
here because svn_error_createf() is not the macro defined in
svn_error.h but the real function. */
svn_error__locate(file, line);
if (expr)
return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,
_("In file '%s' line %d: assertion failed (%s)"),
file, line, expr);
else
return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,
_("In file '%s' line %d: internal malfunction"),
file, line);
}
svn_error_t *
svn_error_abort_on_malfunction(svn_boolean_t can_return,
const char *file, int line,
const char *expr)
{
svn_error_t *err = svn_error_raise_on_malfunction(TRUE, file, line, expr);
svn_handle_error2(err, stderr, FALSE, "svn: ");
abort();
return err; /* Not reached. */
}
/* The current handler for reporting malfunctions, and its default setting. */
static svn_error_malfunction_handler_t malfunction_handler
= svn_error_abort_on_malfunction;
svn_error_malfunction_handler_t
svn_error_set_malfunction_handler(svn_error_malfunction_handler_t func)
{
svn_error_malfunction_handler_t old_malfunction_handler
= malfunction_handler;
malfunction_handler = func;
return old_malfunction_handler;
}
/* Note: Although this is a "__" function, it is in the public ABI, so
* we can never remove it or change its signature. */
svn_error_t *
svn_error__malfunction(svn_boolean_t can_return,
const char *file, int line,
const char *expr)
{
return malfunction_handler(can_return, file, line, expr);
}
/* Misc. */
svn_error_t *
svn_error__wrap_zlib(int zerr, const char *function, const char *message)
{
apr_status_t status;
const char *zmsg;
if (zerr == Z_OK)
return SVN_NO_ERROR;
switch (zerr)
{
case Z_STREAM_ERROR:
status = SVN_ERR_STREAM_MALFORMED_DATA;
zmsg = _("stream error");
break;
case Z_MEM_ERROR:
status = APR_ENOMEM;
zmsg = _("out of memory");
break;
case Z_BUF_ERROR:
status = APR_ENOMEM;
zmsg = _("buffer error");
break;
case Z_VERSION_ERROR:
status = SVN_ERR_STREAM_UNRECOGNIZED_DATA;
zmsg = _("version error");
break;
case Z_DATA_ERROR:
status = SVN_ERR_STREAM_MALFORMED_DATA;
zmsg = _("corrupt data");
break;
default:
status = SVN_ERR_STREAM_UNRECOGNIZED_DATA;
zmsg = _("unknown error");
break;
}
if (message != NULL)
return svn_error_createf(status, NULL, "zlib (%s): %s: %s", function,
zmsg, message);
else
return svn_error_createf(status, NULL, "zlib (%s): %s", function, zmsg);
}
Index: vendor/subversion/dist/subversion/libsvn_subr/gpg_agent.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_subr/gpg_agent.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_subr/gpg_agent.c (revision 286501)
@@ -1,488 +1,642 @@
/*
* gpg_agent.c: GPG Agent provider for SVN_AUTH_CRED_*
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
/* ==================================================================== */
/* This auth provider stores a plaintext password in memory managed by
* a running gpg-agent. In contrast to other password store providers
* it does not save the password to disk.
*
* Prompting is performed by the gpg-agent using a "pinentry" program
* which needs to be installed separately. There are several pinentry
* implementations with different front-ends (e.g. qt, gtk, ncurses).
*
* The gpg-agent will let the password time out after a while,
* or immediately when it receives the SIGHUP signal.
* When the password has timed out it will automatically prompt the
* user for the password again. This is transparent to Subversion.
*
* SECURITY CONSIDERATIONS:
*
* Communication to the agent happens over a UNIX socket, which is located
* in a directory which only the user running Subversion can access.
* However, any program the user runs could access this socket and get
* the Subversion password if the program knows the "cache ID" Subversion
* uses for the password.
* The cache ID is very easy to obtain for programs running as the same user.
* Subversion uses the MD5 of the realmstring as cache ID, and these checksums
* are also used as filenames within ~/.subversion/auth/svn.simple.
* Unlike GNOME Keyring or KDE Wallet, the user is not prompted for
* permission if another program attempts to access the password.
*
* Therefore, while the gpg-agent is running and has the password cached,
* this provider is no more secure than a file storing the password in
* plaintext.
*/
/*** Includes. ***/
#ifndef WIN32
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <apr_pools.h>
#include "svn_auth.h"
#include "svn_config.h"
#include "svn_error.h"
#include "svn_pools.h"
#include "svn_cmdline.h"
#include "svn_checksum.h"
#include "svn_string.h"
+#include "svn_hash.h"
+#include "svn_user.h"
+#include "svn_dirent_uri.h"
#include "private/svn_auth_private.h"
#include "svn_private_config.h"
#ifdef SVN_HAVE_GPG_AGENT
#define BUFFER_SIZE 1024
+#define ATTEMPT_PARAMETER "svn.simple.gpg_agent.attempt"
/* Modify STR in-place such that blanks are escaped as required by the
* gpg-agent protocol. Return a pointer to STR. */
static char *
escape_blanks(char *str)
{
char *s = str;
while (*s)
{
if (*s == ' ')
*s = '+';
s++;
}
return str;
}
+/* Generate the string CACHE_ID_P based on the REALMSTRING allocated in
+ * RESULT_POOL using SCRATCH_POOL for temporary allocations. This is similar
+ * to other password caching mechanisms. */
+static svn_error_t *
+get_cache_id(const char **cache_id_p, const char *realmstring,
+ apr_pool_t *scratch_pool, apr_pool_t *result_pool)
+{
+ const char *cache_id = NULL;
+ svn_checksum_t *digest = NULL;
+
+ SVN_ERR(svn_checksum(&digest, svn_checksum_md5, realmstring,
+ strlen(realmstring), scratch_pool));
+ cache_id = svn_checksum_to_cstring(digest, result_pool);
+ *cache_id_p = cache_id;
+
+ return SVN_NO_ERROR;
+}
+
/* Attempt to read a gpg-agent response message from the socket SD into
* buffer BUF. Buf is assumed to be N bytes large. Return TRUE if a response
* message could be read that fits into the buffer. Else return FALSE.
* If a message could be read it will always be NUL-terminated and the
* trailing newline is retained. */
static svn_boolean_t
receive_from_gpg_agent(int sd, char *buf, size_t n)
{
int i = 0;
size_t recvd;
char c;
/* Clear existing buffer content before reading response. */
if (n > 0)
*buf = '\0';
/* Require the message to fit into the buffer and be terminated
* with a newline. */
while (i < n)
{
recvd = read(sd, &c, 1);
if (recvd == -1)
return FALSE;
buf[i] = c;
i++;
if (i < n && c == '\n')
{
buf[i] = '\0';
return TRUE;
}
}
return FALSE;
}
/* Using socket SD, send the option OPTION with the specified VALUE
* to the gpg agent. Store the response in BUF, assumed to be N bytes
* in size, and evaluate the response. Return TRUE if the agent liked
* the smell of the option, if there is such a thing, and doesn't feel
* saturated by it. Else return FALSE.
* Do temporary allocations in scratch_pool. */
static svn_boolean_t
send_option(int sd, char *buf, size_t n, const char *option, const char *value,
apr_pool_t *scratch_pool)
{
const char *request;
request = apr_psprintf(scratch_pool, "OPTION %s=%s\n", option, value);
if (write(sd, request, strlen(request)) == -1)
return FALSE;
if (!receive_from_gpg_agent(sd, buf, n))
return FALSE;
return (strncmp(buf, "OK", 2) == 0);
}
+/* Send the BYE command and disconnect from the gpg-agent. Doing this avoids
+ * gpg-agent emitting a "Connection reset by peer" log message with some
+ * versions of gpg-agent. */
+static void
+bye_gpg_agent(int sd)
+{
+ /* don't bother to check the result of the write, it either worked or it
+ * didn't, but either way we're closing. */
+ write(sd, "BYE\n", 4);
+ close(sd);
+}
/* Locate a running GPG Agent, and return an open file descriptor
* for communication with the agent in *NEW_SD. If no running agent
* can be found, set *NEW_SD to -1. */
static svn_error_t *
find_running_gpg_agent(int *new_sd, apr_pool_t *pool)
{
char *buffer;
char *gpg_agent_info = NULL;
const char *socket_name = NULL;
const char *request = NULL;
const char *p = NULL;
char *ep = NULL;
int sd;
*new_sd = -1;
+ /* This implements the method of finding the socket as described in
+ * the gpg-agent man page under the --use-standard-socket option.
+ * The manage page misleadingly says the standard socket is
+ * "named 'S.gpg-agent' located in the home directory." The standard
+ * socket path is actually in the .gnupg directory in the home directory,
+ * i.e. ~/.gnupg/S.gpg-agent */
gpg_agent_info = getenv("GPG_AGENT_INFO");
if (gpg_agent_info != NULL)
{
apr_array_header_t *socket_details;
+ /* For reference GPG_AGENT_INFO consists of 3 : separated fields.
+ * The path to the socket, the pid of the gpg-agent process and
+ * finally the version of the protocol the agent talks. */
socket_details = svn_cstring_split(gpg_agent_info, ":", TRUE,
pool);
socket_name = APR_ARRAY_IDX(socket_details, 0, const char *);
}
else
- return SVN_NO_ERROR;
+ {
+ const char *homedir = svn_user_get_homedir(pool);
+ if (!homedir)
+ return SVN_NO_ERROR;
+
+ socket_name = svn_dirent_join_many(pool, homedir, ".gnupg",
+ "S.gpg-agent", NULL);
+ }
+
if (socket_name != NULL)
{
struct sockaddr_un addr;
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, socket_name, sizeof(addr.sun_path) - 1);
addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
sd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sd == -1)
return SVN_NO_ERROR;
if (connect(sd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
{
close(sd);
return SVN_NO_ERROR;
}
}
else
return SVN_NO_ERROR;
/* Receive the connection status from the gpg-agent daemon. */
buffer = apr_palloc(pool, BUFFER_SIZE);
if (!receive_from_gpg_agent(sd, buffer, BUFFER_SIZE))
{
- close(sd);
+ bye_gpg_agent(sd);
return SVN_NO_ERROR;
}
if (strncmp(buffer, "OK", 2) != 0)
{
- close(sd);
+ bye_gpg_agent(sd);
return SVN_NO_ERROR;
}
/* The GPG-Agent documentation says:
* "Clients should deny to access an agent with a socket name which does
* not match its own configuration". */
request = "GETINFO socket_name\n";
if (write(sd, request, strlen(request)) == -1)
{
- close(sd);
+ bye_gpg_agent(sd);
return SVN_NO_ERROR;
}
if (!receive_from_gpg_agent(sd, buffer, BUFFER_SIZE))
{
- close(sd);
+ bye_gpg_agent(sd);
return SVN_NO_ERROR;
}
if (strncmp(buffer, "D", 1) == 0)
p = &buffer[2];
if (!p)
{
- close(sd);
+ bye_gpg_agent(sd);
return SVN_NO_ERROR;
}
ep = strchr(p, '\n');
if (ep != NULL)
*ep = '\0';
if (strcmp(socket_name, p) != 0)
{
- close(sd);
+ bye_gpg_agent(sd);
return SVN_NO_ERROR;
}
/* The agent will terminate its response with "OK". */
if (!receive_from_gpg_agent(sd, buffer, BUFFER_SIZE))
{
- close(sd);
+ bye_gpg_agent(sd);
return SVN_NO_ERROR;
}
if (strncmp(buffer, "OK", 2) != 0)
{
- close(sd);
+ bye_gpg_agent(sd);
return SVN_NO_ERROR;
}
*new_sd = sd;
return SVN_NO_ERROR;
}
-/* Implementation of svn_auth__password_get_t that retrieves the password
- from gpg-agent */
-static svn_error_t *
-password_get_gpg_agent(svn_boolean_t *done,
- const char **password,
- apr_hash_t *creds,
- const char *realmstring,
- const char *username,
- apr_hash_t *parameters,
- svn_boolean_t non_interactive,
- apr_pool_t *pool)
+static svn_boolean_t
+send_options(int sd, char *buf, size_t n, apr_pool_t *scratch_pool)
{
- int sd;
- const char *p = NULL;
- char *ep = NULL;
- char *buffer;
- const char *request = NULL;
- const char *cache_id = NULL;
const char *tty_name;
const char *tty_type;
const char *lc_ctype;
const char *display;
- svn_checksum_t *digest = NULL;
- char *password_prompt;
- char *realm_prompt;
- *done = FALSE;
-
- SVN_ERR(find_running_gpg_agent(&sd, pool));
- if (sd == -1)
- return SVN_NO_ERROR;
-
- buffer = apr_palloc(pool, BUFFER_SIZE);
-
/* Send TTY_NAME to the gpg-agent daemon. */
tty_name = getenv("GPG_TTY");
if (tty_name != NULL)
{
- if (!send_option(sd, buffer, BUFFER_SIZE, "ttyname", tty_name, pool))
- {
- close(sd);
- return SVN_NO_ERROR;
- }
+ if (!send_option(sd, buf, n, "ttyname", tty_name, scratch_pool))
+ return FALSE;
}
/* Send TTY_TYPE to the gpg-agent daemon. */
tty_type = getenv("TERM");
if (tty_type != NULL)
{
- if (!send_option(sd, buffer, BUFFER_SIZE, "ttytype", tty_type, pool))
- {
- close(sd);
- return SVN_NO_ERROR;
- }
+ if (!send_option(sd, buf, n, "ttytype", tty_type, scratch_pool))
+ return FALSE;
}
/* Compute LC_CTYPE. */
lc_ctype = getenv("LC_ALL");
if (lc_ctype == NULL)
lc_ctype = getenv("LC_CTYPE");
if (lc_ctype == NULL)
lc_ctype = getenv("LANG");
/* Send LC_CTYPE to the gpg-agent daemon. */
if (lc_ctype != NULL)
{
- if (!send_option(sd, buffer, BUFFER_SIZE, "lc-ctype", lc_ctype, pool))
- {
- close(sd);
- return SVN_NO_ERROR;
- }
+ if (!send_option(sd, buf, n, "lc-ctype", lc_ctype, scratch_pool))
+ return FALSE;
}
/* Send DISPLAY to the gpg-agent daemon. */
display = getenv("DISPLAY");
if (display != NULL)
{
- if (!send_option(sd, buffer, BUFFER_SIZE, "display", display, pool))
- {
- close(sd);
- return SVN_NO_ERROR;
- }
+ if (!send_option(sd, buf, n, "display", display, scratch_pool))
+ return FALSE;
}
- /* Create the CACHE_ID which will be generated based on REALMSTRING similar
- to other password caching mechanisms. */
- SVN_ERR(svn_checksum(&digest, svn_checksum_md5, realmstring,
- strlen(realmstring), pool));
- cache_id = svn_checksum_to_cstring(digest, pool);
+ return TRUE;
+}
+/* Implementation of svn_auth__password_get_t that retrieves the password
+ from gpg-agent */
+static svn_error_t *
+password_get_gpg_agent(svn_boolean_t *done,
+ const char **password,
+ apr_hash_t *creds,
+ const char *realmstring,
+ const char *username,
+ apr_hash_t *parameters,
+ svn_boolean_t non_interactive,
+ apr_pool_t *pool)
+{
+ int sd;
+ const char *p = NULL;
+ char *ep = NULL;
+ char *buffer;
+ const char *request = NULL;
+ const char *cache_id = NULL;
+ char *password_prompt;
+ char *realm_prompt;
+ char *error_prompt;
+ int *attempt;
+
+ *done = FALSE;
+
+ attempt = svn_hash_gets(parameters, ATTEMPT_PARAMETER);
+
+ SVN_ERR(find_running_gpg_agent(&sd, pool));
+ if (sd == -1)
+ return SVN_NO_ERROR;
+
+ buffer = apr_palloc(pool, BUFFER_SIZE);
+
+ if (!send_options(sd, buffer, BUFFER_SIZE, pool))
+ {
+ bye_gpg_agent(sd);
+ return SVN_NO_ERROR;
+ }
+
+ SVN_ERR(get_cache_id(&cache_id, realmstring, pool, pool));
+
password_prompt = apr_psprintf(pool, _("Password for '%s': "), username);
realm_prompt = apr_psprintf(pool, _("Enter your Subversion password for %s"),
realmstring);
+ if (*attempt == 1)
+ /* X means no error to the gpg-agent protocol */
+ error_prompt = apr_pstrdup(pool, "X");
+ else
+ error_prompt = apr_pstrdup(pool, _("Authentication failed"));
+
request = apr_psprintf(pool,
- "GET_PASSPHRASE --data %s--repeat=1 "
- "%s X %s %s\n",
+ "GET_PASSPHRASE --data %s"
+ "%s %s %s %s\n",
non_interactive ? "--no-ask " : "",
cache_id,
+ escape_blanks(error_prompt),
escape_blanks(password_prompt),
escape_blanks(realm_prompt));
if (write(sd, request, strlen(request)) == -1)
{
- close(sd);
+ bye_gpg_agent(sd);
return SVN_NO_ERROR;
}
if (!receive_from_gpg_agent(sd, buffer, BUFFER_SIZE))
{
- close(sd);
+ bye_gpg_agent(sd);
return SVN_NO_ERROR;
}
- close(sd);
+ bye_gpg_agent(sd);
if (strncmp(buffer, "ERR", 3) == 0)
return SVN_NO_ERROR;
p = NULL;
if (strncmp(buffer, "D", 1) == 0)
p = &buffer[2];
if (!p)
return SVN_NO_ERROR;
ep = strchr(p, '\n');
if (ep != NULL)
*ep = '\0';
*password = p;
*done = TRUE;
return SVN_NO_ERROR;
}
/* Implementation of svn_auth__password_set_t that would store the
password in GPG Agent if that's how this particular integration
worked. But it isn't. GPG Agent stores the password provided by
the user via the pinentry program immediately upon its provision
(and regardless of its accuracy as passwords go), so we just need
to check if a running GPG Agent exists. */
static svn_error_t *
password_set_gpg_agent(svn_boolean_t *done,
apr_hash_t *creds,
const char *realmstring,
const char *username,
const char *password,
apr_hash_t *parameters,
svn_boolean_t non_interactive,
apr_pool_t *pool)
{
int sd;
*done = FALSE;
SVN_ERR(find_running_gpg_agent(&sd, pool));
if (sd == -1)
return SVN_NO_ERROR;
- close(sd);
+ bye_gpg_agent(sd);
*done = TRUE;
return SVN_NO_ERROR;
}
/* An implementation of svn_auth_provider_t::first_credentials() */
static svn_error_t *
simple_gpg_agent_first_creds(void **credentials,
void **iter_baton,
void *provider_baton,
apr_hash_t *parameters,
const char *realmstring,
apr_pool_t *pool)
{
- return svn_auth__simple_creds_cache_get(credentials, iter_baton,
- provider_baton, parameters,
- realmstring, password_get_gpg_agent,
- SVN_AUTH__GPG_AGENT_PASSWORD_TYPE,
- pool);
+ svn_error_t *err;
+ int *attempt = apr_palloc(pool, sizeof(*attempt));
+
+ *attempt = 1;
+ svn_hash_sets(parameters, ATTEMPT_PARAMETER, attempt);
+ err = svn_auth__simple_creds_cache_get(credentials, iter_baton,
+ provider_baton, parameters,
+ realmstring, password_get_gpg_agent,
+ SVN_AUTH__GPG_AGENT_PASSWORD_TYPE,
+ pool);
+ *iter_baton = attempt;
+
+ return err;
}
+/* An implementation of svn_auth_provider_t::next_credentials() */
+static svn_error_t *
+simple_gpg_agent_next_creds(void **credentials,
+ void *iter_baton,
+ void *provider_baton,
+ apr_hash_t *parameters,
+ const char *realmstring,
+ apr_pool_t *pool)
+{
+ int *attempt = (int *)iter_baton;
+ int sd;
+ char *buffer;
+ const char *cache_id = NULL;
+ const char *request = NULL;
+ *credentials = NULL;
+
+ /* The users previous credentials failed so first remove the cached entry,
+ * before trying to retrieve them again. Because gpg-agent stores cached
+ * credentials immediately upon retrieving them, this gives us the
+ * opportunity to remove the invalid credentials and prompt the
+ * user again. While it's possible that server side issues could trigger
+ * this, this cache is ephemeral so at worst we're just speeding up
+ * when the user would need to re-enter their password. */
+
+ if (svn_hash_gets(parameters, SVN_AUTH_PARAM_NON_INTERACTIVE))
+ {
+ /* In this case since we're running non-interactively we do not
+ * want to clear the cache since the user was never prompted by
+ * gpg-agent to set a password. */
+ return SVN_NO_ERROR;
+ }
+
+ *attempt = *attempt + 1;
+
+ SVN_ERR(find_running_gpg_agent(&sd, pool));
+ if (sd == -1)
+ return SVN_NO_ERROR;
+
+ buffer = apr_palloc(pool, BUFFER_SIZE);
+
+ if (!send_options(sd, buffer, BUFFER_SIZE, pool))
+ {
+ bye_gpg_agent(sd);
+ return SVN_NO_ERROR;
+ }
+
+ SVN_ERR(get_cache_id(&cache_id, realmstring, pool, pool));
+
+ request = apr_psprintf(pool, "CLEAR_PASSPHRASE %s\n", cache_id);
+
+ if (write(sd, request, strlen(request)) == -1)
+ {
+ bye_gpg_agent(sd);
+ return SVN_NO_ERROR;
+ }
+
+ if (!receive_from_gpg_agent(sd, buffer, BUFFER_SIZE))
+ {
+ bye_gpg_agent(sd);
+ return SVN_NO_ERROR;
+ }
+
+ if (strncmp(buffer, "OK\n", 3) != 0)
+ {
+ bye_gpg_agent(sd);
+ return SVN_NO_ERROR;
+ }
+
+ /* TODO: This attempt limit hard codes it at 3 attempts (or 2 retries)
+ * which matches svn command line client's retry_limit as set in
+ * svn_cmdline_create_auth_baton(). It would be nice to have that
+ * limit reflected here but that violates the boundry between the
+ * prompt provider and the cache provider. gpg-agent is acting as
+ * both here due to the peculiarties of their design so we'll have to
+ * live with this for now. Note that when these failures get exceeded
+ * it'll eventually fall back on the retry limits of whatever prompt
+ * provider is in effect, so this effectively doubles the limit. */
+ if (*attempt < 4)
+ return svn_auth__simple_creds_cache_get(credentials, &iter_baton,
+ provider_baton, parameters,
+ realmstring,
+ password_get_gpg_agent,
+ SVN_AUTH__GPG_AGENT_PASSWORD_TYPE,
+ pool);
+
+ return SVN_NO_ERROR;
+}
+
+
/* An implementation of svn_auth_provider_t::save_credentials() */
static svn_error_t *
simple_gpg_agent_save_creds(svn_boolean_t *saved,
void *credentials,
void *provider_baton,
apr_hash_t *parameters,
const char *realmstring,
apr_pool_t *pool)
{
return svn_auth__simple_creds_cache_set(saved, credentials,
provider_baton, parameters,
realmstring, password_set_gpg_agent,
SVN_AUTH__GPG_AGENT_PASSWORD_TYPE,
pool);
}
static const svn_auth_provider_t gpg_agent_simple_provider = {
SVN_AUTH_CRED_SIMPLE,
simple_gpg_agent_first_creds,
- NULL,
+ simple_gpg_agent_next_creds,
simple_gpg_agent_save_creds
};
/* Public API */
void
svn_auth_get_gpg_agent_simple_provider(svn_auth_provider_object_t **provider,
apr_pool_t *pool)
{
svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
po->vtable = &gpg_agent_simple_provider;
*provider = po;
}
#endif /* SVN_HAVE_GPG_AGENT */
#endif /* !WIN32 */
Index: vendor/subversion/dist/subversion/libsvn_subr/internal_statements.h
===================================================================
--- vendor/subversion/dist/subversion/libsvn_subr/internal_statements.h (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_subr/internal_statements.h (revision 286501)
@@ -1,76 +1,76 @@
-/* This file is automatically generated from internal_statements.sql and .dist_sandbox/subversion-1.8.10/subversion/libsvn_subr/token-map.h.
+/* This file is automatically generated from internal_statements.sql and .dist_sandbox/subversion-1.8.14/subversion/libsvn_subr/token-map.h.
* Do not edit this file -- edit the source and rerun gen-make.py */
#define STMT_INTERNAL_SAVEPOINT_SVN 0
#define STMT_0_INFO {"STMT_INTERNAL_SAVEPOINT_SVN", NULL}
#define STMT_0 \
"SAVEPOINT svn " \
""
#define STMT_INTERNAL_RELEASE_SAVEPOINT_SVN 1
#define STMT_1_INFO {"STMT_INTERNAL_RELEASE_SAVEPOINT_SVN", NULL}
#define STMT_1 \
"RELEASE SAVEPOINT svn " \
""
#define STMT_INTERNAL_ROLLBACK_TO_SAVEPOINT_SVN 2
#define STMT_2_INFO {"STMT_INTERNAL_ROLLBACK_TO_SAVEPOINT_SVN", NULL}
#define STMT_2 \
"ROLLBACK TO SAVEPOINT svn " \
""
#define STMT_INTERNAL_BEGIN_TRANSACTION 3
#define STMT_3_INFO {"STMT_INTERNAL_BEGIN_TRANSACTION", NULL}
#define STMT_3 \
"BEGIN TRANSACTION " \
""
#define STMT_INTERNAL_BEGIN_IMMEDIATE_TRANSACTION 4
#define STMT_4_INFO {"STMT_INTERNAL_BEGIN_IMMEDIATE_TRANSACTION", NULL}
#define STMT_4 \
"BEGIN IMMEDIATE TRANSACTION " \
""
#define STMT_INTERNAL_COMMIT_TRANSACTION 5
#define STMT_5_INFO {"STMT_INTERNAL_COMMIT_TRANSACTION", NULL}
#define STMT_5 \
"COMMIT TRANSACTION " \
""
#define STMT_INTERNAL_ROLLBACK_TRANSACTION 6
#define STMT_6_INFO {"STMT_INTERNAL_ROLLBACK_TRANSACTION", NULL}
#define STMT_6 \
"ROLLBACK TRANSACTION " \
""
#define STMT_INTERNAL_LAST 7
#define STMT_7_INFO {"STMT_INTERNAL_LAST", NULL}
#define STMT_7 \
"; " \
""
#define INTERNAL_STATEMENTS_SQL_DECLARE_STATEMENTS(varname) \
static const char * const varname[] = { \
STMT_0, \
STMT_1, \
STMT_2, \
STMT_3, \
STMT_4, \
STMT_5, \
STMT_6, \
STMT_7, \
NULL \
}
#define INTERNAL_STATEMENTS_SQL_DECLARE_STATEMENT_INFO(varname) \
static const char * const varname[][2] = { \
STMT_0_INFO, \
STMT_1_INFO, \
STMT_2_INFO, \
STMT_3_INFO, \
STMT_4_INFO, \
STMT_5_INFO, \
STMT_6_INFO, \
STMT_7_INFO, \
{NULL, NULL} \
}
Index: vendor/subversion/dist/subversion/libsvn_subr/io.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_subr/io.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_subr/io.c (revision 286501)
@@ -1,4778 +1,4795 @@
/*
* io.c: shared file reading, writing, and probing code.
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include <stdio.h>
#ifndef WIN32
#include <unistd.h>
#endif
#ifndef APR_STATUS_IS_EPERM
#include <errno.h>
#ifdef EPERM
#define APR_STATUS_IS_EPERM(s) ((s) == EPERM)
#else
#define APR_STATUS_IS_EPERM(s) (0)
#endif
#endif
#include <apr_lib.h>
#include <apr_pools.h>
#include <apr_file_io.h>
#include <apr_file_info.h>
#include <apr_general.h>
#include <apr_strings.h>
#include <apr_portable.h>
#include <apr_md5.h>
#ifdef WIN32
#include <arch/win32/apr_arch_file_io.h>
#endif
#include "svn_hash.h"
#include "svn_types.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_string.h"
#include "svn_error.h"
#include "svn_io.h"
#include "svn_pools.h"
#include "svn_utf.h"
#include "svn_config.h"
#include "svn_private_config.h"
#include "svn_ctype.h"
#include "private/svn_atomic.h"
#include "private/svn_io_private.h"
#define SVN_SLEEP_ENV_VAR "SVN_I_LOVE_CORRUPTED_WORKING_COPIES_SO_DISABLE_SLEEP_FOR_TIMESTAMPS"
/*
Windows is 'aided' by a number of types of applications that
follow other applications around and open up files they have
changed for various reasons (the most intrusive are virus
scanners). So, if one of these other apps has glommed onto
our file we may get an 'access denied' error.
This retry loop does not completely solve the problem (who
knows how long the other app is going to hold onto it for), but
goes a long way towards minimizing it. It is not an infinite
loop because there might really be an error.
Another reason for retrying delete operations on Windows
is that they are asynchronous -- the file or directory is not
actually deleted until the last handle to it is closed. The
retry loop cannot completely solve this problem either, but can
help mitigate it.
*/
#define RETRY_MAX_ATTEMPTS 100
#define RETRY_INITIAL_SLEEP 1000
#define RETRY_MAX_SLEEP 128000
#define RETRY_LOOP(err, expr, retry_test, sleep_test) \
do \
{ \
apr_status_t os_err = APR_TO_OS_ERROR(err); \
int sleep_count = RETRY_INITIAL_SLEEP; \
int retries; \
for (retries = 0; \
retries < RETRY_MAX_ATTEMPTS && (retry_test); \
os_err = APR_TO_OS_ERROR(err)) \
{ \
if (sleep_test) \
{ \
++retries; \
apr_sleep(sleep_count); \
if (sleep_count < RETRY_MAX_SLEEP) \
sleep_count *= 2; \
} \
(err) = (expr); \
} \
} \
while (0)
#if defined(EDEADLK) && APR_HAS_THREADS
#define FILE_LOCK_RETRY_LOOP(err, expr) \
RETRY_LOOP(err, \
expr, \
(APR_STATUS_IS_EINTR(err) || os_err == EDEADLK), \
(!APR_STATUS_IS_EINTR(err)))
#else
#define FILE_LOCK_RETRY_LOOP(err, expr) \
RETRY_LOOP(err, \
expr, \
(APR_STATUS_IS_EINTR(err)), \
0)
#endif
#ifndef WIN32_RETRY_LOOP
#if defined(WIN32) && !defined(SVN_NO_WIN32_RETRY_LOOP)
#define WIN32_RETRY_LOOP(err, expr) \
RETRY_LOOP(err, expr, (os_err == ERROR_ACCESS_DENIED \
|| os_err == ERROR_SHARING_VIOLATION \
|| os_err == ERROR_DIR_NOT_EMPTY), \
1)
#else
#define WIN32_RETRY_LOOP(err, expr) ((void)0)
#endif
#endif
/* Forward declaration */
static apr_status_t
dir_is_empty(const char *dir, apr_pool_t *pool);
static APR_INLINE svn_error_t *
do_io_file_wrapper_cleanup(apr_file_t *file, apr_status_t status,
const char *msg, const char *msg_no_name,
apr_pool_t *pool);
/* Local wrapper of svn_path_cstring_to_utf8() that does no copying on
* operating systems where APR always uses utf-8 as native path format */
static svn_error_t *
cstring_to_utf8(const char **path_utf8,
const char *path_apr,
apr_pool_t *pool)
{
#if defined(WIN32) || defined(DARWIN)
*path_utf8 = path_apr;
return SVN_NO_ERROR;
#else
return svn_path_cstring_to_utf8(path_utf8, path_apr, pool);
#endif
}
/* Local wrapper of svn_path_cstring_from_utf8() that does no copying on
* operating systems where APR always uses utf-8 as native path format */
static svn_error_t *
cstring_from_utf8(const char **path_apr,
const char *path_utf8,
apr_pool_t *pool)
{
#if defined(WIN32) || defined(DARWIN)
*path_apr = path_utf8;
return SVN_NO_ERROR;
#else
return svn_path_cstring_from_utf8(path_apr, path_utf8, pool);
#endif
}
/* Helper function that allows to convert an APR-level PATH to something
* that we can pass the svn_error_wrap_apr. Since we use it in context
* of error reporting, having *some* path info may be more useful than
* having none. Therefore, we use a best effort approach here.
*
* This is different from svn_io_file_name_get in that it uses a different
* signature style and will never fail.
*/
static const char *
try_utf8_from_internal_style(const char *path, apr_pool_t *pool)
{
svn_error_t *error;
const char *path_utf8;
/* Special case. */
if (path == NULL)
return "(NULL)";
/* (try to) convert PATH to UTF-8. If that fails, continue with the plain
* PATH because it is the best we have. It may actually be UTF-8 already.
*/
error = cstring_to_utf8(&path_utf8, path, pool);
if (error)
{
/* fallback to best representation we have */
svn_error_clear(error);
path_utf8 = path;
}
/* Toggle (back-)slashes etc. as necessary.
*/
return svn_dirent_local_style(path_utf8, pool);
}
/* Set *NAME_P to the UTF-8 representation of directory entry NAME.
* NAME is in the internal encoding used by APR; PARENT is in
* UTF-8 and in internal (not local) style.
*
* Use PARENT only for generating an error string if the conversion
* fails because NAME could not be represented in UTF-8. In that
* case, return a two-level error in which the outer error's message
* mentions PARENT, but the inner error's message does not mention
* NAME (except possibly in hex) since NAME may not be printable.
* Such a compound error at least allows the user to go looking in the
* right directory for the problem.
*
* If there is any other error, just return that error directly.
*
* If there is any error, the effect on *NAME_P is undefined.
*
* *NAME_P and NAME may refer to the same storage.
*/
static svn_error_t *
entry_name_to_utf8(const char **name_p,
const char *name,
const char *parent,
apr_pool_t *pool)
{
#if defined(WIN32) || defined(DARWIN)
*name_p = apr_pstrdup(pool, name);
return SVN_NO_ERROR;
#else
svn_error_t *err = svn_path_cstring_to_utf8(name_p, name, pool);
if (err && err->apr_err == APR_EINVAL)
{
return svn_error_createf(err->apr_err, err,
_("Error converting entry "
"in directory '%s' to UTF-8"),
svn_dirent_local_style(parent, pool));
}
return err;
#endif
}
static void
map_apr_finfo_to_node_kind(svn_node_kind_t *kind,
svn_boolean_t *is_special,
apr_finfo_t *finfo)
{
*is_special = FALSE;
if (finfo->filetype == APR_REG)
*kind = svn_node_file;
else if (finfo->filetype == APR_DIR)
*kind = svn_node_dir;
else if (finfo->filetype == APR_LNK)
{
*is_special = TRUE;
*kind = svn_node_file;
}
else
*kind = svn_node_unknown;
}
/* Helper for svn_io_check_path() and svn_io_check_resolved_path();
essentially the same semantics as those two, with the obvious
interpretation for RESOLVE_SYMLINKS. */
static svn_error_t *
io_check_path(const char *path,
svn_boolean_t resolve_symlinks,
svn_boolean_t *is_special_p,
svn_node_kind_t *kind,
apr_pool_t *pool)
{
apr_int32_t flags;
apr_finfo_t finfo;
apr_status_t apr_err;
const char *path_apr;
svn_boolean_t is_special = FALSE;
if (path[0] == '\0')
path = ".";
/* Not using svn_io_stat() here because we want to check the
apr_err return explicitly. */
SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
flags = resolve_symlinks ? APR_FINFO_MIN : (APR_FINFO_MIN | APR_FINFO_LINK);
apr_err = apr_stat(&finfo, path_apr, flags, pool);
if (APR_STATUS_IS_ENOENT(apr_err))
*kind = svn_node_none;
else if (SVN__APR_STATUS_IS_ENOTDIR(apr_err))
*kind = svn_node_none;
else if (apr_err)
return svn_error_wrap_apr(apr_err, _("Can't check path '%s'"),
svn_dirent_local_style(path, pool));
else
map_apr_finfo_to_node_kind(kind, &is_special, &finfo);
*is_special_p = is_special;
return SVN_NO_ERROR;
}
/* Wrapper for apr_file_open(), taking an APR-encoded filename. */
static apr_status_t
file_open(apr_file_t **f,
const char *fname_apr,
apr_int32_t flag,
apr_fileperms_t perm,
svn_boolean_t retry_on_failure,
apr_pool_t *pool)
{
apr_status_t status = apr_file_open(f, fname_apr, flag, perm, pool);
if (retry_on_failure)
{
WIN32_RETRY_LOOP(status, apr_file_open(f, fname_apr, flag, perm, pool));
}
return status;
}
svn_error_t *
svn_io_check_resolved_path(const char *path,
svn_node_kind_t *kind,
apr_pool_t *pool)
{
svn_boolean_t ignored;
return io_check_path(path, TRUE, &ignored, kind, pool);
}
svn_error_t *
svn_io_check_path(const char *path,
svn_node_kind_t *kind,
apr_pool_t *pool)
{
svn_boolean_t ignored;
return io_check_path(path, FALSE, &ignored, kind, pool);
}
svn_error_t *
svn_io_check_special_path(const char *path,
svn_node_kind_t *kind,
svn_boolean_t *is_special,
apr_pool_t *pool)
{
return io_check_path(path, FALSE, is_special, kind, pool);
}
struct temp_file_cleanup_s
{
apr_pool_t *pool;
/* The (APR-encoded) full path of the file to be removed, or NULL if
* nothing to do. */
const char *fname_apr;
};
static apr_status_t
temp_file_plain_cleanup_handler(void *baton)
{
struct temp_file_cleanup_s *b = baton;
apr_status_t apr_err = APR_SUCCESS;
if (b->fname_apr)
{
apr_err = apr_file_remove(b->fname_apr, b->pool);
WIN32_RETRY_LOOP(apr_err, apr_file_remove(b->fname_apr, b->pool));
}
return apr_err;
}
static apr_status_t
temp_file_child_cleanup_handler(void *baton)
{
struct temp_file_cleanup_s *b = baton;
apr_pool_cleanup_kill(b->pool, b,
temp_file_plain_cleanup_handler);
return APR_SUCCESS;
}
svn_error_t *
svn_io_open_uniquely_named(apr_file_t **file,
const char **unique_path,
const char *dirpath,
const char *filename,
const char *suffix,
svn_io_file_del_t delete_when,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *path;
unsigned int i;
struct temp_file_cleanup_s *baton = NULL;
/* At the beginning, we don't know whether unique_path will need
UTF8 conversion */
svn_boolean_t needs_utf8_conversion = TRUE;
SVN_ERR_ASSERT(file || unique_path);
if (dirpath == NULL)
SVN_ERR(svn_io_temp_dir(&dirpath, scratch_pool));
if (filename == NULL)
filename = "tempfile";
if (suffix == NULL)
suffix = ".tmp";
path = svn_dirent_join(dirpath, filename, scratch_pool);
if (delete_when == svn_io_file_del_on_pool_cleanup)
{
baton = apr_palloc(result_pool, sizeof(*baton));
baton->pool = result_pool;
baton->fname_apr = NULL;
/* Because cleanups are run LIFO, we need to make sure to register
our cleanup before the apr_file_close cleanup:
On Windows, you can't remove an open file.
*/
apr_pool_cleanup_register(result_pool, baton,
temp_file_plain_cleanup_handler,
temp_file_child_cleanup_handler);
}
for (i = 1; i <= 99999; i++)
{
const char *unique_name;
const char *unique_name_apr;
apr_file_t *try_file;
apr_status_t apr_err;
apr_int32_t flag = (APR_READ | APR_WRITE | APR_CREATE | APR_EXCL
| APR_BUFFERED | APR_BINARY);
if (delete_when == svn_io_file_del_on_close)
flag |= APR_DELONCLOSE;
/* Special case the first attempt -- if we can avoid having a
generated numeric portion at all, that's best. So first we
try with just the suffix; then future tries add a number
before the suffix. (A do-while loop could avoid the repeated
conditional, but it's not worth the clarity loss.)
If the first attempt fails, the first number will be "2".
This is good, since "1" would misleadingly imply that
the second attempt was actually the first... and if someone's
got conflicts on their conflicts, we probably don't want to
add to their confusion :-). */
if (i == 1)
unique_name = apr_psprintf(scratch_pool, "%s%s", path, suffix);
else
unique_name = apr_psprintf(scratch_pool, "%s.%u%s", path, i, suffix);
/* Hmmm. Ideally, we would append to a native-encoding buf
before starting iteration, then convert back to UTF-8 for
return. But I suppose that would make the appending code
sensitive to i18n in a way it shouldn't be... Oh well. */
if (needs_utf8_conversion)
{
SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name,
scratch_pool));
if (i == 1)
{
/* The variable parts of unique_name will not require UTF8
conversion. Therefore, if UTF8 conversion had no effect
on it in the first iteration, it won't require conversion
in any future iteration. */
needs_utf8_conversion = strcmp(unique_name_apr, unique_name);
}
}
else
unique_name_apr = unique_name;
apr_err = file_open(&try_file, unique_name_apr, flag,
APR_OS_DEFAULT, FALSE, result_pool);
if (APR_STATUS_IS_EEXIST(apr_err))
continue;
else if (apr_err)
{
/* On Win32, CreateFile fails with an "Access Denied" error
code, rather than "File Already Exists", if the colliding
name belongs to a directory. */
if (APR_STATUS_IS_EACCES(apr_err))
{
apr_finfo_t finfo;
apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr,
APR_FINFO_TYPE, scratch_pool);
if (!apr_err_2 && finfo.filetype == APR_DIR)
continue;
#ifdef WIN32
apr_err_2 = APR_TO_OS_ERROR(apr_err);
if (apr_err_2 == ERROR_ACCESS_DENIED ||
apr_err_2 == ERROR_SHARING_VIOLATION)
{
/* The file is in use by another process or is hidden;
create a new name, but don't do this 99999 times in
case the folder is not writable */
i += 797;
continue;
}
#endif
/* Else fall through and return the original error. */
}
if (file)
*file = NULL;
if (unique_path)
*unique_path = NULL;
return svn_error_wrap_apr(apr_err, _("Can't open '%s'"),
svn_dirent_local_style(unique_name,
scratch_pool));
}
else
{
if (delete_when == svn_io_file_del_on_pool_cleanup)
baton->fname_apr = apr_pstrdup(result_pool, unique_name_apr);
if (file)
*file = try_file;
else
apr_file_close(try_file);
if (unique_path)
*unique_path = apr_pstrdup(result_pool, unique_name);
return SVN_NO_ERROR;
}
}
if (file)
*file = NULL;
if (unique_path)
*unique_path = NULL;
return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
NULL,
_("Unable to make name for '%s'"),
svn_dirent_local_style(path, scratch_pool));
}
svn_error_t *
svn_io_create_unique_link(const char **unique_name_p,
const char *path,
const char *dest,
const char *suffix,
apr_pool_t *pool)
{
#ifdef HAVE_SYMLINK
unsigned int i;
const char *unique_name;
const char *unique_name_apr;
const char *dest_apr;
int rv;
SVN_ERR(cstring_from_utf8(&dest_apr, dest, pool));
for (i = 1; i <= 99999; i++)
{
apr_status_t apr_err;
/* Special case the first attempt -- if we can avoid having a
generated numeric portion at all, that's best. So first we
try with just the suffix; then future tries add a number
before the suffix. (A do-while loop could avoid the repeated
conditional, but it's not worth the clarity loss.)
If the first attempt fails, the first number will be "2".
This is good, since "1" would misleadingly imply that
the second attempt was actually the first... and if someone's
got conflicts on their conflicts, we probably don't want to
add to their confusion :-). */
if (i == 1)
unique_name = apr_psprintf(pool, "%s%s", path, suffix);
else
unique_name = apr_psprintf(pool, "%s.%u%s", path, i, suffix);
/* Hmmm. Ideally, we would append to a native-encoding buf
before starting iteration, then convert back to UTF-8 for
return. But I suppose that would make the appending code
sensitive to i18n in a way it shouldn't be... Oh well. */
SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name, pool));
do {
rv = symlink(dest_apr, unique_name_apr);
} while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
apr_err = apr_get_os_error();
if (rv == -1 && APR_STATUS_IS_EEXIST(apr_err))
continue;
else if (rv == -1 && apr_err)
{
/* On Win32, CreateFile fails with an "Access Denied" error
code, rather than "File Already Exists", if the colliding
name belongs to a directory. */
if (APR_STATUS_IS_EACCES(apr_err))
{
apr_finfo_t finfo;
apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr,
APR_FINFO_TYPE, pool);
if (!apr_err_2
&& (finfo.filetype == APR_DIR))
continue;
/* Else ignore apr_err_2; better to fall through and
return the original error. */
}
*unique_name_p = NULL;
return svn_error_wrap_apr(apr_err,
_("Can't create symbolic link '%s'"),
svn_dirent_local_style(unique_name, pool));
}
else
{
*unique_name_p = unique_name;
return SVN_NO_ERROR;
}
}
*unique_name_p = NULL;
return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
NULL,
_("Unable to make name for '%s'"),
svn_dirent_local_style(path, pool));
#else
return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Symbolic links are not supported on this "
"platform"));
#endif
}
svn_error_t *
svn_io_read_link(svn_string_t **dest,
const char *path,
apr_pool_t *pool)
{
#ifdef HAVE_READLINK
svn_string_t dest_apr;
const char *path_apr;
char buf[1025];
ssize_t rv;
SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
do {
rv = readlink(path_apr, buf, sizeof(buf) - 1);
} while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
if (rv == -1)
return svn_error_wrap_apr(apr_get_os_error(),
_("Can't read contents of link"));
buf[rv] = '\0';
dest_apr.data = buf;
dest_apr.len = rv;
/* ### Cast needed, one of these interfaces is wrong */
return svn_utf_string_to_utf8((const svn_string_t **)dest, &dest_apr, pool);
#else
return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Symbolic links are not supported on this "
"platform"));
#endif
}
svn_error_t *
svn_io_copy_link(const char *src,
const char *dst,
apr_pool_t *pool)
{
#ifdef HAVE_READLINK
svn_string_t *link_dest;
const char *dst_tmp;
/* Notice what the link is pointing at... */
SVN_ERR(svn_io_read_link(&link_dest, src, pool));
/* Make a tmp-link pointing at the same thing. */
SVN_ERR(svn_io_create_unique_link(&dst_tmp, dst, link_dest->data,
".tmp", pool));
/* Move the tmp-link to link. */
return svn_io_file_rename(dst_tmp, dst, pool);
#else
return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Symbolic links are not supported on this "
"platform"));
#endif
}
/* Temporary directory name cache for svn_io_temp_dir() */
static volatile svn_atomic_t temp_dir_init_state = 0;
static const char *temp_dir;
/* Helper function to initialize temp dir. Passed to svn_atomic__init_once */
static svn_error_t *
init_temp_dir(void *baton, apr_pool_t *scratch_pool)
{
/* Global pool for the temp path */
apr_pool_t *global_pool = svn_pool_create(NULL);
const char *dir;
apr_status_t apr_err = apr_temp_dir_get(&dir, scratch_pool);
if (apr_err)
return svn_error_wrap_apr(apr_err, _("Can't find a temporary directory"));
SVN_ERR(cstring_to_utf8(&dir, dir, scratch_pool));
dir = svn_dirent_internal_style(dir, scratch_pool);
SVN_ERR(svn_dirent_get_absolute(&temp_dir, dir, global_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_io_temp_dir(const char **dir,
apr_pool_t *pool)
{
SVN_ERR(svn_atomic__init_once(&temp_dir_init_state,
init_temp_dir, NULL, pool));
*dir = apr_pstrdup(pool, temp_dir);
return SVN_NO_ERROR;
}
/*** Creating, copying and appending files. ***/
/* Transfer the contents of FROM_FILE to TO_FILE, using POOL for temporary
* allocations.
*
* NOTE: We don't use apr_copy_file() for this, since it takes filenames
* as parameters. Since we want to copy to a temporary file
* and rename for atomicity (see below), this would require an extra
* close/open pair, which can be expensive, especially on
* remote file systems.
*/
static apr_status_t
copy_contents(apr_file_t *from_file,
apr_file_t *to_file,
apr_pool_t *pool)
{
/* Copy bytes till the cows come home. */
while (1)
{
char buf[SVN__STREAM_CHUNK_SIZE];
apr_size_t bytes_this_time = sizeof(buf);
apr_status_t read_err;
apr_status_t write_err;
/* Read 'em. */
read_err = apr_file_read(from_file, buf, &bytes_this_time);
if (read_err && !APR_STATUS_IS_EOF(read_err))
{
return read_err;
}
/* Write 'em. */
write_err = apr_file_write_full(to_file, buf, bytes_this_time, NULL);
if (write_err)
{
return write_err;
}
if (read_err && APR_STATUS_IS_EOF(read_err))
{
/* Return the results of this close: an error, or success. */
return APR_SUCCESS;
}
}
/* NOTREACHED */
}
svn_error_t *
svn_io_copy_file(const char *src,
const char *dst,
svn_boolean_t copy_perms,
apr_pool_t *pool)
{
apr_file_t *from_file, *to_file;
apr_status_t apr_err;
const char *dst_tmp;
svn_error_t *err;
/* ### NOTE: sometimes src == dst. In this case, because we copy to a
### temporary file, and then rename over the top of the destination,
### the net result is resetting the permissions on src/dst.
###
### Note: specifically, this can happen during a switch when the desired
### permissions for a file change from one branch to another. See
### switch_tests 17.
###
### ... yes, we should avoid copying to the same file, and we should
### make the "reset perms" explicit. The switch *happens* to work
### because of this copy-to-temp-then-rename implementation. If it
### weren't for that, the switch would break.
*/
#ifdef CHECK_FOR_SAME_FILE
if (strcmp(src, dst) == 0)
return SVN_NO_ERROR;
#endif
SVN_ERR(svn_io_file_open(&from_file, src, APR_READ,
APR_OS_DEFAULT, pool));
/* For atomicity, we copy to a tmp file and then rename the tmp
file over the real destination. */
SVN_ERR(svn_io_open_unique_file3(&to_file, &dst_tmp,
svn_dirent_dirname(dst, pool),
svn_io_file_del_none, pool, pool));
apr_err = copy_contents(from_file, to_file, pool);
if (apr_err)
{
err = svn_error_wrap_apr(apr_err, _("Can't copy '%s' to '%s'"),
svn_dirent_local_style(src, pool),
svn_dirent_local_style(dst_tmp, pool));
}
else
err = NULL;
err = svn_error_compose_create(err,
svn_io_file_close(from_file, pool));
err = svn_error_compose_create(err,
svn_io_file_close(to_file, pool));
if (err)
{
return svn_error_compose_create(
err,
svn_io_remove_file2(dst_tmp, TRUE, pool));
}
/* If copying perms, set the perms on dst_tmp now, so they will be
atomically inherited in the upcoming rename. But note that we
had to wait until now to set perms, because if they say
read-only, then we'd have failed filling dst_tmp's contents. */
if (copy_perms)
SVN_ERR(svn_io_copy_perms(src, dst_tmp, pool));
return svn_error_trace(svn_io_file_rename(dst_tmp, dst, pool));
}
#if !defined(WIN32) && !defined(__OS2__)
/* Wrapper for apr_file_perms_set(), taking a UTF8-encoded filename. */
static svn_error_t *
file_perms_set(const char *fname, apr_fileperms_t perms,
apr_pool_t *pool)
{
const char *fname_apr;
apr_status_t status;
SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
status = apr_file_perms_set(fname_apr, perms);
if (status)
return svn_error_wrap_apr(status, _("Can't set permissions on '%s'"),
fname);
else
return SVN_NO_ERROR;
}
/* Set permissions PERMS on the FILE. This is a cheaper variant of the
* file_perms_set wrapper() function because no locale-dependent string
* conversion is required. POOL will be used for allocations.
*/
static svn_error_t *
file_perms_set2(apr_file_t* file, apr_fileperms_t perms, apr_pool_t *pool)
{
const char *fname_apr;
apr_status_t status;
status = apr_file_name_get(&fname_apr, file);
if (status)
return svn_error_wrap_apr(status, _("Can't get file name"));
status = apr_file_perms_set(fname_apr, perms);
if (status)
return svn_error_wrap_apr(status, _("Can't set permissions on '%s'"),
try_utf8_from_internal_style(fname_apr, pool));
else
return SVN_NO_ERROR;
}
#endif /* !WIN32 && !__OS2__ */
svn_error_t *
svn_io_copy_perms(const char *src,
const char *dst,
apr_pool_t *pool)
{
/* ### On Windows or OS/2, apr_file_perms_set always returns APR_ENOTIMPL,
and the path passed to apr_file_perms_set must be encoded
in the platform-specific path encoding; not necessary UTF-8.
We need a platform-specific implementation to get the
permissions right. */
#if !defined(WIN32) && !defined(__OS2__)
{
apr_finfo_t finfo;
svn_node_kind_t kind;
svn_boolean_t is_special;
svn_error_t *err;
/* If DST is a symlink, don't bother copying permissions. */
SVN_ERR(svn_io_check_special_path(dst, &kind, &is_special, pool));
if (is_special)
return SVN_NO_ERROR;
SVN_ERR(svn_io_stat(&finfo, src, APR_FINFO_PROT, pool));
err = file_perms_set(dst, finfo.protection, pool);
if (err)
{
/* We shouldn't be able to get APR_INCOMPLETE or APR_ENOTIMPL
here under normal circumstances, because the perms themselves
came from a call to apr_file_info_get(), and we already know
this is the non-Win32 case. But if it does happen, it's not
an error. */
if (APR_STATUS_IS_INCOMPLETE(err->apr_err) ||
APR_STATUS_IS_ENOTIMPL(err->apr_err))
svn_error_clear(err);
else
{
const char *message;
message = apr_psprintf(pool, _("Can't set permissions on '%s'"),
svn_dirent_local_style(dst, pool));
return svn_error_quick_wrap(err, message);
}
}
}
#endif /* !WIN32 && !__OS2__ */
return SVN_NO_ERROR;
}
svn_error_t *
svn_io_append_file(const char *src, const char *dst, apr_pool_t *pool)
{
apr_status_t apr_err;
const char *src_apr, *dst_apr;
SVN_ERR(cstring_from_utf8(&src_apr, src, pool));
SVN_ERR(cstring_from_utf8(&dst_apr, dst, pool));
apr_err = apr_file_append(src_apr, dst_apr, APR_OS_DEFAULT, pool);
if (apr_err)
return svn_error_wrap_apr(apr_err, _("Can't append '%s' to '%s'"),
svn_dirent_local_style(src, pool),
svn_dirent_local_style(dst, pool));
return SVN_NO_ERROR;
}
svn_error_t *svn_io_copy_dir_recursively(const char *src,
const char *dst_parent,
const char *dst_basename,
svn_boolean_t copy_perms,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
svn_node_kind_t kind;
apr_status_t status;
const char *dst_path;
apr_dir_t *this_dir;
apr_finfo_t this_entry;
apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME;
/* Make a subpool for recursion */
apr_pool_t *subpool = svn_pool_create(pool);
/* The 'dst_path' is simply dst_parent/dst_basename */
dst_path = svn_dirent_join(dst_parent, dst_basename, pool);
/* Sanity checks: SRC and DST_PARENT are directories, and
DST_BASENAME doesn't already exist in DST_PARENT. */
SVN_ERR(svn_io_check_path(src, &kind, subpool));
if (kind != svn_node_dir)
return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
_("Source '%s' is not a directory"),
svn_dirent_local_style(src, pool));
SVN_ERR(svn_io_check_path(dst_parent, &kind, subpool));
if (kind != svn_node_dir)
return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
_("Destination '%s' is not a directory"),
svn_dirent_local_style(dst_parent, pool));
SVN_ERR(svn_io_check_path(dst_path, &kind, subpool));
if (kind != svn_node_none)
return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,
_("Destination '%s' already exists"),
svn_dirent_local_style(dst_path, pool));
/* Create the new directory. */
/* ### TODO: copy permissions (needs apr_file_attrs_get()) */
SVN_ERR(svn_io_dir_make(dst_path, APR_OS_DEFAULT, pool));
/* Loop over the dirents in SRC. ('.' and '..' are auto-excluded) */
SVN_ERR(svn_io_dir_open(&this_dir, src, subpool));
for (status = apr_dir_read(&this_entry, flags, this_dir);
status == APR_SUCCESS;
status = apr_dir_read(&this_entry, flags, this_dir))
{
if ((this_entry.name[0] == '.')
&& ((this_entry.name[1] == '\0')
|| ((this_entry.name[1] == '.')
&& (this_entry.name[2] == '\0'))))
{
continue;
}
else
{
const char *src_target, *entryname_utf8;
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
SVN_ERR(entry_name_to_utf8(&entryname_utf8, this_entry.name,
src, subpool));
src_target = svn_dirent_join(src, entryname_utf8, subpool);
if (this_entry.filetype == APR_REG) /* regular file */
{
const char *dst_target = svn_dirent_join(dst_path,
entryname_utf8,
subpool);
SVN_ERR(svn_io_copy_file(src_target, dst_target,
copy_perms, subpool));
}
else if (this_entry.filetype == APR_LNK) /* symlink */
{
const char *dst_target = svn_dirent_join(dst_path,
entryname_utf8,
subpool);
SVN_ERR(svn_io_copy_link(src_target, dst_target,
subpool));
}
else if (this_entry.filetype == APR_DIR) /* recurse */
{
/* Prevent infinite recursion by filtering off our
newly created destination path. */
if (strcmp(src, dst_parent) == 0
&& strcmp(entryname_utf8, dst_basename) == 0)
continue;
SVN_ERR(svn_io_copy_dir_recursively
(src_target,
dst_path,
entryname_utf8,
copy_perms,
cancel_func,
cancel_baton,
subpool));
}
/* ### support other APR node types someday?? */
}
}
if (! (APR_STATUS_IS_ENOENT(status)))
return svn_error_wrap_apr(status, _("Can't read directory '%s'"),
svn_dirent_local_style(src, pool));
status = apr_dir_close(this_dir);
if (status)
return svn_error_wrap_apr(status, _("Error closing directory '%s'"),
svn_dirent_local_style(src, pool));
/* Free any memory used by recursion */
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_io_make_dir_recursively(const char *path, apr_pool_t *pool)
{
const char *path_apr;
apr_status_t apr_err;
if (svn_path_is_empty(path))
/* Empty path (current dir) is assumed to always exist,
so we do nothing, per docs. */
return SVN_NO_ERROR;
SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
apr_err = apr_dir_make_recursive(path_apr, APR_OS_DEFAULT, pool);
WIN32_RETRY_LOOP(apr_err, apr_dir_make_recursive(path_apr,
APR_OS_DEFAULT, pool));
if (apr_err)
return svn_error_wrap_apr(apr_err, _("Can't make directory '%s'"),
svn_dirent_local_style(path, pool));
return SVN_NO_ERROR;
}
svn_error_t *svn_io_file_create(const char *file,
const char *contents,
apr_pool_t *pool)
{
apr_file_t *f;
apr_size_t written;
svn_error_t *err = SVN_NO_ERROR;
SVN_ERR(svn_io_file_open(&f, file,
(APR_WRITE | APR_CREATE | APR_EXCL),
APR_OS_DEFAULT,
pool));
if (contents && *contents)
err = svn_io_file_write_full(f, contents, strlen(contents),
&written, pool);
return svn_error_trace(
svn_error_compose_create(err,
svn_io_file_close(f, pool)));
}
svn_error_t *svn_io_dir_file_copy(const char *src_path,
const char *dest_path,
const char *file,
apr_pool_t *pool)
{
const char *file_dest_path = svn_dirent_join(dest_path, file, pool);
const char *file_src_path = svn_dirent_join(src_path, file, pool);
return svn_io_copy_file(file_src_path, file_dest_path, TRUE, pool);
}
/*** Modtime checking. ***/
svn_error_t *
svn_io_file_affected_time(apr_time_t *apr_time,
const char *path,
apr_pool_t *pool)
{
apr_finfo_t finfo;
SVN_ERR(svn_io_stat(&finfo, path, APR_FINFO_MIN | APR_FINFO_LINK, pool));
*apr_time = finfo.mtime;
return SVN_NO_ERROR;
}
svn_error_t *
svn_io_set_file_affected_time(apr_time_t apr_time,
const char *path,
apr_pool_t *pool)
{
apr_status_t status;
const char *native_path;
SVN_ERR(cstring_from_utf8(&native_path, path, pool));
status = apr_file_mtime_set(native_path, apr_time, pool);
if (status)
return svn_error_wrap_apr(status, _("Can't set access time of '%s'"),
svn_dirent_local_style(path, pool));
return SVN_NO_ERROR;
}
void
svn_io_sleep_for_timestamps(const char *path, apr_pool_t *pool)
{
apr_time_t now, then;
svn_error_t *err;
char *sleep_env_var;
sleep_env_var = getenv(SVN_SLEEP_ENV_VAR);
if (sleep_env_var && apr_strnatcasecmp(sleep_env_var, "yes") == 0)
return; /* Allow skipping for testing */
now = apr_time_now();
/* Calculate 0.02 seconds after the next second wallclock tick. */
then = apr_time_make(apr_time_sec(now) + 1, APR_USEC_PER_SEC / 50);
/* Worst case is waiting one second, so we can use that time to determine
if we can sleep shorter than that */
if (path)
{
apr_finfo_t finfo;
err = svn_io_stat(&finfo, path, APR_FINFO_MTIME | APR_FINFO_LINK, pool);
if (err)
{
svn_error_clear(err); /* Fall back on original behavior */
}
else if (finfo.mtime % APR_USEC_PER_SEC)
{
/* Very simplistic but safe approach:
If the filesystem has < sec mtime we can be reasonably sure
that the filesystem has some sub-second resolution. On Windows
it is likely to be sub-millisecond; on Linux systems it depends
on the filesystem, ext4 is typically 1ms, 4ms or 10ms resolution.
## Perhaps find a better algorithm here. This will fail once
in every 1000 cases on a millisecond precision filesystem
if the mtime happens to be an exact second.
But better to fail once in every thousand cases than every
time, like we did before.
Note for further research on algorithm:
FAT32 has < 1 sec precision on ctime, but 2 sec on mtime.
Linux/ext4 with CONFIG_HZ=250 has high resolution
apr_time_now and although the filesystem timestamps
have similar high precision they are only updated with
a coarser 4ms resolution. */
/* 10 milliseconds after now. */
#ifndef SVN_HI_RES_SLEEP_MS
#define SVN_HI_RES_SLEEP_MS 10
#endif
then = now + apr_time_from_msec(SVN_HI_RES_SLEEP_MS);
}
/* Remove time taken to do stat() from sleep. */
now = apr_time_now();
}
if (now >= then)
return; /* Passing negative values may suspend indefinitely (Windows) */
/* (t < 1000 will be round to 0 in apr) */
if (then - now < 1000)
apr_sleep(1000);
else
apr_sleep(then - now);
}
svn_error_t *
svn_io_filesizes_different_p(svn_boolean_t *different_p,
const char *file1,
const char *file2,
apr_pool_t *pool)
{
apr_finfo_t finfo1;
apr_finfo_t finfo2;
apr_status_t status;
const char *file1_apr, *file2_apr;
/* Not using svn_io_stat() because don't want to generate
svn_error_t objects for non-error conditions. */
SVN_ERR(cstring_from_utf8(&file1_apr, file1, pool));
SVN_ERR(cstring_from_utf8(&file2_apr, file2, pool));
/* Stat both files */
status = apr_stat(&finfo1, file1_apr, APR_FINFO_MIN, pool);
if (status)
{
/* If we got an error stat'ing a file, it could be because the
file was removed... or who knows. Whatever the case, we
don't know if the filesizes are definitely different, so
assume that they're not. */
*different_p = FALSE;
return SVN_NO_ERROR;
}
status = apr_stat(&finfo2, file2_apr, APR_FINFO_MIN, pool);
if (status)
{
/* See previous comment. */
*different_p = FALSE;
return SVN_NO_ERROR;
}
/* Examine file sizes */
if (finfo1.size == finfo2.size)
*different_p = FALSE;
else
*different_p = TRUE;
return SVN_NO_ERROR;
}
svn_error_t *
svn_io_filesizes_three_different_p(svn_boolean_t *different_p12,
svn_boolean_t *different_p23,
svn_boolean_t *different_p13,
const char *file1,
const char *file2,
const char *file3,
apr_pool_t *scratch_pool)
{
apr_finfo_t finfo1, finfo2, finfo3;
apr_status_t status1, status2, status3;
const char *file1_apr, *file2_apr, *file3_apr;
/* Not using svn_io_stat() because don't want to generate
svn_error_t objects for non-error conditions. */
SVN_ERR(cstring_from_utf8(&file1_apr, file1, scratch_pool));
SVN_ERR(cstring_from_utf8(&file2_apr, file2, scratch_pool));
SVN_ERR(cstring_from_utf8(&file3_apr, file3, scratch_pool));
/* Stat all three files */
status1 = apr_stat(&finfo1, file1_apr, APR_FINFO_MIN, scratch_pool);
status2 = apr_stat(&finfo2, file2_apr, APR_FINFO_MIN, scratch_pool);
status3 = apr_stat(&finfo3, file3_apr, APR_FINFO_MIN, scratch_pool);
/* If we got an error stat'ing a file, it could be because the
file was removed... or who knows. Whatever the case, we
don't know if the filesizes are definitely different, so
assume that they're not. */
*different_p12 = !status1 && !status2 && finfo1.size != finfo2.size;
*different_p23 = !status2 && !status3 && finfo2.size != finfo3.size;
*different_p13 = !status1 && !status3 && finfo1.size != finfo3.size;
return SVN_NO_ERROR;
}
svn_error_t *
svn_io_file_checksum2(svn_checksum_t **checksum,
const char *file,
svn_checksum_kind_t kind,
apr_pool_t *pool)
{
svn_stream_t *file_stream;
svn_stream_t *checksum_stream;
apr_file_t* f;
SVN_ERR(svn_io_file_open(&f, file, APR_READ, APR_OS_DEFAULT, pool));
file_stream = svn_stream_from_aprfile2(f, FALSE, pool);
checksum_stream = svn_stream_checksummed2(file_stream, checksum, NULL, kind,
TRUE, pool);
/* Because the checksummed stream will force the reading (and
checksumming) of all the file's bytes, we can just close the stream
and let its magic work. */
return svn_stream_close(checksum_stream);
}
svn_error_t *
svn_io_file_checksum(unsigned char digest[],
const char *file,
apr_pool_t *pool)
{
svn_checksum_t *checksum;
SVN_ERR(svn_io_file_checksum2(&checksum, file, svn_checksum_md5, pool));
memcpy(digest, checksum->digest, APR_MD5_DIGESTSIZE);
return SVN_NO_ERROR;
}
/*** Permissions and modes. ***/
#if !defined(WIN32) && !defined(__OS2__)
/* Given the file specified by PATH, attempt to create an
identical version of it owned by the current user. This is done by
moving it to a temporary location, copying the file back to its old
path, then deleting the temporarily moved version. All temporary
allocations are done in POOL. */
static svn_error_t *
reown_file(const char *path,
apr_pool_t *pool)
{
const char *unique_name;
SVN_ERR(svn_io_open_unique_file3(NULL, &unique_name,
svn_dirent_dirname(path, pool),
svn_io_file_del_none, pool, pool));
SVN_ERR(svn_io_file_rename(path, unique_name, pool));
SVN_ERR(svn_io_copy_file(unique_name, path, TRUE, pool));
return svn_error_trace(svn_io_remove_file2(unique_name, FALSE, pool));
}
/* Determine what the PERMS for a new file should be by looking at the
permissions of a temporary file that we create.
Unfortunately, umask() as defined in POSIX provides no thread-safe way
to get at the current value of the umask, so what we're doing here is
the only way we have to determine which combination of write bits
(User/Group/World) should be set by default.
Make temporary allocations in SCRATCH_POOL. */
static svn_error_t *
get_default_file_perms(apr_fileperms_t *perms, apr_pool_t *scratch_pool)
{
/* the default permissions as read from the temp folder */
static apr_fileperms_t default_perms = 0;
/* Technically, this "racy": Multiple threads may use enter here and
try to figure out the default permission concurrently. That's fine
since they will end up with the same results. Even more technical,
apr_fileperms_t is an atomic type on 32+ bit machines.
*/
if (default_perms == 0)
{
apr_finfo_t finfo;
apr_file_t *fd;
const char *fname_base, *fname;
apr_uint32_t randomish;
svn_error_t *err;
/* Get the perms for a newly created file to find out what bits
should be set.
Explictly delete the file because we want this file to be as
short-lived as possible since its presence means other
processes may have to try multiple names.
Using svn_io_open_uniquely_named() here because other tempfile
creation functions tweak the permission bits of files they create.
*/
randomish = ((apr_uint32_t)(apr_uintptr_t)scratch_pool
+ (apr_uint32_t)apr_time_now());
fname_base = apr_psprintf(scratch_pool, "svn-%08x", randomish);
SVN_ERR(svn_io_open_uniquely_named(&fd, &fname, NULL, fname_base,
NULL, svn_io_file_del_none,
scratch_pool, scratch_pool));
err = svn_io_file_info_get(&finfo, APR_FINFO_PROT, fd, scratch_pool);
err = svn_error_compose_create(err, svn_io_file_close(fd, scratch_pool));
err = svn_error_compose_create(err, svn_io_remove_file2(fname, TRUE,
scratch_pool));
SVN_ERR(err);
*perms = finfo.protection;
default_perms = finfo.protection;
}
else
*perms = default_perms;
return SVN_NO_ERROR;
}
/* OR together permission bits of the file FD and the default permissions
of a file as determined by get_default_file_perms(). Do temporary
allocations in SCRATCH_POOL. */
static svn_error_t *
merge_default_file_perms(apr_file_t *fd, apr_fileperms_t *perms,
apr_pool_t *scratch_pool)
{
apr_finfo_t finfo;
apr_fileperms_t default_perms;
SVN_ERR(get_default_file_perms(&default_perms, scratch_pool));
SVN_ERR(svn_io_file_info_get(&finfo, APR_FINFO_PROT, fd, scratch_pool));
/* Glom the perms together. */
*perms = default_perms | finfo.protection;
return SVN_NO_ERROR;
}
/* This is a helper function for the svn_io_set_file_read* functions
that attempts to honor the users umask when dealing with
permission changes. It is a no-op when invoked on a symlink. */
static svn_error_t *
io_set_file_perms(const char *path,
svn_boolean_t change_readwrite,
svn_boolean_t enable_write,
svn_boolean_t change_executable,
svn_boolean_t executable,
svn_boolean_t ignore_enoent,
apr_pool_t *pool)
{
apr_status_t status;
const char *path_apr;
apr_finfo_t finfo;
apr_fileperms_t perms_to_set;
SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
/* Try to change only a minimal amount of the perms first
by getting the current perms and adding bits
only on where read perms are granted. If this fails
fall through to just setting file attributes. */
status = apr_stat(&finfo, path_apr, APR_FINFO_PROT | APR_FINFO_LINK, pool);
if (status)
{
if (ignore_enoent && APR_STATUS_IS_ENOENT(status))
return SVN_NO_ERROR;
else if (status != APR_ENOTIMPL)
return svn_error_wrap_apr(status,
_("Can't change perms of file '%s'"),
svn_dirent_local_style(path, pool));
return SVN_NO_ERROR;
}
if (finfo.filetype == APR_LNK)
return SVN_NO_ERROR;
perms_to_set = finfo.protection;
if (change_readwrite)
{
if (enable_write) /* Make read-write. */
{
/* Tweak the owner bits only. The group/other bits aren't safe to
* touch because we may end up setting them in undesired ways. */
perms_to_set |= (APR_UREAD|APR_UWRITE);
}
else
{
if (finfo.protection & APR_UREAD)
perms_to_set &= ~APR_UWRITE;
if (finfo.protection & APR_GREAD)
perms_to_set &= ~APR_GWRITE;
if (finfo.protection & APR_WREAD)
perms_to_set &= ~APR_WWRITE;
}
}
if (change_executable)
{
if (executable)
{
if (finfo.protection & APR_UREAD)
perms_to_set |= APR_UEXECUTE;
if (finfo.protection & APR_GREAD)
perms_to_set |= APR_GEXECUTE;
if (finfo.protection & APR_WREAD)
perms_to_set |= APR_WEXECUTE;
}
else
{
if (finfo.protection & APR_UREAD)
perms_to_set &= ~APR_UEXECUTE;
if (finfo.protection & APR_GREAD)
perms_to_set &= ~APR_GEXECUTE;
if (finfo.protection & APR_WREAD)
perms_to_set &= ~APR_WEXECUTE;
}
}
/* If we aren't changing anything then just return, this saves
some system calls and helps with shared working copies */
if (perms_to_set == finfo.protection)
return SVN_NO_ERROR;
status = apr_file_perms_set(path_apr, perms_to_set);
if (!status)
return SVN_NO_ERROR;
if (APR_STATUS_IS_EPERM(status))
{
/* We don't have permissions to change the
permissions! Try a move, copy, and delete
workaround to see if we can get the file owned by
us. If these succeed, try the permissions set
again.
Note that we only attempt this in the
stat-available path. This assumes that the
move-copy workaround will only be helpful on
platforms that implement apr_stat. */
SVN_ERR(reown_file(path, pool));
status = apr_file_perms_set(path_apr, perms_to_set);
}
if (!status)
return SVN_NO_ERROR;
if (ignore_enoent && APR_STATUS_IS_ENOENT(status))
return SVN_NO_ERROR;
else if (status == APR_ENOTIMPL)
{
/* At least try to set the attributes. */
apr_fileattrs_t attrs = 0;
apr_fileattrs_t attrs_values = 0;
if (change_readwrite)
{
attrs = APR_FILE_ATTR_READONLY;
if (!enable_write)
attrs_values = APR_FILE_ATTR_READONLY;
}
if (change_executable)
{
attrs = APR_FILE_ATTR_EXECUTABLE;
if (executable)
attrs_values = APR_FILE_ATTR_EXECUTABLE;
}
status = apr_file_attrs_set(path_apr, attrs, attrs_values, pool);
}
return svn_error_wrap_apr(status,
_("Can't change perms of file '%s'"),
svn_dirent_local_style(path, pool));
}
#endif /* !WIN32 && !__OS2__ */
#ifdef WIN32
#if APR_HAS_UNICODE_FS
/* copy of the apr function utf8_to_unicode_path since apr doesn't export this one */
static apr_status_t io_utf8_to_unicode_path(apr_wchar_t* retstr, apr_size_t retlen,
const char* srcstr)
{
/* TODO: The computations could preconvert the string to determine
* the true size of the retstr, but that's a memory over speed
* tradeoff that isn't appropriate this early in development.
*
* Allocate the maximum string length based on leading 4
* characters of \\?\ (allowing nearly unlimited path lengths)
* plus the trailing null, then transform /'s into \\'s since
* the \\?\ form doesn't allow '/' path separators.
*
* Note that the \\?\ form only works for local drive paths, and
* \\?\UNC\ is needed UNC paths.
*/
apr_size_t srcremains = strlen(srcstr) + 1;
apr_wchar_t *t = retstr;
apr_status_t rv;
/* This is correct, we don't twist the filename if it will
* definitely be shorter than 248 characters. It merits some
* performance testing to see if this has any effect, but there
* seem to be applications that get confused by the resulting
* Unicode \\?\ style file names, especially if they use argv[0]
* or call the Win32 API functions such as GetModuleName, etc.
* Not every application is prepared to handle such names.
*
* Note also this is shorter than MAX_PATH, as directory paths
* are actually limited to 248 characters.
*
* Note that a utf-8 name can never result in more wide chars
* than the original number of utf-8 narrow chars.
*/
if (srcremains > 248) {
if (srcstr[1] == ':' && (srcstr[2] == '/' || srcstr[2] == '\\')) {
wcscpy (retstr, L"\\\\?\\");
retlen -= 4;
t += 4;
}
else if ((srcstr[0] == '/' || srcstr[0] == '\\')
&& (srcstr[1] == '/' || srcstr[1] == '\\')
&& (srcstr[2] != '?')) {
/* Skip the slashes */
srcstr += 2;
srcremains -= 2;
wcscpy (retstr, L"\\\\?\\UNC\\");
retlen -= 8;
t += 8;
}
}
if (rv = apr_conv_utf8_to_ucs2(srcstr, &srcremains, t, &retlen)) {
return (rv == APR_INCOMPLETE) ? APR_EINVAL : rv;
}
if (srcremains) {
return APR_ENAMETOOLONG;
}
for (; *t; ++t)
if (*t == L'/')
*t = L'\\';
return APR_SUCCESS;
}
#endif
static apr_status_t io_win_file_attrs_set(const char *fname,
DWORD attributes,
DWORD attr_mask,
apr_pool_t *pool)
{
/* this is an implementation of apr_file_attrs_set() but one
that uses the proper Windows attributes instead of the apr
attributes. This way, we can apply any Windows file and
folder attributes even if apr doesn't implement them */
DWORD flags;
apr_status_t rv;
#if APR_HAS_UNICODE_FS
apr_wchar_t wfname[APR_PATH_MAX];
#endif
#if APR_HAS_UNICODE_FS
IF_WIN_OS_IS_UNICODE
{
if (rv = io_utf8_to_unicode_path(wfname,
sizeof(wfname) / sizeof(wfname[0]),
fname))
return rv;
flags = GetFileAttributesW(wfname);
}
#endif
#if APR_HAS_ANSI_FS
ELSE_WIN_OS_IS_ANSI
{
flags = GetFileAttributesA(fname);
}
#endif
if (flags == 0xFFFFFFFF)
return apr_get_os_error();
flags &= ~attr_mask;
flags |= (attributes & attr_mask);
#if APR_HAS_UNICODE_FS
IF_WIN_OS_IS_UNICODE
{
rv = SetFileAttributesW(wfname, flags);
}
#endif
#if APR_HAS_ANSI_FS
ELSE_WIN_OS_IS_ANSI
{
rv = SetFileAttributesA(fname, flags);
}
#endif
if (rv == 0)
return apr_get_os_error();
return APR_SUCCESS;
}
#endif
svn_error_t *
svn_io_set_file_read_write_carefully(const char *path,
svn_boolean_t enable_write,
svn_boolean_t ignore_enoent,
apr_pool_t *pool)
{
if (enable_write)
return svn_io_set_file_read_write(path, ignore_enoent, pool);
return svn_io_set_file_read_only(path, ignore_enoent, pool);
}
svn_error_t *
svn_io_set_file_read_only(const char *path,
svn_boolean_t ignore_enoent,
apr_pool_t *pool)
{
/* On Windows and OS/2, just set the file attributes -- on unix call
our internal function which attempts to honor the umask. */
#if !defined(WIN32) && !defined(__OS2__)
return io_set_file_perms(path, TRUE, FALSE, FALSE, FALSE,
ignore_enoent, pool);
#else
apr_status_t status;
const char *path_apr;
SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
status = apr_file_attrs_set(path_apr,
APR_FILE_ATTR_READONLY,
APR_FILE_ATTR_READONLY,
pool);
if (status && status != APR_ENOTIMPL)
if (!ignore_enoent || !APR_STATUS_IS_ENOENT(status))
return svn_error_wrap_apr(status,
_("Can't set file '%s' read-only"),
svn_dirent_local_style(path, pool));
return SVN_NO_ERROR;
#endif
}
svn_error_t *
svn_io_set_file_read_write(const char *path,
svn_boolean_t ignore_enoent,
apr_pool_t *pool)
{
/* On Windows and OS/2, just set the file attributes -- on unix call
our internal function which attempts to honor the umask. */
#if !defined(WIN32) && !defined(__OS2__)
return io_set_file_perms(path, TRUE, TRUE, FALSE, FALSE,
ignore_enoent, pool);
#else
apr_status_t status;
const char *path_apr;
SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
status = apr_file_attrs_set(path_apr,
0,
APR_FILE_ATTR_READONLY,
pool);
if (status && status != APR_ENOTIMPL)
if (!ignore_enoent || !APR_STATUS_IS_ENOENT(status))
return svn_error_wrap_apr(status,
_("Can't set file '%s' read-write"),
svn_dirent_local_style(path, pool));
return SVN_NO_ERROR;
#endif
}
svn_error_t *
svn_io_set_file_executable(const char *path,
svn_boolean_t executable,
svn_boolean_t ignore_enoent,
apr_pool_t *pool)
{
/* On Windows and OS/2, just exit -- on unix call our internal function
which attempts to honor the umask. */
#if (!defined(WIN32) && !defined(__OS2__))
return io_set_file_perms(path, FALSE, FALSE, TRUE, executable,
ignore_enoent, pool);
#else
return SVN_NO_ERROR;
#endif
}
svn_error_t *
svn_io__is_finfo_read_only(svn_boolean_t *read_only,
apr_finfo_t *file_info,
apr_pool_t *pool)
{
#if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
apr_status_t apr_err;
apr_uid_t uid;
apr_gid_t gid;
*read_only = FALSE;
apr_err = apr_uid_current(&uid, &gid, pool);
if (apr_err)
return svn_error_wrap_apr(apr_err, _("Error getting UID of process"));
/* Check write bit for current user. */
if (apr_uid_compare(uid, file_info->user) == APR_SUCCESS)
*read_only = !(file_info->protection & APR_UWRITE);
else if (apr_gid_compare(gid, file_info->group) == APR_SUCCESS)
*read_only = !(file_info->protection & APR_GWRITE);
else
*read_only = !(file_info->protection & APR_WWRITE);
#else /* WIN32 || __OS2__ || !APR_HAS_USER */
*read_only = (file_info->protection & APR_FREADONLY);
#endif
return SVN_NO_ERROR;
}
svn_error_t *
svn_io__is_finfo_executable(svn_boolean_t *executable,
apr_finfo_t *file_info,
apr_pool_t *pool)
{
#if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
apr_status_t apr_err;
apr_uid_t uid;
apr_gid_t gid;
*executable = FALSE;
apr_err = apr_uid_current(&uid, &gid, pool);
if (apr_err)
return svn_error_wrap_apr(apr_err, _("Error getting UID of process"));
/* Check executable bit for current user. */
if (apr_uid_compare(uid, file_info->user) == APR_SUCCESS)
*executable = (file_info->protection & APR_UEXECUTE);
else if (apr_gid_compare(gid, file_info->group) == APR_SUCCESS)
*executable = (file_info->protection & APR_GEXECUTE);
else
*executable = (file_info->protection & APR_WEXECUTE);
#else /* WIN32 || __OS2__ || !APR_HAS_USER */
*executable = FALSE;
#endif
return SVN_NO_ERROR;
}
svn_error_t *
svn_io_is_file_executable(svn_boolean_t *executable,
const char *path,
apr_pool_t *pool)
{
#if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
apr_finfo_t file_info;
SVN_ERR(svn_io_stat(&file_info, path, APR_FINFO_PROT | APR_FINFO_OWNER,
pool));
SVN_ERR(svn_io__is_finfo_executable(executable, &file_info, pool));
#else /* WIN32 || __OS2__ || !APR_HAS_USER */
*executable = FALSE;
#endif
return SVN_NO_ERROR;
}
/*** File locking. ***/
#if !defined(WIN32) && !defined(__OS2__)
/* Clear all outstanding locks on ARG, an open apr_file_t *. */
static apr_status_t
file_clear_locks(void *arg)
{
apr_status_t apr_err;
apr_file_t *f = arg;
/* Remove locks. */
apr_err = apr_file_unlock(f);
if (apr_err)
return apr_err;
return 0;
}
#endif
svn_error_t *
svn_io_lock_open_file(apr_file_t *lockfile_handle,
svn_boolean_t exclusive,
svn_boolean_t nonblocking,
apr_pool_t *pool)
{
int locktype = APR_FLOCK_SHARED;
apr_status_t apr_err;
const char *fname;
if (exclusive)
locktype = APR_FLOCK_EXCLUSIVE;
if (nonblocking)
locktype |= APR_FLOCK_NONBLOCK;
/* We need this only in case of an error but this is cheap to get -
* so we do it here for clarity. */
apr_err = apr_file_name_get(&fname, lockfile_handle);
if (apr_err)
return svn_error_wrap_apr(apr_err, _("Can't get file name"));
/* Get lock on the filehandle. */
apr_err = apr_file_lock(lockfile_handle, locktype);
/* In deployments with two or more multithreaded servers running on
the same system serving two or more fsfs repositories it is
possible for a deadlock to occur when getting a write lock on
db/txn-current-lock:
Process 1 Process 2
--------- ---------
thread 1: get lock in repos A
thread 1: get lock in repos B
thread 2: block getting lock in repos A
thread 2: try to get lock in B *** deadlock ***
Retry for a while for the deadlock to clear. */
FILE_LOCK_RETRY_LOOP(apr_err, apr_file_lock(lockfile_handle, locktype));
if (apr_err)
{
switch (locktype & APR_FLOCK_TYPEMASK)
{
case APR_FLOCK_SHARED:
return svn_error_wrap_apr(apr_err,
_("Can't get shared lock on file '%s'"),
try_utf8_from_internal_style(fname, pool));
case APR_FLOCK_EXCLUSIVE:
return svn_error_wrap_apr(apr_err,
_("Can't get exclusive lock on file '%s'"),
try_utf8_from_internal_style(fname, pool));
default:
SVN_ERR_MALFUNCTION();
}
}
/* On Windows and OS/2 file locks are automatically released when
the file handle closes */
#if !defined(WIN32) && !defined(__OS2__)
apr_pool_cleanup_register(pool, lockfile_handle,
file_clear_locks,
apr_pool_cleanup_null);
#endif
return SVN_NO_ERROR;
}
svn_error_t *
svn_io_unlock_open_file(apr_file_t *lockfile_handle,
apr_pool_t *pool)
{
const char *fname;
apr_status_t apr_err;
/* We need this only in case of an error but this is cheap to get -
* so we do it here for clarity. */
apr_err = apr_file_name_get(&fname, lockfile_handle);
if (apr_err)
return svn_error_wrap_apr(apr_err, _("Can't get file name"));
/* The actual unlock attempt. */
apr_err = apr_file_unlock(lockfile_handle);
if (apr_err)
return svn_error_wrap_apr(apr_err, _("Can't unlock file '%s'"),
try_utf8_from_internal_style(fname, pool));
/* On Windows and OS/2 file locks are automatically released when
the file handle closes */
#if !defined(WIN32) && !defined(__OS2__)
apr_pool_cleanup_kill(pool, lockfile_handle, file_clear_locks);
#endif
return SVN_NO_ERROR;
}
svn_error_t *
svn_io_file_lock2(const char *lock_file,
svn_boolean_t exclusive,
svn_boolean_t nonblocking,
apr_pool_t *pool)
{
int locktype = APR_FLOCK_SHARED;
apr_file_t *lockfile_handle;
apr_int32_t flags;
if (exclusive)
locktype = APR_FLOCK_EXCLUSIVE;
flags = APR_READ;
if (locktype == APR_FLOCK_EXCLUSIVE)
flags |= APR_WRITE;
/* locktype is never read after this block, so we don't need to bother
setting it. If that were to ever change, uncomment the following
block.
if (nonblocking)
locktype |= APR_FLOCK_NONBLOCK;
*/
SVN_ERR(svn_io_file_open(&lockfile_handle, lock_file, flags,
APR_OS_DEFAULT,
pool));
/* Get lock on the filehandle. */
return svn_io_lock_open_file(lockfile_handle, exclusive, nonblocking, pool);
}
/* Data consistency/coherency operations. */
svn_error_t *svn_io_file_flush_to_disk(apr_file_t *file,
apr_pool_t *pool)
{
apr_os_file_t filehand;
/* First make sure that any user-space buffered data is flushed. */
SVN_ERR(do_io_file_wrapper_cleanup(file, apr_file_flush(file),
N_("Can't flush file '%s'"),
N_("Can't flush stream"),
pool));
apr_os_file_get(&filehand, file);
/* Call the operating system specific function to actually force the
data to disk. */
{
#ifdef WIN32
if (! FlushFileBuffers(filehand))
return svn_error_wrap_apr(apr_get_os_error(),
_("Can't flush file to disk"));
#else
int rv;
do {
rv = fsync(filehand);
} while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
/* If the file is in a memory filesystem, fsync() may return
EINVAL. Presumably the user knows the risks, and we can just
ignore the error. */
if (rv == -1 && APR_STATUS_IS_EINVAL(apr_get_os_error()))
return SVN_NO_ERROR;
if (rv == -1)
return svn_error_wrap_apr(apr_get_os_error(),
_("Can't flush file to disk"));
#endif
}
return SVN_NO_ERROR;
}
/* TODO write test for these two functions, then refactor. */
/* Set RESULT to an svn_stringbuf_t containing the contents of FILE.
FILENAME is the FILE's on-disk APR-safe name, or NULL if that name
isn't known. If CHECK_SIZE is TRUE, the function will attempt to
first stat() the file to determine it's size before sucking its
contents into the stringbuf. (Doing so can prevent unnecessary
memory usage, an unwanted side effect of the stringbuf growth and
reallocation mechanism.) */
static svn_error_t *
stringbuf_from_aprfile(svn_stringbuf_t **result,
const char *filename,
apr_file_t *file,
svn_boolean_t check_size,
apr_pool_t *pool)
{
apr_size_t len;
svn_error_t *err;
svn_stringbuf_t *res = NULL;
apr_size_t res_initial_len = SVN__STREAM_CHUNK_SIZE;
char *buf = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
/* If our caller wants us to check the size of the file for
efficient memory handling, we'll try to do so. */
if (check_size)
{
apr_status_t status;
/* If our caller didn't tell us the file's name, we'll ask APR
if it knows the name. No problem if we can't figure it out. */
if (! filename)
{
const char *filename_apr;
if (! (status = apr_file_name_get(&filename_apr, file)))
filename = filename_apr;
}
/* If we now know the filename, try to stat(). If we succeed,
we know how to allocate our stringbuf. */
if (filename)
{
apr_finfo_t finfo;
if (! (status = apr_stat(&finfo, filename, APR_FINFO_MIN, pool)))
res_initial_len = (apr_size_t)finfo.size;
}
}
/* XXX: We should check the incoming data for being of type binary. */
res = svn_stringbuf_create_ensure(res_initial_len, pool);
/* apr_file_read will not return data and eof in the same call. So this loop
* is safe from missing read data. */
len = SVN__STREAM_CHUNK_SIZE;
err = svn_io_file_read(file, buf, &len, pool);
while (! err)
{
svn_stringbuf_appendbytes(res, buf, len);
len = SVN__STREAM_CHUNK_SIZE;
err = svn_io_file_read(file, buf, &len, pool);
}
/* Having read all the data we *expect* EOF */
if (err && !APR_STATUS_IS_EOF(err->apr_err))
return err;
svn_error_clear(err);
*result = res;
return SVN_NO_ERROR;
}
svn_error_t *
svn_stringbuf_from_file2(svn_stringbuf_t **result,
const char *filename,
apr_pool_t *pool)
{
apr_file_t *f;
if (filename[0] == '-' && filename[1] == '\0')
{
apr_status_t apr_err;
if ((apr_err = apr_file_open_stdin(&f, pool)))
return svn_error_wrap_apr(apr_err, _("Can't open stdin"));
SVN_ERR(stringbuf_from_aprfile(result, NULL, f, FALSE, pool));
}
else
{
SVN_ERR(svn_io_file_open(&f, filename, APR_READ, APR_OS_DEFAULT, pool));
SVN_ERR(stringbuf_from_aprfile(result, filename, f, TRUE, pool));
}
return svn_io_file_close(f, pool);
}
svn_error_t *
svn_stringbuf_from_file(svn_stringbuf_t **result,
const char *filename,
apr_pool_t *pool)
{
if (filename[0] == '-' && filename[1] == '\0')
return svn_error_create
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Reading from stdin is disallowed"));
return svn_stringbuf_from_file2(result, filename, pool);
}
svn_error_t *
svn_stringbuf_from_aprfile(svn_stringbuf_t **result,
apr_file_t *file,
apr_pool_t *pool)
{
return stringbuf_from_aprfile(result, NULL, file, TRUE, pool);
}
/* Deletion. */
svn_error_t *
svn_io_remove_file2(const char *path,
svn_boolean_t ignore_enoent,
apr_pool_t *scratch_pool)
{
apr_status_t apr_err;
const char *path_apr;
SVN_ERR(cstring_from_utf8(&path_apr, path, scratch_pool));
apr_err = apr_file_remove(path_apr, scratch_pool);
if (!apr_err
|| (ignore_enoent
&& (APR_STATUS_IS_ENOENT(apr_err)
|| SVN__APR_STATUS_IS_ENOTDIR(apr_err))))
return SVN_NO_ERROR;
#ifdef WIN32
/* If the target is read only NTFS reports EACCESS and FAT/FAT32
reports EEXIST */
if (APR_STATUS_IS_EACCES(apr_err) || APR_STATUS_IS_EEXIST(apr_err))
{
/* Set the destination file writable because Windows will not
allow us to delete when path is read-only */
SVN_ERR(svn_io_set_file_read_write(path, ignore_enoent, scratch_pool));
apr_err = apr_file_remove(path_apr, scratch_pool);
if (!apr_err)
return SVN_NO_ERROR;
}
{
apr_status_t os_err = APR_TO_OS_ERROR(apr_err);
/* Check to make sure we aren't trying to delete a directory */
if (os_err == ERROR_ACCESS_DENIED || os_err == ERROR_SHARING_VIOLATION)
{
apr_finfo_t finfo;
if (!apr_stat(&finfo, path_apr, APR_FINFO_TYPE, scratch_pool)
&& finfo.filetype == APR_REG)
{
WIN32_RETRY_LOOP(apr_err, apr_file_remove(path_apr,
scratch_pool));
}
}
/* Just return the delete error */
}
#endif
if (apr_err)
return svn_error_wrap_apr(apr_err, _("Can't remove file '%s'"),
svn_dirent_local_style(path, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_io_remove_dir(const char *path, apr_pool_t *pool)
{
return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool);
}
/*
Mac OS X has a bug where if you're reading the contents of a
directory via readdir in a loop, and you remove one of the entries in
the directory and the directory has 338 or more files in it you will
skip over some of the entries in the directory. Needless to say,
this causes problems if you are using this kind of loop inside a
function that is recursively deleting a directory, because when you
get around to removing the directory it will still have something in
it. A similar problem has been observed in other BSDs. This bug has
since been fixed. See http://www.vnode.ch/fixing_seekdir for details.
The workaround is to delete the files only _after_ the initial
directory scan. A previous workaround involving rewinddir is
problematic on Win32 and some NFS clients, notably NetBSD.
See http://subversion.tigris.org/issues/show_bug.cgi?id=1896 and
http://subversion.tigris.org/issues/show_bug.cgi?id=3501.
*/
/* Neither windows nor unix allows us to delete a non-empty
directory.
This is a function to perform the equivalent of 'rm -rf'. */
svn_error_t *
svn_io_remove_dir2(const char *path, svn_boolean_t ignore_enoent,
svn_cancel_func_t cancel_func, void *cancel_baton,
apr_pool_t *pool)
{
svn_error_t *err;
apr_pool_t *subpool;
apr_hash_t *dirents;
apr_hash_index_t *hi;
/* Check for pending cancellation request.
If we need to bail out, do so early. */
if (cancel_func)
SVN_ERR((*cancel_func)(cancel_baton));
subpool = svn_pool_create(pool);
err = svn_io_get_dirents3(&dirents, path, TRUE, subpool, subpool);
if (err)
{
/* if the directory doesn't exist, our mission is accomplished */
if (ignore_enoent && APR_STATUS_IS_ENOENT(err->apr_err))
{
svn_error_clear(err);
return SVN_NO_ERROR;
}
return svn_error_trace(err);
}
for (hi = apr_hash_first(subpool, dirents); hi; hi = apr_hash_next(hi))
{
const char *name = svn__apr_hash_index_key(hi);
const svn_io_dirent2_t *dirent = svn__apr_hash_index_val(hi);
const char *fullpath;
fullpath = svn_dirent_join(path, name, subpool);
if (dirent->kind == svn_node_dir)
{
/* Don't check for cancellation, the callee will immediately do so */
SVN_ERR(svn_io_remove_dir2(fullpath, FALSE, cancel_func,
cancel_baton, subpool));
}
else
{
if (cancel_func)
SVN_ERR((*cancel_func)(cancel_baton));
err = svn_io_remove_file2(fullpath, FALSE, subpool);
if (err)
return svn_error_createf
(err->apr_err, err, _("Can't remove '%s'"),
svn_dirent_local_style(fullpath, subpool));
}
}
svn_pool_destroy(subpool);
return svn_io_dir_remove_nonrecursive(path, pool);
}
svn_error_t *
svn_io_get_dir_filenames(apr_hash_t **dirents,
const char *path,
apr_pool_t *pool)
{
return svn_error_trace(svn_io_get_dirents3(dirents, path, TRUE,
pool, pool));
}
svn_io_dirent2_t *
svn_io_dirent2_create(apr_pool_t *result_pool)
{
svn_io_dirent2_t *dirent = apr_pcalloc(result_pool, sizeof(*dirent));
/*dirent->kind = svn_node_none;
dirent->special = FALSE;*/
dirent->filesize = SVN_INVALID_FILESIZE;
/*dirent->mtime = 0;*/
return dirent;
}
svn_io_dirent2_t *
svn_io_dirent2_dup(const svn_io_dirent2_t *item,
apr_pool_t *result_pool)
{
return apr_pmemdup(result_pool,
item,
sizeof(*item));
}
svn_error_t *
svn_io_get_dirents3(apr_hash_t **dirents,
const char *path,
svn_boolean_t only_check_type,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_status_t status;
apr_dir_t *this_dir;
apr_finfo_t this_entry;
apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME;
if (!only_check_type)
flags |= APR_FINFO_SIZE | APR_FINFO_MTIME;
*dirents = apr_hash_make(result_pool);
SVN_ERR(svn_io_dir_open(&this_dir, path, scratch_pool));
for (status = apr_dir_read(&this_entry, flags, this_dir);
status == APR_SUCCESS;
status = apr_dir_read(&this_entry, flags, this_dir))
{
if ((this_entry.name[0] == '.')
&& ((this_entry.name[1] == '\0')
|| ((this_entry.name[1] == '.')
&& (this_entry.name[2] == '\0'))))
{
continue;
}
else
{
const char *name;
svn_io_dirent2_t *dirent = svn_io_dirent2_create(result_pool);
SVN_ERR(entry_name_to_utf8(&name, this_entry.name, path, result_pool));
map_apr_finfo_to_node_kind(&(dirent->kind),
&(dirent->special),
&this_entry);
if (!only_check_type)
{
dirent->filesize = this_entry.size;
dirent->mtime = this_entry.mtime;
}
svn_hash_sets(*dirents, name, dirent);
}
}
if (! (APR_STATUS_IS_ENOENT(status)))
return svn_error_wrap_apr(status, _("Can't read directory '%s'"),
svn_dirent_local_style(path, scratch_pool));
status = apr_dir_close(this_dir);
if (status)
return svn_error_wrap_apr(status, _("Error closing directory '%s'"),
svn_dirent_local_style(path, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_io_stat_dirent2(const svn_io_dirent2_t **dirent_p,
const char *path,
svn_boolean_t verify_truename,
svn_boolean_t ignore_enoent,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_finfo_t finfo;
svn_io_dirent2_t *dirent;
svn_error_t *err;
apr_int32_t wanted = APR_FINFO_TYPE | APR_FINFO_LINK
| APR_FINFO_SIZE | APR_FINFO_MTIME;
#if defined(WIN32) || defined(__OS2__)
if (verify_truename)
wanted |= APR_FINFO_NAME;
#endif
err = svn_io_stat(&finfo, path, wanted, scratch_pool);
if (err && ignore_enoent &&
(APR_STATUS_IS_ENOENT(err->apr_err)
|| SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
{
svn_error_clear(err);
dirent = svn_io_dirent2_create(result_pool);
SVN_ERR_ASSERT(dirent->kind == svn_node_none);
*dirent_p = dirent;
return SVN_NO_ERROR;
}
SVN_ERR(err);
#if defined(WIN32) || defined(__OS2__) || defined(DARWIN)
if (verify_truename)
{
const char *requested_name = svn_dirent_basename(path, NULL);
if (requested_name[0] == '\0')
{
/* No parent directory. No need to stat/verify */
}
#if defined(WIN32) || defined(__OS2__)
else if (finfo.name)
{
const char *name_on_disk;
SVN_ERR(entry_name_to_utf8(&name_on_disk, finfo.name, path,
scratch_pool));
if (strcmp(name_on_disk, requested_name) /* != 0 */)
{
if (ignore_enoent)
{
*dirent_p = svn_io_dirent2_create(result_pool);
return SVN_NO_ERROR;
}
else
return svn_error_createf(APR_ENOENT, NULL,
_("Path '%s' not found, case obstructed by '%s'"),
svn_dirent_local_style(path, scratch_pool),
name_on_disk);
}
}
#elif defined(DARWIN)
/* Currently apr doesn't set finfo.name on DARWIN, returning
APR_INCOMPLETE.
### Can we optimize this in another way? */
else
{
apr_hash_t *dirents;
err = svn_io_get_dirents3(&dirents,
svn_dirent_dirname(path, scratch_pool),
TRUE /* only_check_type */,
scratch_pool, scratch_pool);
if (err && ignore_enoent
&& (APR_STATUS_IS_ENOENT(err->apr_err)
|| SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
{
svn_error_clear(err);
*dirent_p = svn_io_dirent2_create(result_pool);
return SVN_NO_ERROR;
}
else
SVN_ERR(err);
if (! svn_hash_gets(dirents, requested_name))
{
if (ignore_enoent)
{
*dirent_p = svn_io_dirent2_create(result_pool);
return SVN_NO_ERROR;
}
else
return svn_error_createf(APR_ENOENT, NULL,
_("Path '%s' not found"),
svn_dirent_local_style(path, scratch_pool));
}
}
#endif
}
#endif
dirent = svn_io_dirent2_create(result_pool);
map_apr_finfo_to_node_kind(&(dirent->kind), &(dirent->special), &finfo);
dirent->filesize = finfo.size;
dirent->mtime = finfo.mtime;
*dirent_p = dirent;
return SVN_NO_ERROR;
}
/* Pool userdata key for the error file passed to svn_io_start_cmd(). */
#define ERRFILE_KEY "svn-io-start-cmd-errfile"
/* Handle an error from the child process (before command execution) by
printing DESC and the error string corresponding to STATUS to stderr. */
static void
handle_child_process_error(apr_pool_t *pool, apr_status_t status,
const char *desc)
{
char errbuf[256];
apr_file_t *errfile;
void *p;
/* We can't do anything if we get an error here, so just return. */
if (apr_pool_userdata_get(&p, ERRFILE_KEY, pool))
return;
errfile = p;
if (errfile)
/* What we get from APR is in native encoding. */
apr_file_printf(errfile, "%s: %s",
desc, apr_strerror(status, errbuf,
sizeof(errbuf)));
}
svn_error_t *
svn_io_start_cmd3(apr_proc_t *cmd_proc,
const char *path,
const char *cmd,
const char *const *args,
const char *const *env,
svn_boolean_t inherit,
svn_boolean_t infile_pipe,
apr_file_t *infile,
svn_boolean_t outfile_pipe,
apr_file_t *outfile,
svn_boolean_t errfile_pipe,
apr_file_t *errfile,
apr_pool_t *pool)
{
apr_status_t apr_err;
apr_procattr_t *cmdproc_attr;
int num_args;
const char **args_native;
const char *cmd_apr;
SVN_ERR_ASSERT(!((infile != NULL) && infile_pipe));
SVN_ERR_ASSERT(!((outfile != NULL) && outfile_pipe));
SVN_ERR_ASSERT(!((errfile != NULL) && errfile_pipe));
/* Create the process attributes. */
apr_err = apr_procattr_create(&cmdproc_attr, pool);
if (apr_err)
return svn_error_wrap_apr(apr_err,
_("Can't create process '%s' attributes"),
cmd);
/* Make sure we invoke cmd directly, not through a shell. */
apr_err = apr_procattr_cmdtype_set(cmdproc_attr,
inherit ? APR_PROGRAM_PATH : APR_PROGRAM);
if (apr_err)
return svn_error_wrap_apr(apr_err, _("Can't set process '%s' cmdtype"),
cmd);
/* Set the process's working directory. */
if (path)
{
const char *path_apr;
SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
apr_err = apr_procattr_dir_set(cmdproc_attr, path_apr);
if (apr_err)
return svn_error_wrap_apr(apr_err,
_("Can't set process '%s' directory"),
cmd);
}
/* Use requested inputs and outputs.
### Unfortunately each of these apr functions creates a pipe and then
overwrites the pipe file descriptor with the descriptor we pass
in. The pipes can then never be closed. This is an APR bug. */
if (infile)
{
apr_err = apr_procattr_child_in_set(cmdproc_attr, infile, NULL);
if (apr_err)
return svn_error_wrap_apr(apr_err,
_("Can't set process '%s' child input"),
cmd);
}
if (outfile)
{
apr_err = apr_procattr_child_out_set(cmdproc_attr, outfile, NULL);
if (apr_err)
return svn_error_wrap_apr(apr_err,
_("Can't set process '%s' child outfile"),
cmd);
}
if (errfile)
{
apr_err = apr_procattr_child_err_set(cmdproc_attr, errfile, NULL);
if (apr_err)
return svn_error_wrap_apr(apr_err,
_("Can't set process '%s' child errfile"),
cmd);
}
/* Forward request for pipes to APR. */
if (infile_pipe || outfile_pipe || errfile_pipe)
{
apr_err = apr_procattr_io_set(cmdproc_attr,
infile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE,
outfile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE,
errfile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE);
if (apr_err)
return svn_error_wrap_apr(apr_err,
_("Can't set process '%s' stdio pipes"),
cmd);
}
/* Have the child print any problems executing its program to errfile. */
apr_err = apr_pool_userdata_set(errfile, ERRFILE_KEY, NULL, pool);
if (apr_err)
return svn_error_wrap_apr(apr_err,
_("Can't set process '%s' child errfile for "
"error handler"),
cmd);
apr_err = apr_procattr_child_errfn_set(cmdproc_attr,
handle_child_process_error);
if (apr_err)
return svn_error_wrap_apr(apr_err,
_("Can't set process '%s' error handler"),
cmd);
/* Convert cmd and args from UTF-8 */
SVN_ERR(cstring_from_utf8(&cmd_apr, cmd, pool));
for (num_args = 0; args[num_args]; num_args++)
;
args_native = apr_palloc(pool, (num_args + 1) * sizeof(char *));
args_native[num_args] = NULL;
while (num_args--)
{
/* ### Well, it turns out that on APR on Windows expects all
program args to be in UTF-8. Callers of svn_io_run_cmd
should be aware of that. */
SVN_ERR(cstring_from_utf8(&args_native[num_args],
args[num_args], pool));
}
/* Start the cmd command. */
apr_err = apr_proc_create(cmd_proc, cmd_apr, args_native,
inherit ? NULL : env, cmdproc_attr, pool);
if (apr_err)
return svn_error_wrap_apr(apr_err, _("Can't start process '%s'"), cmd);
return SVN_NO_ERROR;
}
#undef ERRFILE_KEY
svn_error_t *
svn_io_wait_for_cmd(apr_proc_t *cmd_proc,
const char *cmd,
int *exitcode,
apr_exit_why_e *exitwhy,
apr_pool_t *pool)
{
apr_status_t apr_err;
apr_exit_why_e exitwhy_val;
int exitcode_val;
/* The Win32 apr_proc_wait doesn't set this... */
exitwhy_val = APR_PROC_EXIT;
/* Wait for the cmd command to finish. */
apr_err = apr_proc_wait(cmd_proc, &exitcode_val, &exitwhy_val, APR_WAIT);
if (!APR_STATUS_IS_CHILD_DONE(apr_err))
return svn_error_wrap_apr(apr_err, _("Error waiting for process '%s'"),
cmd);
if (exitwhy)
*exitwhy = exitwhy_val;
else if (APR_PROC_CHECK_SIGNALED(exitwhy_val)
&& APR_PROC_CHECK_CORE_DUMP(exitwhy_val))
return svn_error_createf
(SVN_ERR_EXTERNAL_PROGRAM, NULL,
_("Process '%s' failed (signal %d, core dumped)"),
cmd, exitcode_val);
else if (APR_PROC_CHECK_SIGNALED(exitwhy_val))
return svn_error_createf
(SVN_ERR_EXTERNAL_PROGRAM, NULL,
_("Process '%s' failed (signal %d)"),
cmd, exitcode_val);
else if (! APR_PROC_CHECK_EXIT(exitwhy_val))
/* Don't really know what happened here. */
return svn_error_createf
(SVN_ERR_EXTERNAL_PROGRAM, NULL,
_("Process '%s' failed (exitwhy %d, exitcode %d)"),
cmd, exitwhy_val, exitcode_val);
if (exitcode)
*exitcode = exitcode_val;
else if (exitcode_val != 0)
return svn_error_createf
(SVN_ERR_EXTERNAL_PROGRAM, NULL,
_("Process '%s' returned error exitcode %d"), cmd, exitcode_val);
return SVN_NO_ERROR;
}
svn_error_t *
svn_io_run_cmd(const char *path,
const char *cmd,
const char *const *args,
int *exitcode,
apr_exit_why_e *exitwhy,
svn_boolean_t inherit,
apr_file_t *infile,
apr_file_t *outfile,
apr_file_t *errfile,
apr_pool_t *pool)
{
apr_proc_t cmd_proc;
SVN_ERR(svn_io_start_cmd3(&cmd_proc, path, cmd, args, NULL, inherit,
FALSE, infile, FALSE, outfile, FALSE, errfile,
pool));
return svn_io_wait_for_cmd(&cmd_proc, cmd, exitcode, exitwhy, pool);
}
svn_error_t *
svn_io_run_diff2(const char *dir,
const char *const *user_args,
int num_user_args,
const char *label1,
const char *label2,
const char *from,
const char *to,
int *pexitcode,
apr_file_t *outfile,
apr_file_t *errfile,
const char *diff_cmd,
apr_pool_t *pool)
{
const char **args;
int i;
int exitcode;
int nargs = 4; /* the diff command itself, two paths, plus a trailing NULL */
apr_pool_t *subpool = svn_pool_create(pool);
if (pexitcode == NULL)
pexitcode = &exitcode;
if (user_args != NULL)
nargs += num_user_args;
else
nargs += 1; /* -u */
if (label1 != NULL)
nargs += 2; /* the -L and the label itself */
if (label2 != NULL)
nargs += 2; /* the -L and the label itself */
args = apr_palloc(subpool, nargs * sizeof(char *));
i = 0;
args[i++] = diff_cmd;
if (user_args != NULL)
{
int j;
for (j = 0; j < num_user_args; ++j)
args[i++] = user_args[j];
}
else
args[i++] = "-u"; /* assume -u if the user didn't give us any args */
if (label1 != NULL)
{
args[i++] = "-L";
args[i++] = label1;
}
if (label2 != NULL)
{
args[i++] = "-L";
args[i++] = label2;
}
args[i++] = svn_dirent_local_style(from, subpool);
args[i++] = svn_dirent_local_style(to, subpool);
args[i++] = NULL;
SVN_ERR_ASSERT(i == nargs);
SVN_ERR(svn_io_run_cmd(dir, diff_cmd, args, pexitcode, NULL, TRUE,
NULL, outfile, errfile, subpool));
/* The man page for (GNU) diff describes the return value as:
"An exit status of 0 means no differences were found, 1 means
some differences were found, and 2 means trouble."
A return value of 2 typically occurs when diff cannot read its input
or write to its output, but in any case we probably ought to return an
error for anything other than 0 or 1 as the output is likely to be
corrupt.
*/
if (*pexitcode != 0 && *pexitcode != 1)
return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
_("'%s' returned %d"),
svn_dirent_local_style(diff_cmd, pool),
*pexitcode);
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_io_run_diff3_3(int *exitcode,
const char *dir,
const char *mine,
const char *older,
const char *yours,
const char *mine_label,
const char *older_label,
const char *yours_label,
apr_file_t *merged,
const char *diff3_cmd,
const apr_array_header_t *user_args,
apr_pool_t *pool)
{
const char **args = apr_palloc(pool,
sizeof(char*) * (13
+ (user_args
? user_args->nelts
: 1)));
#ifndef NDEBUG
int nargs = 12;
#endif
int i = 0;
/* Labels fall back to sensible defaults if not specified. */
if (mine_label == NULL)
mine_label = ".working";
if (older_label == NULL)
older_label = ".old";
if (yours_label == NULL)
yours_label = ".new";
/* Set up diff3 command line. */
args[i++] = diff3_cmd;
if (user_args)
{
int j;
for (j = 0; j < user_args->nelts; ++j)
args[i++] = APR_ARRAY_IDX(user_args, j, const char *);
#ifndef NDEBUG
nargs += user_args->nelts;
#endif
}
else
{
args[i++] = "-E"; /* We tried "-A" here, but that caused
overlapping identical changes to
conflict. See issue #682. */
#ifndef NDEBUG
++nargs;
#endif
}
args[i++] = "-m";
args[i++] = "-L";
args[i++] = mine_label;
args[i++] = "-L";
args[i++] = older_label; /* note: this label is ignored if
using 2-part markers, which is the
case with "-E". */
args[i++] = "-L";
args[i++] = yours_label;
#ifdef SVN_DIFF3_HAS_DIFF_PROGRAM_ARG
{
svn_boolean_t has_arg;
/* ### FIXME: we really shouldn't be reading the config here;
instead, the necessary bits should be passed in by the caller.
But should we add another parameter to this function, when the
whole external diff3 thing might eventually go away? */
apr_hash_t *config;
svn_config_t *cfg;
SVN_ERR(svn_config_get_config(&config, pool));
cfg = config ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG) : NULL;
SVN_ERR(svn_config_get_bool(cfg, &has_arg, SVN_CONFIG_SECTION_HELPERS,
SVN_CONFIG_OPTION_DIFF3_HAS_PROGRAM_ARG,
TRUE));
if (has_arg)
{
const char *diff_cmd, *diff_utf8;
svn_config_get(cfg, &diff_cmd, SVN_CONFIG_SECTION_HELPERS,
SVN_CONFIG_OPTION_DIFF_CMD, SVN_CLIENT_DIFF);
SVN_ERR(cstring_to_utf8(&diff_utf8, diff_cmd, pool));
args[i++] = apr_pstrcat(pool, "--diff-program=", diff_utf8, NULL);
#ifndef NDEBUG
++nargs;
#endif
}
}
#endif
args[i++] = svn_dirent_local_style(mine, pool);
args[i++] = svn_dirent_local_style(older, pool);
args[i++] = svn_dirent_local_style(yours, pool);
args[i++] = NULL;
#ifndef NDEBUG
SVN_ERR_ASSERT(i == nargs);
#endif
/* Run diff3, output the merged text into the scratch file. */
SVN_ERR(svn_io_run_cmd(dir, diff3_cmd, args,
exitcode, NULL,
TRUE, /* keep environment */
NULL, merged, NULL,
pool));
/* According to the diff3 docs, a '0' means the merge was clean, and
'1' means conflict markers were found. Anything else is real
error. */
if ((*exitcode != 0) && (*exitcode != 1))
return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
_("Error running '%s': exitcode was %d, "
"args were:"
"\nin directory '%s', basenames:\n%s\n%s\n%s"),
svn_dirent_local_style(diff3_cmd, pool),
*exitcode,
svn_dirent_local_style(dir, pool),
/* Don't call svn_path_local_style() on
the basenames. We don't want them to
be absolute, and we don't need the
separator conversion. */
mine, older, yours);
return SVN_NO_ERROR;
}
/* Canonicalize a string for hashing. Modifies KEY in place. */
static APR_INLINE char *
fileext_tolower(char *key)
{
register char *p;
for (p = key; *p != 0; ++p)
*p = (char)apr_tolower(*p);
return key;
}
svn_error_t *
svn_io_parse_mimetypes_file(apr_hash_t **type_map,
const char *mimetypes_file,
apr_pool_t *pool)
{
svn_error_t *err = SVN_NO_ERROR;
apr_hash_t *types = apr_hash_make(pool);
svn_boolean_t eof = FALSE;
svn_stringbuf_t *buf;
apr_pool_t *subpool = svn_pool_create(pool);
apr_file_t *types_file;
svn_stream_t *mimetypes_stream;
SVN_ERR(svn_io_file_open(&types_file, mimetypes_file,
APR_READ, APR_OS_DEFAULT, pool));
mimetypes_stream = svn_stream_from_aprfile2(types_file, FALSE, pool);
while (1)
{
apr_array_header_t *tokens;
const char *type;
svn_pool_clear(subpool);
/* Read a line. */
if ((err = svn_stream_readline(mimetypes_stream, &buf,
APR_EOL_STR, &eof, subpool)))
break;
/* Only pay attention to non-empty, non-comment lines. */
if (buf->len)
{
int i;
if (buf->data[0] == '#')
continue;
/* Tokenize (into our return pool). */
tokens = svn_cstring_split(buf->data, " \t", TRUE, pool);
if (tokens->nelts < 2)
continue;
/* The first token in a multi-token line is the media type.
Subsequent tokens are filename extensions associated with
that media type. */
type = APR_ARRAY_IDX(tokens, 0, const char *);
for (i = 1; i < tokens->nelts; i++)
{
/* We can safely address 'ext' as a non-const string because
* we know svn_cstring_split() allocated it in 'pool' for us. */
char *ext = APR_ARRAY_IDX(tokens, i, char *);
fileext_tolower(ext);
svn_hash_sets(types, ext, type);
}
}
if (eof)
break;
}
svn_pool_destroy(subpool);
/* If there was an error above, close the file (ignoring any error
from *that*) and return the originally error. */
if (err)
{
svn_error_clear(svn_stream_close(mimetypes_stream));
return err;
}
/* Close the stream (which closes the underlying file, too). */
SVN_ERR(svn_stream_close(mimetypes_stream));
*type_map = types;
return SVN_NO_ERROR;
}
svn_error_t *
svn_io_detect_mimetype2(const char **mimetype,
const char *file,
apr_hash_t *mimetype_map,
apr_pool_t *pool)
{
static const char * const generic_binary = "application/octet-stream";
svn_node_kind_t kind;
apr_file_t *fh;
svn_error_t *err;
unsigned char block[1024];
apr_size_t amt_read = sizeof(block);
/* Default return value is NULL. */
*mimetype = NULL;
/* If there is a mimetype_map provided, we'll first try to look up
our file's extension in the map. Failing that, we'll run the
heuristic. */
if (mimetype_map)
{
const char *type_from_map;
char *path_ext; /* Can point to physical const memory but only when
svn_path_splitext sets it to "". */
svn_path_splitext(NULL, (const char **)&path_ext, file, pool);
fileext_tolower(path_ext);
if ((type_from_map = svn_hash_gets(mimetype_map, path_ext)))
{
*mimetype = type_from_map;
return SVN_NO_ERROR;
}
}
/* See if this file even exists, and make sure it really is a file. */
SVN_ERR(svn_io_check_path(file, &kind, pool));
if (kind != svn_node_file)
return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL,
_("Can't detect MIME type of non-file '%s'"),
svn_dirent_local_style(file, pool));
SVN_ERR(svn_io_file_open(&fh, file, APR_READ, 0, pool));
/* Read a block of data from FILE. */
err = svn_io_file_read(fh, block, &amt_read, pool);
if (err && ! APR_STATUS_IS_EOF(err->apr_err))
return err;
svn_error_clear(err);
/* Now close the file. No use keeping it open any more. */
SVN_ERR(svn_io_file_close(fh, pool));
if (svn_io_is_binary_data(block, amt_read))
*mimetype = generic_binary;
return SVN_NO_ERROR;
}
svn_boolean_t
svn_io_is_binary_data(const void *data, apr_size_t len)
{
const unsigned char *buf = data;
if (len == 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF)
{
/* This is an empty UTF-8 file which only contains the UTF-8 BOM.
* Treat it as plain text. */
return FALSE;
}
/* Right now, this function is going to be really stupid. It's
going to examine the block of data, and make sure that 15%
of the bytes are such that their value is in the ranges 0x07-0x0D
or 0x20-0x7F, and that none of those bytes is 0x00. If those
criteria are not met, we're calling it binary.
NOTE: Originally, I intended to target 85% of the bytes being in
the specified ranges, but I flubbed the condition. At any rate,
folks aren't complaining, so I'm not sure that it's worth
adjusting this retroactively now. --cmpilato */
if (len > 0)
{
apr_size_t i;
apr_size_t binary_count = 0;
/* Run through the data we've read, counting the 'binary-ish'
bytes. HINT: If we see a 0x00 byte, we'll set our count to its
max and stop reading the file. */
for (i = 0; i < len; i++)
{
if (buf[i] == 0)
{
binary_count = len;
break;
}
if ((buf[i] < 0x07)
|| ((buf[i] > 0x0D) && (buf[i] < 0x20))
|| (buf[i] > 0x7F))
{
binary_count++;
}
}
return (((binary_count * 1000) / len) > 850);
}
return FALSE;
}
svn_error_t *
svn_io_detect_mimetype(const char **mimetype,
const char *file,
apr_pool_t *pool)
{
return svn_io_detect_mimetype2(mimetype, file, NULL, pool);
}
svn_error_t *
svn_io_file_open(apr_file_t **new_file, const char *fname,
apr_int32_t flag, apr_fileperms_t perm,
apr_pool_t *pool)
{
const char *fname_apr;
apr_status_t status;
SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
status = file_open(new_file, fname_apr, flag | APR_BINARY, perm, TRUE,
pool);
if (status)
return svn_error_wrap_apr(status, _("Can't open file '%s'"),
svn_dirent_local_style(fname, pool));
else
return SVN_NO_ERROR;
}
static APR_INLINE svn_error_t *
do_io_file_wrapper_cleanup(apr_file_t *file, apr_status_t status,
const char *msg, const char *msg_no_name,
apr_pool_t *pool)
{
const char *name;
svn_error_t *err;
if (! status)
return SVN_NO_ERROR;
err = svn_io_file_name_get(&name, file, pool);
if (err)
name = NULL;
svn_error_clear(err);
/* ### Issue #3014: Return a specific error for broken pipes,
* ### with a single element in the error chain. */
if (SVN__APR_STATUS_IS_EPIPE(status))
return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL);
if (name)
return svn_error_wrap_apr(status, _(msg),
try_utf8_from_internal_style(name, pool));
else
return svn_error_wrap_apr(status, "%s", _(msg_no_name));
}
svn_error_t *
svn_io_file_close(apr_file_t *file, apr_pool_t *pool)
{
return do_io_file_wrapper_cleanup(file, apr_file_close(file),
N_("Can't close file '%s'"),
N_("Can't close stream"),
pool);
}
svn_error_t *
svn_io_file_getc(char *ch, apr_file_t *file, apr_pool_t *pool)
{
return do_io_file_wrapper_cleanup(file, apr_file_getc(ch, file),
N_("Can't read file '%s'"),
N_("Can't read stream"),
pool);
}
svn_error_t *
svn_io_file_putc(char ch, apr_file_t *file, apr_pool_t *pool)
{
return do_io_file_wrapper_cleanup(file, apr_file_putc(ch, file),
N_("Can't write file '%s'"),
N_("Can't write stream"),
pool);
}
svn_error_t *
svn_io_file_info_get(apr_finfo_t *finfo, apr_int32_t wanted,
apr_file_t *file, apr_pool_t *pool)
{
/* Quoting APR: On NT this request is incredibly expensive, but accurate. */
wanted &= ~SVN__APR_FINFO_MASK_OUT;
return do_io_file_wrapper_cleanup(
file, apr_file_info_get(finfo, wanted, file),
N_("Can't get attribute information from file '%s'"),
N_("Can't get attribute information from stream"),
pool);
}
svn_error_t *
svn_io_file_read(apr_file_t *file, void *buf,
apr_size_t *nbytes, apr_pool_t *pool)
{
return do_io_file_wrapper_cleanup(file, apr_file_read(file, buf, nbytes),
N_("Can't read file '%s'"),
N_("Can't read stream"),
pool);
}
svn_error_t *
svn_io_file_read_full2(apr_file_t *file, void *buf,
apr_size_t nbytes, apr_size_t *bytes_read,
svn_boolean_t *hit_eof,
apr_pool_t *pool)
{
apr_status_t status = apr_file_read_full(file, buf, nbytes, bytes_read);
if (hit_eof)
{
if (APR_STATUS_IS_EOF(status))
{
*hit_eof = TRUE;
return SVN_NO_ERROR;
}
else
*hit_eof = FALSE;
}
return do_io_file_wrapper_cleanup(file, status,
N_("Can't read file '%s'"),
N_("Can't read stream"),
pool);
}
svn_error_t *
svn_io_file_seek(apr_file_t *file, apr_seek_where_t where,
apr_off_t *offset, apr_pool_t *pool)
{
return do_io_file_wrapper_cleanup(
file, apr_file_seek(file, where, offset),
N_("Can't set position pointer in file '%s'"),
N_("Can't set position pointer in stream"),
pool);
}
svn_error_t *
svn_io_file_write(apr_file_t *file, const void *buf,
apr_size_t *nbytes, apr_pool_t *pool)
{
return svn_error_trace(do_io_file_wrapper_cleanup(
file, apr_file_write(file, buf, nbytes),
N_("Can't write to file '%s'"),
N_("Can't write to stream"),
pool));
}
svn_error_t *
svn_io_file_write_full(apr_file_t *file, const void *buf,
apr_size_t nbytes, apr_size_t *bytes_written,
apr_pool_t *pool)
{
/* We cannot simply call apr_file_write_full on Win32 as it may fail
for larger values of NBYTES. In that case, we have to emulate the
"_full" part here. Thus, always call apr_file_write directly on
Win32 as this minimizes overhead for small data buffers. */
#ifdef WIN32
#define MAXBUFSIZE 30*1024
apr_size_t bw = nbytes;
apr_size_t to_write = nbytes;
/* try a simple "write everything at once" first */
apr_status_t rv = apr_file_write(file, buf, &bw);
buf = (char *)buf + bw;
to_write -= bw;
/* if the OS cannot handle that, use smaller chunks */
if (rv == APR_FROM_OS_ERROR(ERROR_NOT_ENOUGH_MEMORY)
&& nbytes > MAXBUFSIZE)
{
do {
bw = to_write > MAXBUFSIZE ? MAXBUFSIZE : to_write;
rv = apr_file_write(file, buf, &bw);
buf = (char *)buf + bw;
to_write -= bw;
} while (rv == APR_SUCCESS && to_write > 0);
}
/* bytes_written may actually be NULL */
if (bytes_written)
*bytes_written = nbytes - to_write;
#undef MAXBUFSIZE
#else
apr_status_t rv = apr_file_write_full(file, buf, nbytes, bytes_written);
#endif
return svn_error_trace(do_io_file_wrapper_cleanup(
file, rv,
N_("Can't write to file '%s'"),
N_("Can't write to stream"),
pool));
}
svn_error_t *
svn_io_write_unique(const char **tmp_path,
const char *dirpath,
const void *buf,
apr_size_t nbytes,
svn_io_file_del_t delete_when,
apr_pool_t *pool)
{
apr_file_t *new_file;
svn_error_t *err;
SVN_ERR(svn_io_open_unique_file3(&new_file, tmp_path, dirpath,
delete_when, pool, pool));
err = svn_io_file_write_full(new_file, buf, nbytes, NULL, pool);
if (!err)
err = svn_io_file_flush_to_disk(new_file, pool);
return svn_error_trace(
svn_error_compose_create(err,
svn_io_file_close(new_file, pool)));
}
svn_error_t *
svn_io_file_trunc(apr_file_t *file, apr_off_t offset, apr_pool_t *pool)
{
/* This is a work-around. APR would flush the write buffer
_after_ truncating the file causing now invalid buffered
data to be written behind OFFSET. */
SVN_ERR(do_io_file_wrapper_cleanup(file, apr_file_flush(file),
N_("Can't flush file '%s'"),
N_("Can't flush stream"),
pool));
return do_io_file_wrapper_cleanup(file, apr_file_trunc(file, offset),
N_("Can't truncate file '%s'"),
N_("Can't truncate stream"),
pool);
}
svn_error_t *
svn_io_read_length_line(apr_file_t *file, char *buf, apr_size_t *limit,
apr_pool_t *pool)
{
/* variables */
apr_size_t total_read = 0;
svn_boolean_t eof = FALSE;
const char *name;
svn_error_t *err;
apr_size_t buf_size = *limit;
while (buf_size > 0)
{
/* read a fair chunk of data at once. But don't get too ambitious
* as that would result in too much waste. Also make sure we can
* put a NUL after the last byte read.
*/
apr_size_t to_read = buf_size < 129 ? buf_size - 1 : 128;
apr_size_t bytes_read = 0;
char *eol;
if (to_read == 0)
break;
/* read data block (or just a part of it) */
SVN_ERR(svn_io_file_read_full2(file, buf, to_read,
&bytes_read, &eof, pool));
/* look or a newline char */
buf[bytes_read] = 0;
eol = strchr(buf, '\n');
if (eol)
{
apr_off_t offset = (eol + 1 - buf) - (apr_off_t)bytes_read;
*eol = 0;
*limit = total_read + (eol - buf);
/* correct the file pointer:
* appear as though we just had read the newline char
*/
SVN_ERR(svn_io_file_seek(file, APR_CUR, &offset, pool));
return SVN_NO_ERROR;
}
else if (eof)
{
/* no EOL found but we hit the end of the file.
* Generate a nice EOF error object and return it.
*/
char dummy;
SVN_ERR(svn_io_file_getc(&dummy, file, pool));
}
/* next data chunk */
buf_size -= bytes_read;
buf += bytes_read;
total_read += bytes_read;
}
/* buffer limit has been exceeded without finding the EOL */
err = svn_io_file_name_get(&name, file, pool);
if (err)
name = NULL;
svn_error_clear(err);
if (name)
return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
_("Can't read length line in file '%s'"),
svn_dirent_local_style(name, pool));
else
return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL,
_("Can't read length line in stream"));
}
svn_error_t *
svn_io_stat(apr_finfo_t *finfo, const char *fname,
apr_int32_t wanted, apr_pool_t *pool)
{
apr_status_t status;
const char *fname_apr;
/* APR doesn't like "" directories */
if (fname[0] == '\0')
fname = ".";
SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
/* Quoting APR: On NT this request is incredibly expensive, but accurate. */
wanted &= ~SVN__APR_FINFO_MASK_OUT;
status = apr_stat(finfo, fname_apr, wanted, pool);
if (status)
return svn_error_wrap_apr(status, _("Can't stat '%s'"),
svn_dirent_local_style(fname, pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_io_file_rename(const char *from_path, const char *to_path,
apr_pool_t *pool)
{
apr_status_t status = APR_SUCCESS;
const char *from_path_apr, *to_path_apr;
SVN_ERR(cstring_from_utf8(&from_path_apr, from_path, pool));
SVN_ERR(cstring_from_utf8(&to_path_apr, to_path, pool));
status = apr_file_rename(from_path_apr, to_path_apr, pool);
#if defined(WIN32) || defined(__OS2__)
/* If the target file is read only NTFS reports EACCESS and
FAT/FAT32 reports EEXIST */
if (APR_STATUS_IS_EACCES(status) || APR_STATUS_IS_EEXIST(status))
{
/* Set the destination file writable because Windows will not
allow us to rename when to_path is read-only, but will
allow renaming when from_path is read only. */
SVN_ERR(svn_io_set_file_read_write(to_path, TRUE, pool));
status = apr_file_rename(from_path_apr, to_path_apr, pool);
}
WIN32_RETRY_LOOP(status, apr_file_rename(from_path_apr, to_path_apr, pool));
#endif /* WIN32 || __OS2__ */
if (status)
return svn_error_wrap_apr(status, _("Can't move '%s' to '%s'"),
svn_dirent_local_style(from_path, pool),
svn_dirent_local_style(to_path, pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_io_file_move(const char *from_path, const char *to_path,
apr_pool_t *pool)
{
svn_error_t *err = svn_io_file_rename(from_path, to_path, pool);
if (err && APR_STATUS_IS_EXDEV(err->apr_err))
{
const char *tmp_to_path;
svn_error_clear(err);
SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_to_path,
svn_dirent_dirname(to_path, pool),
svn_io_file_del_none,
pool, pool));
err = svn_io_copy_file(from_path, tmp_to_path, TRUE, pool);
if (err)
goto failed_tmp;
err = svn_io_file_rename(tmp_to_path, to_path, pool);
if (err)
goto failed_tmp;
err = svn_io_remove_file2(from_path, FALSE, pool);
if (! err)
return SVN_NO_ERROR;
svn_error_clear(svn_io_remove_file2(to_path, FALSE, pool));
return err;
failed_tmp:
svn_error_clear(svn_io_remove_file2(tmp_to_path, FALSE, pool));
}
return err;
}
/* Common implementation of svn_io_dir_make and svn_io_dir_make_hidden.
HIDDEN determines if the hidden attribute
should be set on the newly created directory. */
static svn_error_t *
dir_make(const char *path, apr_fileperms_t perm,
svn_boolean_t hidden, svn_boolean_t sgid, apr_pool_t *pool)
{
apr_status_t status;
const char *path_apr;
SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
/* APR doesn't like "" directories */
if (path_apr[0] == '\0')
path_apr = ".";
#if (APR_OS_DEFAULT & APR_WSTICKY)
/* The APR shipped with httpd 2.0.50 contains a bug where
APR_OS_DEFAULT encompasses the setuid, setgid, and sticky bits.
There is a special case for file creation, but not directory
creation, so directories wind up getting created with the sticky
bit set. (There is no such thing as a setuid directory, and the
setgid bit is apparently ignored at mkdir() time.) If we detect
this problem, work around it by unsetting those bits if we are
passed APR_OS_DEFAULT. */
if (perm == APR_OS_DEFAULT)
perm &= ~(APR_USETID | APR_GSETID | APR_WSTICKY);
#endif
status = apr_dir_make(path_apr, perm, pool);
WIN32_RETRY_LOOP(status, apr_dir_make(path_apr, perm, pool));
if (status)
return svn_error_wrap_apr(status, _("Can't create directory '%s'"),
svn_dirent_local_style(path, pool));
#ifdef APR_FILE_ATTR_HIDDEN
if (hidden)
{
#ifndef WIN32
status = apr_file_attrs_set(path_apr,
APR_FILE_ATTR_HIDDEN,
APR_FILE_ATTR_HIDDEN,
pool);
#else
/* on Windows, use our wrapper so we can also set the
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED attribute */
status = io_win_file_attrs_set(path_apr,
FILE_ATTRIBUTE_HIDDEN |
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
FILE_ATTRIBUTE_HIDDEN |
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
pool);
#endif
if (status)
return svn_error_wrap_apr(status, _("Can't hide directory '%s'"),
svn_dirent_local_style(path, pool));
}
#endif
/* Windows does not implement sgid. Skip here because retrieving
the file permissions via APR_FINFO_PROT | APR_FINFO_OWNER is documented
to be 'incredibly expensive'. */
#ifndef WIN32
if (sgid)
{
apr_finfo_t finfo;
/* Per our contract, don't do error-checking. Some filesystems
* don't support the sgid bit, and that's okay. */
status = apr_stat(&finfo, path_apr, APR_FINFO_PROT, pool);
if (!status)
apr_file_perms_set(path_apr, finfo.protection | APR_GSETID);
}
#endif
return SVN_NO_ERROR;
}
svn_error_t *
svn_io_dir_make(const char *path, apr_fileperms_t perm, apr_pool_t *pool)
{
return dir_make(path, perm, FALSE, FALSE, pool);
}
svn_error_t *
svn_io_dir_make_hidden(const char *path, apr_fileperms_t perm,
apr_pool_t *pool)
{
return dir_make(path, perm, TRUE, FALSE, pool);
}
svn_error_t *
svn_io_dir_make_sgid(const char *path, apr_fileperms_t perm,
apr_pool_t *pool)
{
return dir_make(path, perm, FALSE, TRUE, pool);
}
svn_error_t *
svn_io_dir_open(apr_dir_t **new_dir, const char *dirname, apr_pool_t *pool)
{
apr_status_t status;
const char *dirname_apr;
/* APR doesn't like "" directories */
if (dirname[0] == '\0')
dirname = ".";
SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
status = apr_dir_open(new_dir, dirname_apr, pool);
if (status)
return svn_error_wrap_apr(status, _("Can't open directory '%s'"),
svn_dirent_local_style(dirname, pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_io_dir_remove_nonrecursive(const char *dirname, apr_pool_t *pool)
{
apr_status_t status;
const char *dirname_apr;
SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
status = apr_dir_remove(dirname_apr, pool);
#ifdef WIN32
{
svn_boolean_t retry = TRUE;
if (APR_TO_OS_ERROR(status) == ERROR_DIR_NOT_EMPTY)
{
apr_status_t empty_status = dir_is_empty(dirname_apr, pool);
if (APR_STATUS_IS_ENOTEMPTY(empty_status))
retry = FALSE;
}
if (retry)
{
WIN32_RETRY_LOOP(status, apr_dir_remove(dirname_apr, pool));
}
}
#endif
if (status)
return svn_error_wrap_apr(status, _("Can't remove directory '%s'"),
svn_dirent_local_style(dirname, pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_io_dir_read(apr_finfo_t *finfo,
apr_int32_t wanted,
apr_dir_t *thedir,
apr_pool_t *pool)
{
apr_status_t status;
status = apr_dir_read(finfo, wanted, thedir);
if (status)
return svn_error_wrap_apr(status, _("Can't read directory"));
/* It would be nice to use entry_name_to_utf8() below, but can we
get the dir's path out of an apr_dir_t? I don't see a reliable
way to do it. */
if (finfo->fname)
SVN_ERR(svn_path_cstring_to_utf8(&finfo->fname, finfo->fname, pool));
if (finfo->name)
SVN_ERR(svn_path_cstring_to_utf8(&finfo->name, finfo->name, pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_io_dir_close(apr_dir_t *thedir)
{
apr_status_t apr_err = apr_dir_close(thedir);
if (apr_err)
return svn_error_wrap_apr(apr_err, _("Error closing directory"));
return SVN_NO_ERROR;
}
svn_error_t *
svn_io_dir_walk2(const char *dirname,
apr_int32_t wanted,
svn_io_walk_func_t walk_func,
void *walk_baton,
apr_pool_t *pool)
{
apr_status_t apr_err;
apr_dir_t *handle;
apr_pool_t *subpool;
const char *dirname_apr;
apr_finfo_t finfo;
wanted |= APR_FINFO_TYPE | APR_FINFO_NAME;
/* Quoting APR: On NT this request is incredibly expensive, but accurate. */
wanted &= ~SVN__APR_FINFO_MASK_OUT;
/* The documentation for apr_dir_read used to state that "." and ".."
will be returned as the first two files, but it doesn't
work that way in practice, in particular ext3 on Linux-2.6 doesn't
follow the rules. For details see
http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=56666
If APR ever does implement "dot-first" then it would be possible to
remove the svn_io_stat and walk_func calls and use the walk_func
inside the loop.
Note: apr_stat doesn't handle FINFO_NAME but svn_io_dir_walk is
documented to provide it, so we have to do a bit extra. */
SVN_ERR(svn_io_stat(&finfo, dirname, wanted & ~APR_FINFO_NAME, pool));
SVN_ERR(cstring_from_utf8(&finfo.name,
svn_dirent_basename(dirname, pool),
pool));
finfo.valid |= APR_FINFO_NAME;
SVN_ERR((*walk_func)(walk_baton, dirname, &finfo, pool));
SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
/* APR doesn't like "" directories */
if (dirname_apr[0] == '\0')
dirname_apr = ".";
apr_err = apr_dir_open(&handle, dirname_apr, pool);
if (apr_err)
return svn_error_wrap_apr(apr_err, _("Can't open directory '%s'"),
svn_dirent_local_style(dirname, pool));
/* iteration subpool */
subpool = svn_pool_create(pool);
while (1)
{
const char *name_utf8;
const char *full_path;
svn_pool_clear(subpool);
apr_err = apr_dir_read(&finfo, wanted, handle);
if (APR_STATUS_IS_ENOENT(apr_err))
break;
else if (apr_err)
{
return svn_error_wrap_apr(apr_err,
_("Can't read directory entry in '%s'"),
svn_dirent_local_style(dirname, pool));
}
if (finfo.filetype == APR_DIR)
{
if (finfo.name[0] == '.'
&& (finfo.name[1] == '\0'
|| (finfo.name[1] == '.' && finfo.name[2] == '\0')))
/* skip "." and ".." */
continue;
/* some other directory. recurse. it will be passed to the
callback inside the recursion. */
SVN_ERR(entry_name_to_utf8(&name_utf8, finfo.name, dirname,
subpool));
full_path = svn_dirent_join(dirname, name_utf8, subpool);
SVN_ERR(svn_io_dir_walk2(full_path,
wanted,
walk_func,
walk_baton,
subpool));
}
else if (finfo.filetype == APR_REG || finfo.filetype == APR_LNK)
{
/* some other directory. pass it to the callback. */
SVN_ERR(entry_name_to_utf8(&name_utf8, finfo.name, dirname,
subpool));
full_path = svn_dirent_join(dirname, name_utf8, subpool);
SVN_ERR((*walk_func)(walk_baton,
full_path,
&finfo,
subpool));
}
/* else:
Some other type of file; skip it for now. We've reserved the
right to expand our coverage here in the future, though,
without revving this API.
*/
}
svn_pool_destroy(subpool);
apr_err = apr_dir_close(handle);
if (apr_err)
return svn_error_wrap_apr(apr_err, _("Error closing directory '%s'"),
svn_dirent_local_style(dirname, pool));
return SVN_NO_ERROR;
}
/**
* Determine if a directory is empty or not.
* @param Return APR_SUCCESS if the dir is empty, else APR_ENOTEMPTY if not.
* @param path The directory.
* @param pool Used for temporary allocation.
* @remark If path is not a directory, or some other error occurs,
* then return the appropriate apr status code.
*
* (This function is written in APR style, in anticipation of
* perhaps someday being moved to APR as 'apr_dir_is_empty'.)
*/
static apr_status_t
dir_is_empty(const char *dir, apr_pool_t *pool)
{
apr_status_t apr_err;
apr_dir_t *dir_handle;
apr_finfo_t finfo;
apr_status_t retval = APR_SUCCESS;
/* APR doesn't like "" directories */
if (dir[0] == '\0')
dir = ".";
apr_err = apr_dir_open(&dir_handle, dir, pool);
if (apr_err != APR_SUCCESS)
return apr_err;
for (apr_err = apr_dir_read(&finfo, APR_FINFO_NAME, dir_handle);
apr_err == APR_SUCCESS;
apr_err = apr_dir_read(&finfo, APR_FINFO_NAME, dir_handle))
{
/* Ignore entries for this dir and its parent, robustly.
(APR promises that they'll come first, so technically
this guard could be moved outside the loop. But Ryan Bloom
says he doesn't believe it, and I believe him. */
if (! (finfo.name[0] == '.'
&& (finfo.name[1] == '\0'
|| (finfo.name[1] == '.' && finfo.name[2] == '\0'))))
{
retval = APR_ENOTEMPTY;
break;
}
}
/* Make sure we broke out of the loop for the right reason. */
if (apr_err && ! APR_STATUS_IS_ENOENT(apr_err))
return apr_err;
apr_err = apr_dir_close(dir_handle);
if (apr_err != APR_SUCCESS)
return apr_err;
return retval;
}
svn_error_t *
svn_io_dir_empty(svn_boolean_t *is_empty_p,
const char *path,
apr_pool_t *pool)
{
apr_status_t status;
const char *path_apr;
SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
status = dir_is_empty(path_apr, pool);
if (!status)
*is_empty_p = TRUE;
else if (APR_STATUS_IS_ENOTEMPTY(status))
*is_empty_p = FALSE;
else
return svn_error_wrap_apr(status, _("Can't check directory '%s'"),
svn_dirent_local_style(path, pool));
return SVN_NO_ERROR;
}
/*** Version/format files ***/
svn_error_t *
svn_io_write_version_file(const char *path,
int version,
apr_pool_t *pool)
{
const char *path_tmp;
const char *format_contents = apr_psprintf(pool, "%d\n", version);
SVN_ERR_ASSERT(version >= 0);
SVN_ERR(svn_io_write_unique(&path_tmp,
svn_dirent_dirname(path, pool),
format_contents, strlen(format_contents),
svn_io_file_del_none, pool));
#if defined(WIN32) || defined(__OS2__)
/* make the destination writable, but only on Windows, because
Windows does not let us replace read-only files. */
SVN_ERR(svn_io_set_file_read_write(path, TRUE, pool));
#endif /* WIN32 || __OS2__ */
/* rename the temp file as the real destination */
SVN_ERR(svn_io_file_rename(path_tmp, path, pool));
/* And finally remove the perms to make it read only */
return svn_io_set_file_read_only(path, FALSE, pool);
}
svn_error_t *
svn_io_read_version_file(int *version,
const char *path,
apr_pool_t *pool)
{
apr_file_t *format_file;
char buf[80];
apr_size_t len;
svn_error_t *err;
/* Read a chunk of data from PATH */
SVN_ERR(svn_io_file_open(&format_file, path, APR_READ,
APR_OS_DEFAULT, pool));
len = sizeof(buf);
err = svn_io_file_read(format_file, buf, &len, pool);
/* Close the file. */
SVN_ERR(svn_error_compose_create(err,
svn_io_file_close(format_file, pool)));
/* If there was no data in PATH, return an error. */
if (len == 0)
return svn_error_createf(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
_("Reading '%s'"),
svn_dirent_local_style(path, pool));
/* Check that the first line contains only digits. */
{
apr_size_t i;
for (i = 0; i < len; ++i)
{
char c = buf[i];
if (i > 0 && (c == '\r' || c == '\n'))
{
buf[i] = '\0';
break;
}
if (! svn_ctype_isdigit(c))
return svn_error_createf
(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
_("First line of '%s' contains non-digit"),
svn_dirent_local_style(path, pool));
}
}
/* Convert to integer. */
SVN_ERR(svn_cstring_atoi(version, buf));
return SVN_NO_ERROR;
}
/* Do a byte-for-byte comparison of FILE1 and FILE2. */
static svn_error_t *
contents_identical_p(svn_boolean_t *identical_p,
const char *file1,
const char *file2,
apr_pool_t *pool)
{
svn_error_t *err;
apr_size_t bytes_read1, bytes_read2;
char *buf1 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
char *buf2 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
apr_file_t *file1_h;
apr_file_t *file2_h;
svn_boolean_t eof1 = FALSE;
svn_boolean_t eof2 = FALSE;
SVN_ERR(svn_io_file_open(&file1_h, file1, APR_READ, APR_OS_DEFAULT,
pool));
err = svn_io_file_open(&file2_h, file2, APR_READ, APR_OS_DEFAULT,
pool);
if (err)
return svn_error_trace(
svn_error_compose_create(err,
svn_io_file_close(file1_h, pool)));
*identical_p = TRUE; /* assume TRUE, until disproved below */
while (!err && !eof1 && !eof2)
{
err = svn_io_file_read_full2(file1_h, buf1,
SVN__STREAM_CHUNK_SIZE, &bytes_read1,
&eof1, pool);
if (err)
break;
err = svn_io_file_read_full2(file2_h, buf2,
SVN__STREAM_CHUNK_SIZE, &bytes_read2,
&eof2, pool);
if (err)
break;
if ((bytes_read1 != bytes_read2) || memcmp(buf1, buf2, bytes_read1))
{
*identical_p = FALSE;
break;
}
}
/* Special case: one file being a prefix of the other and the shorter
* file's size is a multiple of SVN__STREAM_CHUNK_SIZE. */
if (!err && (eof1 != eof2))
*identical_p = FALSE;
return svn_error_trace(
svn_error_compose_create(
err,
svn_error_compose_create(svn_io_file_close(file1_h, pool),
svn_io_file_close(file2_h, pool))));
}
/* Do a byte-for-byte comparison of FILE1, FILE2 and FILE3. */
static svn_error_t *
contents_three_identical_p(svn_boolean_t *identical_p12,
svn_boolean_t *identical_p23,
svn_boolean_t *identical_p13,
const char *file1,
const char *file2,
const char *file3,
apr_pool_t *scratch_pool)
{
svn_error_t *err;
apr_size_t bytes_read1, bytes_read2, bytes_read3;
char *buf1 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
char *buf2 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
char *buf3 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
apr_file_t *file1_h;
apr_file_t *file2_h;
apr_file_t *file3_h;
svn_boolean_t eof1 = FALSE;
svn_boolean_t eof2 = FALSE;
svn_boolean_t eof3 = FALSE;
svn_boolean_t read_1, read_2, read_3;
SVN_ERR(svn_io_file_open(&file1_h, file1, APR_READ, APR_OS_DEFAULT,
scratch_pool));
err = svn_io_file_open(&file2_h, file2, APR_READ, APR_OS_DEFAULT,
scratch_pool);
if (err)
return svn_error_trace(
svn_error_compose_create(err,
svn_io_file_close(file1_h, scratch_pool)));
err = svn_io_file_open(&file3_h, file3, APR_READ, APR_OS_DEFAULT,
scratch_pool);
if (err)
return svn_error_trace(
svn_error_compose_create(
err,
svn_error_compose_create(svn_io_file_close(file1_h,
scratch_pool),
svn_io_file_close(file2_h,
scratch_pool))));
/* assume TRUE, until disproved below */
*identical_p12 = *identical_p23 = *identical_p13 = TRUE;
/* We need to read as long as no error occurs, and as long as one of the
* flags could still change due to a read operation */
while (!err
&& ((*identical_p12 && !eof1 && !eof2)
|| (*identical_p23 && !eof2 && !eof3)
|| (*identical_p13 && !eof1 && !eof3)))
{
read_1 = read_2 = read_3 = FALSE;
/* As long as a file is not at the end yet, and it is still
* potentially identical to another file, we read the next chunk.*/
if (!eof1 && (*identical_p12 || *identical_p13))
{
err = svn_io_file_read_full2(file1_h, buf1,
SVN__STREAM_CHUNK_SIZE, &bytes_read1,
&eof1, scratch_pool);
if (err)
break;
read_1 = TRUE;
}
if (!eof2 && (*identical_p12 || *identical_p23))
{
err = svn_io_file_read_full2(file2_h, buf2,
SVN__STREAM_CHUNK_SIZE, &bytes_read2,
&eof2, scratch_pool);
if (err)
break;
read_2 = TRUE;
}
if (!eof3 && (*identical_p13 || *identical_p23))
{
err = svn_io_file_read_full2(file3_h, buf3,
SVN__STREAM_CHUNK_SIZE, &bytes_read3,
&eof3, scratch_pool);
if (err)
break;
read_3 = TRUE;
}
/* If the files are still marked identical, and at least one of them
* is not at the end of file, we check whether they differ, and set
* their flag to false then. */
if (*identical_p12
&& (read_1 || read_2)
&& ((eof1 != eof2)
|| (bytes_read1 != bytes_read2)
|| memcmp(buf1, buf2, bytes_read1)))
{
*identical_p12 = FALSE;
}
if (*identical_p23
&& (read_2 || read_3)
&& ((eof2 != eof3)
|| (bytes_read2 != bytes_read3)
|| memcmp(buf2, buf3, bytes_read2)))
{
*identical_p23 = FALSE;
}
if (*identical_p13
&& (read_1 || read_3)
&& ((eof1 != eof3)
|| (bytes_read1 != bytes_read3)
|| memcmp(buf1, buf3, bytes_read3)))
{
*identical_p13 = FALSE;
}
}
return svn_error_trace(
svn_error_compose_create(
err,
svn_error_compose_create(
svn_io_file_close(file1_h, scratch_pool),
svn_error_compose_create(
svn_io_file_close(file2_h, scratch_pool),
svn_io_file_close(file3_h, scratch_pool)))));
}
svn_error_t *
svn_io_files_contents_same_p(svn_boolean_t *same,
const char *file1,
const char *file2,
apr_pool_t *pool)
{
svn_boolean_t q;
SVN_ERR(svn_io_filesizes_different_p(&q, file1, file2, pool));
if (q)
{
*same = FALSE;
return SVN_NO_ERROR;
}
SVN_ERR(contents_identical_p(&q, file1, file2, pool));
if (q)
*same = TRUE;
else
*same = FALSE;
return SVN_NO_ERROR;
}
svn_error_t *
svn_io_files_contents_three_same_p(svn_boolean_t *same12,
svn_boolean_t *same23,
svn_boolean_t *same13,
const char *file1,
const char *file2,
const char *file3,
apr_pool_t *scratch_pool)
{
svn_boolean_t diff_size12, diff_size23, diff_size13;
SVN_ERR(svn_io_filesizes_three_different_p(&diff_size12,
&diff_size23,
&diff_size13,
file1,
file2,
file3,
scratch_pool));
if (diff_size12 && diff_size23 && diff_size13)
{
*same12 = *same23 = *same13 = FALSE;
}
else if (diff_size12 && diff_size23)
{
*same12 = *same23 = FALSE;
SVN_ERR(contents_identical_p(same13, file1, file3, scratch_pool));
}
else if (diff_size23 && diff_size13)
{
*same23 = *same13 = FALSE;
SVN_ERR(contents_identical_p(same12, file1, file2, scratch_pool));
}
else if (diff_size12 && diff_size13)
{
*same12 = *same13 = FALSE;
SVN_ERR(contents_identical_p(same23, file2, file3, scratch_pool));
}
else
{
SVN_ERR_ASSERT(!diff_size12 && !diff_size23 && !diff_size13);
SVN_ERR(contents_three_identical_p(same12, same23, same13,
file1, file2, file3,
scratch_pool));
}
return SVN_NO_ERROR;
}
#ifdef WIN32
/* Counter value of file_mktemp request (used in a threadsafe way), to make
sure that a single process normally never generates the same tempname
twice */
static volatile apr_uint32_t tempname_counter = 0;
#endif
/* Creates a new temporary file in DIRECTORY with apr flags FLAGS.
Set *NEW_FILE to the file handle and *NEW_FILE_NAME to its name.
Perform temporary allocations in SCRATCH_POOL and the result in
RESULT_POOL. */
static svn_error_t *
temp_file_create(apr_file_t **new_file,
const char **new_file_name,
const char *directory,
apr_int32_t flags,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
#ifndef WIN32
const char *templ = svn_dirent_join(directory, "svn-XXXXXX", scratch_pool);
const char *templ_apr;
apr_status_t status;
SVN_ERR(svn_path_cstring_from_utf8(&templ_apr, templ, scratch_pool));
/* ### svn_path_cstring_from_utf8() guarantees to make a copy of the
data available in POOL and we need a non-const pointer here,
as apr changes the template to return the new filename. */
status = apr_file_mktemp(new_file, (char *)templ_apr, flags, result_pool);
if (status)
return svn_error_wrap_apr(status, _("Can't create temporary file from "
"template '%s'"), templ);
/* Translate the returned path back to utf-8 before returning it */
return svn_error_trace(svn_path_cstring_to_utf8(new_file_name,
templ_apr,
result_pool));
#else
/* The Windows implementation of apr_file_mktemp doesn't handle access
denied errors correctly. Therefore we implement our own temp file
creation function here. */
/* ### Most of this is borrowed from the svn_io_open_uniquely_named(),
### the function we used before. But we try to guess a more unique
### name before trying if it exists. */
/* Offset by some time value and a unique request nr to make the number
+- unique for both this process and on the computer */
int baseNr = (GetTickCount() << 11) + 7 * svn_atomic_inc(&tempname_counter)
+ GetCurrentProcessId();
int i;
/* ### Maybe use an iterpool? */
for (i = 0; i <= 99999; i++)
{
apr_uint32_t unique_nr;
const char *unique_name;
const char *unique_name_apr;
apr_file_t *try_file;
apr_status_t apr_err;
/* Generate a number that should be unique for this application and
usually for the entire computer to reduce the number of cycles
through this loop. (A bit of calculation is much cheaper then
disk io) */
unique_nr = baseNr + 3 * i;
unique_name = svn_dirent_join(directory,
apr_psprintf(scratch_pool, "svn-%X",
unique_nr),
scratch_pool);
SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name, scratch_pool));
apr_err = file_open(&try_file, unique_name_apr, flags,
APR_OS_DEFAULT, FALSE, scratch_pool);
if (APR_STATUS_IS_EEXIST(apr_err))
continue;
else if (apr_err)
{
/* On Win32, CreateFile fails with an "Access Denied" error
code, rather than "File Already Exists", if the colliding
name belongs to a directory. */
if (APR_STATUS_IS_EACCES(apr_err))
{
apr_finfo_t finfo;
apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr,
APR_FINFO_TYPE, scratch_pool);
if (!apr_err_2 && finfo.filetype == APR_DIR)
continue;
apr_err_2 = APR_TO_OS_ERROR(apr_err);
if (apr_err_2 == ERROR_ACCESS_DENIED ||
apr_err_2 == ERROR_SHARING_VIOLATION)
{
/* The file is in use by another process or is hidden;
create a new name, but don't do this 99999 times in
case the folder is not writable */
i += 797;
continue;
}
/* Else fall through and return the original error. */
}
return svn_error_wrap_apr(apr_err, _("Can't open '%s'"),
svn_dirent_local_style(unique_name,
scratch_pool));
}
else
{
/* Move file to the right pool */
apr_err = apr_file_setaside(new_file, try_file, result_pool);
if (apr_err)
return svn_error_wrap_apr(apr_err, _("Can't set aside '%s'"),
svn_dirent_local_style(unique_name,
scratch_pool));
*new_file_name = apr_pstrdup(result_pool, unique_name);
return SVN_NO_ERROR;
}
}
return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
NULL,
_("Unable to make name in '%s'"),
svn_dirent_local_style(directory, scratch_pool));
#endif
}
/* Wrapper for apr_file_name_get(), passing out a UTF8-encoded filename. */
svn_error_t *
svn_io_file_name_get(const char **filename,
apr_file_t *file,
apr_pool_t *pool)
{
const char *fname_apr;
apr_status_t status;
status = apr_file_name_get(&fname_apr, file);
if (status)
return svn_error_wrap_apr(status, _("Can't get file name"));
if (fname_apr)
SVN_ERR(svn_path_cstring_to_utf8(filename, fname_apr, pool));
else
*filename = NULL;
return SVN_NO_ERROR;
}
svn_error_t *
svn_io_open_unique_file3(apr_file_t **file,
const char **unique_path,
const char *dirpath,
svn_io_file_del_t delete_when,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_file_t *tempfile;
const char *tempname;
struct temp_file_cleanup_s *baton = NULL;
apr_int32_t flags = (APR_READ | APR_WRITE | APR_CREATE | APR_EXCL |
APR_BUFFERED | APR_BINARY);
#if !defined(WIN32) && !defined(__OS2__)
apr_fileperms_t perms;
svn_boolean_t using_system_temp_dir = FALSE;
#endif
SVN_ERR_ASSERT(file || unique_path);
if (file)
*file = NULL;
if (unique_path)
*unique_path = NULL;
if (dirpath == NULL)
{
#if !defined(WIN32) && !defined(__OS2__)
using_system_temp_dir = TRUE;
#endif
SVN_ERR(svn_io_temp_dir(&dirpath, scratch_pool));
}
switch (delete_when)
{
case svn_io_file_del_on_pool_cleanup:
baton = apr_palloc(result_pool, sizeof(*baton));
baton->pool = result_pool;
baton->fname_apr = NULL;
/* Because cleanups are run LIFO, we need to make sure to register
our cleanup before the apr_file_close cleanup:
On Windows, you can't remove an open file.
*/
apr_pool_cleanup_register(result_pool, baton,
temp_file_plain_cleanup_handler,
temp_file_child_cleanup_handler);
break;
case svn_io_file_del_on_close:
flags |= APR_DELONCLOSE;
break;
default:
break;
}
SVN_ERR(temp_file_create(&tempfile, &tempname, dirpath, flags,
result_pool, scratch_pool));
#if !defined(WIN32) && !defined(__OS2__)
/* apr_file_mktemp() creates files with mode 0600.
* This is appropriate if we're using a system temp dir since we don't
* want to leak sensitive data into temp files other users can read.
* If we're not using a system temp dir we're probably using the
* .svn/tmp area and it's likely that the tempfile will end up being
* copied or renamed into the working copy.
* This would cause working files having mode 0600 while users might
* expect to see 0644 or 0664. So we tweak perms of the tempfile in this
* case, but only if the umask allows it. */
if (!using_system_temp_dir)
{
+ svn_error_t *err;
+
SVN_ERR(merge_default_file_perms(tempfile, &perms, scratch_pool));
- SVN_ERR(file_perms_set2(tempfile, perms, scratch_pool));
+ err = file_perms_set2(tempfile, perms, scratch_pool);
+ if (err)
+ {
+ if (APR_STATUS_IS_INCOMPLETE(err->apr_err) ||
+ APR_STATUS_IS_ENOTIMPL(err->apr_err))
+ svn_error_clear(err);
+ else
+ {
+ const char *message;
+ message = apr_psprintf(scratch_pool,
+ _("Can't set permissions on '%s'"),
+ svn_dirent_local_style(tempname,
+ scratch_pool));
+ return svn_error_quick_wrap(err, message);
+ }
+ }
}
#endif
if (file)
*file = tempfile;
else
SVN_ERR(svn_io_file_close(tempfile, scratch_pool));
if (unique_path)
*unique_path = tempname; /* Was allocated in result_pool */
if (baton)
SVN_ERR(cstring_from_utf8(&baton->fname_apr, tempname, result_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_io_file_readline(apr_file_t *file,
svn_stringbuf_t **stringbuf,
const char **eol,
svn_boolean_t *eof,
apr_size_t max_len,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_stringbuf_t *str;
const char *eol_str;
apr_size_t numbytes;
char c;
apr_size_t len;
svn_boolean_t found_eof;
str = svn_stringbuf_create_ensure(80, result_pool);
/* Read bytes into STR up to and including, but not storing,
* the next EOL sequence. */
eol_str = NULL;
numbytes = 1;
len = 0;
found_eof = FALSE;
while (!found_eof)
{
if (len < max_len)
SVN_ERR(svn_io_file_read_full2(file, &c, sizeof(c), &numbytes,
&found_eof, scratch_pool));
len++;
if (numbytes != 1 || len > max_len)
{
found_eof = TRUE;
break;
}
if (c == '\n')
{
eol_str = "\n";
}
else if (c == '\r')
{
eol_str = "\r";
if (!found_eof && len < max_len)
{
apr_off_t pos;
/* Check for "\r\n" by peeking at the next byte. */
pos = 0;
SVN_ERR(svn_io_file_seek(file, APR_CUR, &pos, scratch_pool));
SVN_ERR(svn_io_file_read_full2(file, &c, sizeof(c), &numbytes,
&found_eof, scratch_pool));
if (numbytes == 1 && c == '\n')
{
eol_str = "\r\n";
len++;
}
else
{
/* Pretend we never peeked. */
SVN_ERR(svn_io_file_seek(file, APR_SET, &pos, scratch_pool));
found_eof = FALSE;
numbytes = 1;
}
}
}
else
svn_stringbuf_appendbyte(str, c);
if (eol_str)
break;
}
if (eol)
*eol = eol_str;
if (eof)
*eof = found_eof;
*stringbuf = str;
return SVN_NO_ERROR;
}
Index: vendor/subversion/dist/subversion/libsvn_subr/mergeinfo.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_subr/mergeinfo.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_subr/mergeinfo.c (revision 286501)
@@ -1,2631 +1,2680 @@
/*
* mergeinfo.c: Mergeinfo parsing and handling
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include <assert.h>
#include <ctype.h>
#include "svn_path.h"
#include "svn_types.h"
#include "svn_ctype.h"
#include "svn_pools.h"
#include "svn_sorts.h"
#include "svn_error.h"
#include "svn_error_codes.h"
#include "svn_string.h"
#include "svn_mergeinfo.h"
#include "private/svn_fspath.h"
#include "private/svn_mergeinfo_private.h"
#include "private/svn_string_private.h"
#include "private/svn_subr_private.h"
#include "svn_private_config.h"
#include "svn_hash.h"
#include "private/svn_dep_compat.h"
/* Attempt to combine two ranges, IN1 and IN2. If they are adjacent or
overlapping, and their inheritability allows them to be combined, put
the result in OUTPUT and return TRUE, otherwise return FALSE.
CONSIDER_INHERITANCE determines how to account for the inheritability
of IN1 and IN2 when trying to combine ranges. If ranges with different
inheritability are combined (CONSIDER_INHERITANCE must be FALSE for this
to happen) the result is inheritable. If both ranges are inheritable the
result is inheritable. Only and if both ranges are non-inheritable is
the result is non-inheritable.
Range overlapping detection algorithm from
http://c2.com/cgi-bin/wiki/fullSearch?TestIfDateRangesOverlap
*/
static svn_boolean_t
combine_ranges(svn_merge_range_t *output,
const svn_merge_range_t *in1,
const svn_merge_range_t *in2,
svn_boolean_t consider_inheritance)
{
if (in1->start <= in2->end && in2->start <= in1->end)
{
if (!consider_inheritance
|| (consider_inheritance
&& (in1->inheritable == in2->inheritable)))
{
output->start = MIN(in1->start, in2->start);
output->end = MAX(in1->end, in2->end);
output->inheritable = (in1->inheritable || in2->inheritable);
return TRUE;
}
}
return FALSE;
}
/* pathname -> PATHNAME */
static svn_error_t *
parse_pathname(const char **input,
const char *end,
const char **pathname,
apr_pool_t *pool)
{
const char *curr = *input;
const char *last_colon = NULL;
/* A pathname may contain colons, so find the last colon before END
or newline. We'll consider this the divider between the pathname
and the revisionlist. */
while (curr < end && *curr != '\n')
{
if (*curr == ':')
last_colon = curr;
curr++;
}
if (!last_colon)
return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
_("Pathname not terminated by ':'"));
if (last_colon == *input)
return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
_("No pathname preceding ':'"));
/* Tolerate relative repository paths, but convert them to absolute.
### Efficiency? 1 string duplication here, 2 in canonicalize. */
*pathname = svn_fspath__canonicalize(apr_pstrndup(pool, *input,
last_colon - *input),
pool);
*input = last_colon;
return SVN_NO_ERROR;
}
/* Return TRUE iff (svn_merge_range_t *) RANGE describes a valid, forward
* revision range.
*
* Note: The smallest valid value of RANGE->start is 0 because it is an
* exclusive endpoint, being one less than the revision number of the first
* change described by the range, and the oldest possible change is "r1" as
* there cannot be a change "r0". */
#define IS_VALID_FORWARD_RANGE(range) \
(SVN_IS_VALID_REVNUM((range)->start) && ((range)->start < (range)->end))
/* Ways in which two svn_merge_range_t can intersect or adjoin, if at all. */
typedef enum intersection_type_t
{
/* Ranges don't intersect and don't adjoin. */
svn__no_intersection,
/* Ranges are equal. */
svn__equal_intersection,
/* Ranges adjoin but don't overlap. */
svn__adjoining_intersection,
/* Ranges overlap but neither is a subset of the other. */
svn__overlapping_intersection,
/* One range is a proper subset of the other. */
svn__proper_subset_intersection
} intersection_type_t;
/* Given ranges R1 and R2, both of which must be forward merge ranges,
set *INTERSECTION_TYPE to describe how the ranges intersect, if they
do at all. The inheritance type of the ranges is not considered. */
static svn_error_t *
get_type_of_intersection(const svn_merge_range_t *r1,
const svn_merge_range_t *r2,
intersection_type_t *intersection_type)
{
SVN_ERR_ASSERT(r1);
SVN_ERR_ASSERT(r2);
SVN_ERR_ASSERT(IS_VALID_FORWARD_RANGE(r1));
SVN_ERR_ASSERT(IS_VALID_FORWARD_RANGE(r2));
if (!(r1->start <= r2->end && r2->start <= r1->end))
*intersection_type = svn__no_intersection;
else if (r1->start == r2->start && r1->end == r2->end)
*intersection_type = svn__equal_intersection;
else if (r1->end == r2->start || r2->end == r1->start)
*intersection_type = svn__adjoining_intersection;
else if (r1->start <= r2->start && r1->end >= r2->end)
*intersection_type = svn__proper_subset_intersection;
else if (r2->start <= r1->start && r2->end >= r1->end)
*intersection_type = svn__proper_subset_intersection;
else
*intersection_type = svn__overlapping_intersection;
return SVN_NO_ERROR;
}
/* Modify or extend RANGELIST (a list of merge ranges) to incorporate
NEW_RANGE. RANGELIST is a "rangelist" as defined in svn_mergeinfo.h.
OVERVIEW
Determine the minimal set of non-overlapping merge ranges required to
represent the combination of RANGELIST and NEW_RANGE. The result depends
on whether and how NEW_RANGE overlaps any merge range[*] in RANGELIST,
and also on any differences in the inheritability of each range,
according to the rules described below. Modify RANGELIST to represent
this result, by adjusting the last range in it and/or appending one or
two more ranges.
([*] Due to the simplifying assumption below, only the last range in
RANGELIST is considered.)
DETAILS
If RANGELIST is not empty assume NEW_RANGE does not intersect with any
range before the last one in RANGELIST.
If RANGELIST is empty or NEW_RANGE does not intersect with the lastrange
in RANGELIST, then append a copy of NEW_RANGE, allocated in RESULT_POOL,
to RANGELIST.
If NEW_RANGE intersects with the last range in RANGELIST then combine
these two ranges as described below:
If the intersecting ranges have the same inheritability then simply
combine the ranges in place. Otherwise, if the ranges intersect but
differ in inheritability, then merge the ranges as dictated by
CONSIDER_INHERITANCE:
If CONSIDER_INHERITANCE is false then intersecting ranges are combined
into a single range. The inheritability of the resulting range is
non-inheritable *only* if both ranges are non-inheritable, otherwise the
combined range is inheritable, e.g.:
Last range in NEW_RANGE RESULTING RANGES
RANGELIST
------------- --------- ----------------
4-10* 6-13 4-13
4-10 6-13* 4-13
4-10* 6-13* 4-13*
If CONSIDER_INHERITANCE is true, then only the intersection between the
two ranges is combined, with the inheritability of the resulting range
non-inheritable only if both ranges were non-inheritable. The
non-intersecting portions are added as separate ranges allocated in
RESULT_POOL, e.g.:
Last range in NEW_RANGE RESULTING RANGES
RANGELIST
------------- --------- ----------------
4-10* 6 4-5*, 6, 7-10*
4-10* 6-12 4-5*, 6-12
Note that the standard rules for rangelists still apply and overlapping
ranges are not allowed. So if the above would result in overlapping
ranges of the same inheritance, the overlapping ranges are merged into a
single range, e.g.:
Last range in NEW_RANGE RESULTING RANGES
RANGELIST
------------- --------- ----------------
4-10 6* 4-10 (Not 4-5, 6, 7-10)
When replacing the last range in RANGELIST, either allocate a new range in
RESULT_POOL or modify the existing range in place. Any new ranges added
to RANGELIST are allocated in RESULT_POOL.
*/
static svn_error_t *
combine_with_lastrange(const svn_merge_range_t *new_range,
svn_rangelist_t *rangelist,
svn_boolean_t consider_inheritance,
apr_pool_t *result_pool)
{
svn_merge_range_t *lastrange;
svn_merge_range_t combined_range;
/* We don't accept a NULL RANGELIST. */
SVN_ERR_ASSERT(rangelist);
if (rangelist->nelts > 0)
lastrange = APR_ARRAY_IDX(rangelist, rangelist->nelts - 1, svn_merge_range_t *);
else
lastrange = NULL;
if (!lastrange)
{
/* No *LASTRANGE so push NEW_RANGE onto RANGELIST and we are done. */
APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) =
svn_merge_range_dup(new_range, result_pool);
}
else if (!consider_inheritance)
{
/* We are not considering inheritance so we can merge intersecting
ranges of different inheritability. Of course if the ranges
don't intersect at all we simply push NEW_RANGE only RANGELIST. */
if (combine_ranges(&combined_range, lastrange, new_range, FALSE))
{
*lastrange = combined_range;
}
else
{
APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) =
svn_merge_range_dup(new_range, result_pool);
}
}
else /* Considering inheritance */
{
if (combine_ranges(&combined_range, lastrange, new_range, TRUE))
{
/* Even when considering inheritance two intersection ranges
of the same inheritability can simply be combined. */
*lastrange = combined_range;
}
else
{
/* If we are here then the ranges either don't intersect or do
intersect but have differing inheritability. Check for the
first case as that is easy to handle. */
intersection_type_t intersection_type;
svn_boolean_t sorted = FALSE;
SVN_ERR(get_type_of_intersection(new_range, lastrange,
&intersection_type));
switch (intersection_type)
{
case svn__no_intersection:
/* NEW_RANGE and *LASTRANGE *really* don't intersect so
just push NEW_RANGE only RANGELIST. */
APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) =
svn_merge_range_dup(new_range, result_pool);
sorted = (svn_sort_compare_ranges(&lastrange,
&new_range) < 0);
break;
case svn__equal_intersection:
/* They range are equal so all we do is force the
inheritability of lastrange to true. */
lastrange->inheritable = TRUE;
sorted = TRUE;
break;
case svn__adjoining_intersection:
/* They adjoin but don't overlap so just push NEW_RANGE
onto RANGELIST. */
APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) =
svn_merge_range_dup(new_range, result_pool);
sorted = (svn_sort_compare_ranges(&lastrange,
&new_range) < 0);
break;
case svn__overlapping_intersection:
/* They ranges overlap but neither is a proper subset of
the other. We'll end up pusing two new ranges onto
RANGELIST, the intersecting part and the part unique to
NEW_RANGE.*/
{
svn_merge_range_t *r1 = svn_merge_range_dup(lastrange,
result_pool);
svn_merge_range_t *r2 = svn_merge_range_dup(new_range,
result_pool);
/* Pop off *LASTRANGE to make our manipulations
easier. */
apr_array_pop(rangelist);
/* Ensure R1 is the older range. */
if (r2->start < r1->start)
{
/* Swap R1 and R2. */
*r2 = *r1;
*r1 = *new_range;
}
/* Absorb the intersecting ranges into the
inheritable range. */
if (r1->inheritable)
r2->start = r1->end;
else
r1->end = r2->start;
/* Push everything back onto RANGELIST. */
APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r1;
sorted = (svn_sort_compare_ranges(&lastrange,
&r1) < 0);
APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r2;
if (sorted)
sorted = (svn_sort_compare_ranges(&r1, &r2) < 0);
break;
}
default: /* svn__proper_subset_intersection */
{
/* One range is a proper subset of the other. */
svn_merge_range_t *r1 = svn_merge_range_dup(lastrange,
result_pool);
svn_merge_range_t *r2 = svn_merge_range_dup(new_range,
result_pool);
svn_merge_range_t *r3 = NULL;
/* Pop off *LASTRANGE to make our manipulations
easier. */
apr_array_pop(rangelist);
/* Ensure R1 is the superset. */
if (r2->start < r1->start || r2->end > r1->end)
{
/* Swap R1 and R2. */
*r2 = *r1;
*r1 = *new_range;
}
if (r1->inheritable)
{
/* The simple case: The superset is inheritable, so
just combine r1 and r2. */
r1->start = MIN(r1->start, r2->start);
r1->end = MAX(r1->end, r2->end);
r2 = NULL;
}
else if (r1->start == r2->start)
{
svn_revnum_t tmp_revnum;
/* *LASTRANGE and NEW_RANGE share an end point. */
tmp_revnum = r1->end;
r1->end = r2->end;
r2->inheritable = r1->inheritable;
r1->inheritable = TRUE;
r2->start = r1->end;
r2->end = tmp_revnum;
}
else if (r1->end == r2->end)
{
/* *LASTRANGE and NEW_RANGE share an end point. */
r1->end = r2->start;
r2->inheritable = TRUE;
}
else
{
/* NEW_RANGE and *LASTRANGE share neither start
nor end points. */
r3 = apr_pcalloc(result_pool, sizeof(*r3));
r3->start = r2->end;
r3->end = r1->end;
r3->inheritable = r1->inheritable;
r2->inheritable = TRUE;
r1->end = r2->start;
}
/* Push everything back onto RANGELIST. */
APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r1;
sorted = (svn_sort_compare_ranges(&lastrange, &r1) < 0);
if (r2)
{
APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r2;
if (sorted)
sorted = (svn_sort_compare_ranges(&r1, &r2) < 0);
}
if (r3)
{
APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r3;
if (sorted)
{
if (r2)
sorted = (svn_sort_compare_ranges(&r2,
&r3) < 0);
else
sorted = (svn_sort_compare_ranges(&r1,
&r3) < 0);
}
}
break;
}
}
/* Some of the above cases might have put *RANGELIST out of
order, so re-sort.*/
if (!sorted)
qsort(rangelist->elts, rangelist->nelts, rangelist->elt_size,
svn_sort_compare_ranges);
}
}
return SVN_NO_ERROR;
}
/* Convert a single svn_merge_range_t *RANGE back into a string. */
static char *
range_to_string(const svn_merge_range_t *range,
apr_pool_t *pool)
{
const char *mark
= range->inheritable ? "" : SVN_MERGEINFO_NONINHERITABLE_STR;
if (range->start == range->end - 1)
return apr_psprintf(pool, "%ld%s", range->end, mark);
else if (range->start - 1 == range->end)
return apr_psprintf(pool, "-%ld%s", range->start, mark);
else if (range->start < range->end)
return apr_psprintf(pool, "%ld-%ld%s", range->start + 1, range->end, mark);
else
return apr_psprintf(pool, "%ld-%ld%s", range->start, range->end + 1, mark);
}
/* Helper for svn_mergeinfo_parse()
Append revision ranges onto the array RANGELIST to represent the range
descriptions found in the string *INPUT. Read only as far as a newline
or the position END, whichever comes first. Set *INPUT to the position
after the last character of INPUT that was used.
revisionlist -> (revisionelement)(COMMA revisionelement)*
revisionrange -> REVISION "-" REVISION("*")
revisionelement -> revisionrange | REVISION("*")
*/
static svn_error_t *
parse_rangelist(const char **input, const char *end,
svn_rangelist_t *rangelist,
apr_pool_t *pool)
{
const char *curr = *input;
/* Eat any leading horizontal white-space before the rangelist. */
while (curr < end && *curr != '\n' && isspace(*curr))
curr++;
if (*curr == '\n' || curr == end)
{
/* Empty range list. */
*input = curr;
return SVN_NO_ERROR;
}
while (curr < end && *curr != '\n')
{
/* Parse individual revisions or revision ranges. */
svn_merge_range_t *mrange = apr_pcalloc(pool, sizeof(*mrange));
svn_revnum_t firstrev;
SVN_ERR(svn_revnum_parse(&firstrev, curr, &curr));
if (*curr != '-' && *curr != '\n' && *curr != ',' && *curr != '*'
&& curr != end)
return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
_("Invalid character '%c' found in revision "
"list"), *curr);
mrange->start = firstrev - 1;
mrange->end = firstrev;
mrange->inheritable = TRUE;
if (firstrev == 0)
return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
_("Invalid revision number '0' found in "
"range list"));
if (*curr == '-')
{
svn_revnum_t secondrev;
curr++;
SVN_ERR(svn_revnum_parse(&secondrev, curr, &curr));
if (firstrev > secondrev)
return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
_("Unable to parse reversed revision "
"range '%ld-%ld'"),
firstrev, secondrev);
else if (firstrev == secondrev)
return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
_("Unable to parse revision range "
"'%ld-%ld' with same start and end "
"revisions"), firstrev, secondrev);
mrange->end = secondrev;
}
if (*curr == '\n' || curr == end)
{
APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange;
*input = curr;
return SVN_NO_ERROR;
}
else if (*curr == ',')
{
APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange;
curr++;
}
else if (*curr == '*')
{
mrange->inheritable = FALSE;
curr++;
if (*curr == ',' || *curr == '\n' || curr == end)
{
APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange;
if (*curr == ',')
{
curr++;
}
else
{
*input = curr;
return SVN_NO_ERROR;
}
}
else
{
return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
_("Invalid character '%c' found in "
"range list"), *curr);
}
}
else
{
return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
_("Invalid character '%c' found in "
"range list"), *curr);
}
}
if (*curr != '\n')
return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
_("Range list parsing ended before hitting "
"newline"));
*input = curr;
return SVN_NO_ERROR;
}
svn_error_t *
svn_rangelist__parse(svn_rangelist_t **rangelist,
const char *str,
apr_pool_t *result_pool)
{
const char *s = str;
*rangelist = apr_array_make(result_pool, 1, sizeof(svn_merge_range_t *));
SVN_ERR(parse_rangelist(&s, s + strlen(s), *rangelist, result_pool));
return SVN_NO_ERROR;
}
+/* Return TRUE, if all ranges in RANGELIST are in ascending order and do
+ * not overlap and are not adjacent.
+ *
+ * ### Can yield false negatives: ranges of differing inheritance are
+ * allowed to be adjacent.
+ *
+ * If this returns FALSE, you probaly want to qsort() the
+ * ranges and then call svn_rangelist__combine_adjacent_ranges().
+ */
+static svn_boolean_t
+is_rangelist_normalized(svn_rangelist_t *rangelist)
+{
+ int i;
+ svn_merge_range_t **ranges = (svn_merge_range_t **)rangelist->elts;
+
+ for (i = 0; i < rangelist->nelts-1; ++i)
+ if (ranges[i]->end >= ranges[i+1]->start)
+ return FALSE;
+
+ return TRUE;
+}
+
svn_error_t *
+svn_rangelist__canonicalize(svn_rangelist_t *rangelist,
+ apr_pool_t *scratch_pool)
+{
+ if (! is_rangelist_normalized(rangelist))
+ {
+ qsort(rangelist->elts, rangelist->nelts, rangelist->elt_size,
+ svn_sort_compare_ranges);
+
+ SVN_ERR(svn_rangelist__combine_adjacent_ranges(rangelist, scratch_pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
svn_rangelist__combine_adjacent_ranges(svn_rangelist_t *rangelist,
apr_pool_t *scratch_pool)
{
int i;
svn_merge_range_t *range, *lastrange;
lastrange = APR_ARRAY_IDX(rangelist, 0, svn_merge_range_t *);
for (i = 1; i < rangelist->nelts; i++)
{
range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
if (lastrange->start <= range->end
&& range->start <= lastrange->end)
{
/* The ranges are adjacent or intersect. */
/* svn_mergeinfo_parse promises to combine overlapping
ranges as long as their inheritability is the same. */
if (range->start < lastrange->end
&& range->inheritable != lastrange->inheritable)
{
return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
_("Unable to parse overlapping "
"revision ranges '%s' and '%s' "
"with different inheritance "
"types"),
range_to_string(lastrange,
scratch_pool),
range_to_string(range,
scratch_pool));
}
/* Combine overlapping or adjacent ranges with the
same inheritability. */
if (lastrange->inheritable == range->inheritable)
{
lastrange->end = MAX(range->end, lastrange->end);
svn_sort__array_delete(rangelist, i, 1);
i--;
}
}
lastrange = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
}
return SVN_NO_ERROR;
}
/* revisionline -> PATHNAME COLON revisionlist */
static svn_error_t *
parse_revision_line(const char **input, const char *end, svn_mergeinfo_t hash,
apr_pool_t *scratch_pool)
{
const char *pathname = "";
apr_ssize_t klen;
svn_rangelist_t *existing_rangelist;
svn_rangelist_t *rangelist = apr_array_make(scratch_pool, 1,
sizeof(svn_merge_range_t *));
SVN_ERR(parse_pathname(input, end, &pathname, scratch_pool));
if (*(*input) != ':')
return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
_("Pathname not terminated by ':'"));
*input = *input + 1;
SVN_ERR(parse_rangelist(input, end, rangelist, scratch_pool));
if (rangelist->nelts == 0)
return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
_("Mergeinfo for '%s' maps to an "
"empty revision range"), pathname);
if (*input != end && *(*input) != '\n')
return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
_("Could not find end of line in range list line "
"in '%s'"), *input);
if (*input != end)
*input = *input + 1;
- /* Sort the rangelist, combine adjacent ranges into single ranges,
- and make sure there are no overlapping ranges. */
- if (rangelist->nelts > 1)
- {
- qsort(rangelist->elts, rangelist->nelts, rangelist->elt_size,
- svn_sort_compare_ranges);
+ /* Sort the rangelist, combine adjacent ranges into single ranges, and
+ make sure there are no overlapping ranges. Luckily, most data in
+ svn:mergeinfo will already be in normalized form and this will be quick.
+ */
+ SVN_ERR(svn_rangelist__canonicalize(rangelist, scratch_pool));
- SVN_ERR(svn_rangelist__combine_adjacent_ranges(rangelist, scratch_pool));
- }
-
/* Handle any funky mergeinfo with relative merge source paths that
might exist due to issue #3547. It's possible that this issue allowed
the creation of mergeinfo with path keys that differ only by a
leading slash, e.g. "trunk:4033\n/trunk:4039-4995". In the event
we encounter this we merge the rangelists together under a single
absolute path key. */
klen = strlen(pathname);
existing_rangelist = apr_hash_get(hash, pathname, klen);
if (existing_rangelist)
SVN_ERR(svn_rangelist_merge2(rangelist, existing_rangelist,
scratch_pool, scratch_pool));
apr_hash_set(hash, apr_pstrmemdup(apr_hash_pool_get(hash), pathname, klen),
klen, svn_rangelist_dup(rangelist, apr_hash_pool_get(hash)));
return SVN_NO_ERROR;
}
/* top -> revisionline (NEWLINE revisionline)* */
static svn_error_t *
parse_top(const char **input, const char *end, svn_mergeinfo_t hash,
apr_pool_t *pool)
{
apr_pool_t *iterpool = svn_pool_create(pool);
while (*input < end)
{
svn_pool_clear(iterpool);
SVN_ERR(parse_revision_line(input, end, hash, iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_mergeinfo_parse(svn_mergeinfo_t *mergeinfo,
const char *input,
apr_pool_t *pool)
{
svn_error_t *err;
*mergeinfo = svn_hash__make(pool);
err = parse_top(&input, input + strlen(input), *mergeinfo, pool);
/* Always return SVN_ERR_MERGEINFO_PARSE_ERROR as the topmost error. */
if (err && err->apr_err != SVN_ERR_MERGEINFO_PARSE_ERROR)
err = svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, err,
_("Could not parse mergeinfo string '%s'"),
input);
return err;
}
/* Cleanup after svn_rangelist_merge2 when it modifies the ending range of
a single rangelist element in-place.
If *RANGE_INDEX is not a valid element in RANGELIST do nothing. Otherwise
ensure that RANGELIST[*RANGE_INDEX]->END does not adjoin or overlap any
subsequent ranges in RANGELIST.
If overlap is found, then remove, modify, and/or add elements to RANGELIST
as per the invariants for rangelists documented in svn_mergeinfo.h. If
RANGELIST[*RANGE_INDEX]->END adjoins a subsequent element then combine the
elements if their inheritability permits -- The inheritance of intersecting
and adjoining ranges is handled as per svn_mergeinfo_merge2. Upon return
set *RANGE_INDEX to the index of the youngest element modified, added, or
adjoined to RANGELIST[*RANGE_INDEX].
Note: Adjoining rangelist elements are those where the end rev of the older
element is equal to the start rev of the younger element.
Any new elements inserted into RANGELIST are allocated in RESULT_POOL.*/
static void
adjust_remaining_ranges(svn_rangelist_t *rangelist,
int *range_index,
apr_pool_t *result_pool)
{
int i;
int starting_index;
int elements_to_delete = 0;
svn_merge_range_t *modified_range;
if (*range_index >= rangelist->nelts)
return;
starting_index = *range_index + 1;
modified_range = APR_ARRAY_IDX(rangelist, *range_index, svn_merge_range_t *);
for (i = *range_index + 1; i < rangelist->nelts; i++)
{
svn_merge_range_t *next_range = APR_ARRAY_IDX(rangelist, i,
svn_merge_range_t *);
/* If MODIFIED_RANGE doesn't adjoin or overlap the next range in
RANGELIST then we are finished. */
if (modified_range->end < next_range->start)
break;
/* Does MODIFIED_RANGE adjoin NEXT_RANGE? */
if (modified_range->end == next_range->start)
{
if (modified_range->inheritable == next_range->inheritable)
{
/* Combine adjoining ranges with the same inheritability. */
modified_range->end = next_range->end;
elements_to_delete++;
}
else
{
/* Cannot join because inheritance differs. */
(*range_index)++;
}
break;
}
/* Alright, we know MODIFIED_RANGE overlaps NEXT_RANGE, but how? */
if (modified_range->end > next_range->end)
{
/* NEXT_RANGE is a proper subset of MODIFIED_RANGE and the two
don't share the same end range. */
if (modified_range->inheritable
|| (modified_range->inheritable == next_range->inheritable))
{
/* MODIFIED_RANGE absorbs NEXT_RANGE. */
elements_to_delete++;
}
else
{
/* NEXT_RANGE is a proper subset MODIFIED_RANGE but
MODIFIED_RANGE is non-inheritable and NEXT_RANGE is
inheritable. This means MODIFIED_RANGE is truncated,
NEXT_RANGE remains, and the portion of MODIFIED_RANGE
younger than NEXT_RANGE is added as a separate range:
______________________________________________
| |
M MODIFIED_RANGE N
| (!inhertiable) |
|______________________________________________|
| |
O NEXT_RANGE P
| (inheritable)|
|______________|
|
V
_______________________________________________
| | | |
M MODIFIED_RANGE O NEXT_RANGE P NEW_RANGE N
| (!inhertiable) | (inheritable)| (!inheritable)|
|________________|______________|_______________|
*/
svn_merge_range_t *new_modified_range =
apr_palloc(result_pool, sizeof(*new_modified_range));
new_modified_range->start = next_range->end;
new_modified_range->end = modified_range->end;
new_modified_range->inheritable = FALSE;
modified_range->end = next_range->start;
(*range_index)+=2;
svn_sort__array_insert(&new_modified_range, rangelist,
*range_index);
/* Recurse with the new range. */
adjust_remaining_ranges(rangelist, range_index, result_pool);
break;
}
}
else if (modified_range->end == next_range->end)
{
/* NEXT_RANGE is a proper subset MODIFIED_RANGE and share
the same end range. */
if (modified_range->inheritable
|| (modified_range->inheritable == next_range->inheritable))
{
/* MODIFIED_RANGE absorbs NEXT_RANGE. */
elements_to_delete++;
}
else
{
/* The intersection between MODIFIED_RANGE and NEXT_RANGE is
absorbed by the latter. */
modified_range->end = next_range->start;
(*range_index)++;
}
break;
}
else
{
/* NEXT_RANGE and MODIFIED_RANGE intersect but NEXT_RANGE is not
a proper subset of MODIFIED_RANGE, nor do the two share the
same end revision, i.e. they overlap. */
if (modified_range->inheritable == next_range->inheritable)
{
/* Combine overlapping ranges with the same inheritability. */
modified_range->end = next_range->end;
elements_to_delete++;
}
else if (modified_range->inheritable)
{
/* MODIFIED_RANGE absorbs the portion of NEXT_RANGE it overlaps
and NEXT_RANGE is truncated. */
next_range->start = modified_range->end;
(*range_index)++;
}
else
{
/* NEXT_RANGE absorbs the portion of MODIFIED_RANGE it overlaps
and MODIFIED_RANGE is truncated. */
modified_range->end = next_range->start;
(*range_index)++;
}
break;
}
}
if (elements_to_delete)
svn_sort__array_delete(rangelist, starting_index, elements_to_delete);
}
svn_error_t *
svn_rangelist_merge2(svn_rangelist_t *rangelist,
const svn_rangelist_t *changes,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
int i = 0;
int j = 0;
/* We may modify CHANGES, so make a copy in SCRATCH_POOL. */
changes = svn_rangelist_dup(changes, scratch_pool);
while (i < rangelist->nelts && j < changes->nelts)
{
svn_merge_range_t *range =
APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
svn_merge_range_t *change =
APR_ARRAY_IDX(changes, j, svn_merge_range_t *);
int res = svn_sort_compare_ranges(&range, &change);
if (res == 0)
{
/* Only when merging two non-inheritable ranges is the result also
non-inheritable. In all other cases ensure an inheritiable
result. */
if (range->inheritable || change->inheritable)
range->inheritable = TRUE;
i++;
j++;
}
else if (res < 0) /* CHANGE is younger than RANGE */
{
if (range->end < change->start)
{
/* RANGE is older than CHANGE and the two do not
adjoin or overlap */
i++;
}
else if (range->end == change->start)
{
/* RANGE and CHANGE adjoin */
if (range->inheritable == change->inheritable)
{
/* RANGE and CHANGE have the same inheritability so
RANGE expands to absord CHANGE. */
range->end = change->end;
adjust_remaining_ranges(rangelist, &i, result_pool);
j++;
}
else
{
/* RANGE and CHANGE adjoin, but have different
inheritability. Since RANGE is older, just
move on to the next RANGE. */
i++;
}
}
else
{
/* RANGE and CHANGE overlap, but how? */
if ((range->inheritable == change->inheritable)
|| range->inheritable)
{
/* If CHANGE is a proper subset of RANGE, it absorbs RANGE
with no adjustment otherwise only the intersection is
absorbed and CHANGE is truncated. */
if (range->end >= change->end)
j++;
else
change->start = range->end;
}
else
{
/* RANGE is non-inheritable and CHANGE is inheritable. */
if (range->start < change->start)
{
/* CHANGE absorbs intersection with RANGE and RANGE
is truncated. */
svn_merge_range_t *range_copy =
svn_merge_range_dup(range, result_pool);
range_copy->end = change->start;
range->start = change->start;
svn_sort__array_insert(&range_copy, rangelist, i++);
}
else
{
/* CHANGE and RANGE share the same start rev, but
RANGE is considered older because its end rev
is older. */
range->inheritable = TRUE;
change->start = range->end;
}
}
}
}
else /* res > 0, CHANGE is older than RANGE */
{
if (change->end < range->start)
{
/* CHANGE is older than RANGE and the two do not
adjoin or overlap, so insert a copy of CHANGE
into RANGELIST. */
svn_merge_range_t *change_copy =
svn_merge_range_dup(change, result_pool);
svn_sort__array_insert(&change_copy, rangelist, i++);
j++;
}
else if (change->end == range->start)
{
/* RANGE and CHANGE adjoin */
if (range->inheritable == change->inheritable)
{
/* RANGE and CHANGE have the same inheritability so we
can simply combine the two in place. */
range->start = change->start;
j++;
}
else
{
/* RANGE and CHANGE have different inheritability so insert
a copy of CHANGE into RANGELIST. */
svn_merge_range_t *change_copy =
svn_merge_range_dup(change, result_pool);
svn_sort__array_insert(&change_copy, rangelist, i);
j++;
}
}
else
{
/* RANGE and CHANGE overlap. */
if (range->inheritable == change->inheritable)
{
/* RANGE and CHANGE have the same inheritability so we
can simply combine the two in place... */
range->start = change->start;
if (range->end < change->end)
{
/* ...but if RANGE is expanded ensure that we don't
violate any rangelist invariants. */
range->end = change->end;
adjust_remaining_ranges(rangelist, &i, result_pool);
}
j++;
}
else if (range->inheritable)
{
if (change->start < range->start)
{
/* RANGE is inheritable so absorbs any part of CHANGE
it overlaps. CHANGE is truncated and the remainder
inserted into RANGELIST. */
svn_merge_range_t *change_copy =
svn_merge_range_dup(change, result_pool);
change_copy->end = range->start;
change->start = range->start;
svn_sort__array_insert(&change_copy, rangelist, i++);
}
else
{
/* CHANGE and RANGE share the same start rev, but
CHANGE is considered older because CHANGE->END is
older than RANGE->END. */
j++;
}
}
else
{
/* RANGE is non-inheritable and CHANGE is inheritable. */
if (change->start < range->start)
{
if (change->end == range->end)
{
/* RANGE is a proper subset of CHANGE and share the
same end revision, so set RANGE equal to CHANGE. */
range->start = change->start;
range->inheritable = TRUE;
j++;
}
else if (change->end > range->end)
{
/* RANGE is a proper subset of CHANGE and CHANGE has
a younger end revision, so set RANGE equal to its
intersection with CHANGE and truncate CHANGE. */
range->start = change->start;
range->inheritable = TRUE;
change->start = range->end;
}
else
{
/* CHANGE and RANGE overlap. Set RANGE equal to its
intersection with CHANGE and take the remainder
of RANGE and insert it into RANGELIST. */
svn_merge_range_t *range_copy =
svn_merge_range_dup(range, result_pool);
range_copy->start = change->end;
range->start = change->start;
range->end = change->end;
range->inheritable = TRUE;
svn_sort__array_insert(&range_copy, rangelist, ++i);
j++;
}
}
else
{
/* CHANGE and RANGE share the same start rev, but
CHANGE is considered older because its end rev
is older.
Insert the intersection of RANGE and CHANGE into
RANGELIST and then set RANGE to the non-intersecting
portion of RANGE. */
svn_merge_range_t *range_copy =
svn_merge_range_dup(range, result_pool);
range_copy->end = change->end;
range_copy->inheritable = TRUE;
range->start = change->end;
svn_sort__array_insert(&range_copy, rangelist, i++);
j++;
}
}
}
}
}
/* Copy any remaining elements in CHANGES into RANGELIST. */
for (; j < (changes)->nelts; j++)
{
svn_merge_range_t *change =
APR_ARRAY_IDX(changes, j, svn_merge_range_t *);
svn_merge_range_t *change_copy = svn_merge_range_dup(change,
result_pool);
svn_sort__array_insert(&change_copy, rangelist, rangelist->nelts);
}
return SVN_NO_ERROR;
}
/* Return TRUE iff the forward revision ranges FIRST and SECOND overlap and
* (if CONSIDER_INHERITANCE is TRUE) have the same inheritability. */
static svn_boolean_t
range_intersect(const svn_merge_range_t *first, const svn_merge_range_t *second,
svn_boolean_t consider_inheritance)
{
SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(first));
SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(second));
return (first->start + 1 <= second->end)
&& (second->start + 1 <= first->end)
&& (!consider_inheritance
|| (!(first->inheritable) == !(second->inheritable)));
}
/* Return TRUE iff the forward revision range FIRST wholly contains the
* forward revision range SECOND and (if CONSIDER_INHERITANCE is TRUE) has
* the same inheritability. */
static svn_boolean_t
range_contains(const svn_merge_range_t *first, const svn_merge_range_t *second,
svn_boolean_t consider_inheritance)
{
SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(first));
SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(second));
return (first->start <= second->start) && (second->end <= first->end)
&& (!consider_inheritance
|| (!(first->inheritable) == !(second->inheritable)));
}
/* Swap start and end fields of RANGE. */
static void
range_swap_endpoints(svn_merge_range_t *range)
{
svn_revnum_t swap = range->start;
range->start = range->end;
range->end = swap;
}
svn_error_t *
svn_rangelist_reverse(svn_rangelist_t *rangelist, apr_pool_t *pool)
{
int i;
svn_sort__array_reverse(rangelist, pool);
for (i = 0; i < rangelist->nelts; i++)
{
range_swap_endpoints(APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *));
}
return SVN_NO_ERROR;
}
void
svn_rangelist__set_inheritance(svn_rangelist_t *rangelist,
svn_boolean_t inheritable)
{
if (rangelist)
{
int i;
svn_merge_range_t *range;
for (i = 0; i < rangelist->nelts; i++)
{
range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
range->inheritable = inheritable;
}
}
return;
}
void
svn_mergeinfo__set_inheritance(svn_mergeinfo_t mergeinfo,
svn_boolean_t inheritable,
apr_pool_t *scratch_pool)
{
if (mergeinfo)
{
apr_hash_index_t *hi;
for (hi = apr_hash_first(scratch_pool, mergeinfo);
hi;
hi = apr_hash_next(hi))
{
svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
if (rangelist)
svn_rangelist__set_inheritance(rangelist, inheritable);
}
}
return;
}
/* If DO_REMOVE is true, then remove any overlapping ranges described by
RANGELIST1 from RANGELIST2 and place the results in *OUTPUT. When
DO_REMOVE is true, RANGELIST1 is effectively the "eraser" and RANGELIST2
the "whiteboard".
If DO_REMOVE is false, then capture the intersection between RANGELIST1
and RANGELIST2 and place the results in *OUTPUT. The ordering of
RANGELIST1 and RANGELIST2 doesn't matter when DO_REMOVE is false.
If CONSIDER_INHERITANCE is true, then take the inheritance of the
ranges in RANGELIST1 and RANGELIST2 into account when comparing them
for intersection, see the doc string for svn_rangelist_intersect().
If CONSIDER_INHERITANCE is false, then ranges with differing inheritance
may intersect, but the resulting intersection is non-inheritable only
if both ranges were non-inheritable, e.g.:
RANGELIST1 RANGELIST2 CONSIDER DO_REMOVE *OUTPUT
INHERITANCE
---------- ------ ----------- --------- -------
90-420* 1-100 TRUE FALSE Empty Rangelist
90-420 1-100* TRUE FALSE Empty Rangelist
90-420 1-100 TRUE FALSE 90-100
90-420* 1-100* TRUE FALSE 90-100*
90-420* 1-100 FALSE FALSE 90-100
90-420 1-100* FALSE FALSE 90-100
90-420 1-100 FALSE FALSE 90-100
90-420* 1-100* FALSE FALSE 90-100*
Allocate the contents of *OUTPUT in POOL. */
static svn_error_t *
rangelist_intersect_or_remove(svn_rangelist_t **output,
const svn_rangelist_t *rangelist1,
const svn_rangelist_t *rangelist2,
svn_boolean_t do_remove,
svn_boolean_t consider_inheritance,
apr_pool_t *pool)
{
int i1, i2, lasti2;
svn_merge_range_t working_elt2;
*output = apr_array_make(pool, 1, sizeof(svn_merge_range_t *));
i1 = 0;
i2 = 0;
lasti2 = -1; /* Initialized to a value that "i2" will never be. */
while (i1 < rangelist1->nelts && i2 < rangelist2->nelts)
{
svn_merge_range_t *elt1, *elt2;
elt1 = APR_ARRAY_IDX(rangelist1, i1, svn_merge_range_t *);
/* Instead of making a copy of the entire array of rangelist2
elements, we just keep a copy of the current rangelist2 element
that needs to be used, and modify our copy if necessary. */
if (i2 != lasti2)
{
working_elt2 =
*(APR_ARRAY_IDX(rangelist2, i2, svn_merge_range_t *));
lasti2 = i2;
}
elt2 = &working_elt2;
/* If the rangelist2 range is contained completely in the
rangelist1, we increment the rangelist2.
If the ranges intersect, and match exactly, we increment both
rangelist1 and rangelist2.
Otherwise, we have to generate a range for the left part of
the removal of rangelist1 from rangelist2, and possibly change
the rangelist2 to the remaining portion of the right part of
the removal, to test against. */
if (range_contains(elt1, elt2, consider_inheritance))
{
if (!do_remove)
{
svn_merge_range_t tmp_range;
tmp_range.start = elt2->start;
tmp_range.end = elt2->end;
/* The intersection of two ranges is non-inheritable only
if both ranges are non-inheritable. */
tmp_range.inheritable =
(elt2->inheritable || elt1->inheritable);
SVN_ERR(combine_with_lastrange(&tmp_range, *output,
consider_inheritance,
pool));
}
i2++;
if (elt2->start == elt1->start && elt2->end == elt1->end)
i1++;
}
else if (range_intersect(elt1, elt2, consider_inheritance))
{
if (elt2->start < elt1->start)
{
/* The rangelist2 range starts before the rangelist1 range. */
svn_merge_range_t tmp_range;
if (do_remove)
{
/* Retain the range that falls before the rangelist1
start. */
tmp_range.start = elt2->start;
tmp_range.end = elt1->start;
tmp_range.inheritable = elt2->inheritable;
}
else
{
/* Retain the range that falls between the rangelist1
start and rangelist2 end. */
tmp_range.start = elt1->start;
tmp_range.end = MIN(elt2->end, elt1->end);
/* The intersection of two ranges is non-inheritable only
if both ranges are non-inheritable. */
tmp_range.inheritable =
(elt2->inheritable || elt1->inheritable);
}
SVN_ERR(combine_with_lastrange(&tmp_range,
*output, consider_inheritance,
pool));
}
/* Set up the rest of the rangelist2 range for further
processing. */
if (elt2->end > elt1->end)
{
/* The rangelist2 range ends after the rangelist1 range. */
if (!do_remove)
{
/* Partial overlap. */
svn_merge_range_t tmp_range;
tmp_range.start = MAX(elt2->start, elt1->start);
tmp_range.end = elt1->end;
/* The intersection of two ranges is non-inheritable only
if both ranges are non-inheritable. */
tmp_range.inheritable =
(elt2->inheritable || elt1->inheritable);
SVN_ERR(combine_with_lastrange(&tmp_range,
*output,
consider_inheritance,
pool));
}
working_elt2.start = elt1->end;
working_elt2.end = elt2->end;
}
else
i2++;
}
else /* ranges don't intersect */
{
/* See which side of the rangelist2 the rangelist1 is on. If it
is on the left side, we need to move the rangelist1.
If it is on past the rangelist2 on the right side, we
need to output the rangelist2 and increment the
rangelist2. */
if (svn_sort_compare_ranges(&elt1, &elt2) < 0)
i1++;
else
{
svn_merge_range_t *lastrange;
if ((*output)->nelts > 0)
lastrange = APR_ARRAY_IDX(*output, (*output)->nelts - 1,
svn_merge_range_t *);
else
lastrange = NULL;
if (do_remove && !(lastrange &&
combine_ranges(lastrange, lastrange, elt2,
consider_inheritance)))
{
lastrange = svn_merge_range_dup(elt2, pool);
APR_ARRAY_PUSH(*output, svn_merge_range_t *) = lastrange;
}
i2++;
}
}
}
if (do_remove)
{
/* Copy the current rangelist2 element if we didn't hit the end
of the rangelist2, and we still had it around. This element
may have been touched, so we can't just walk the rangelist2
array, we have to use our copy. This case only happens when
we ran out of rangelist1 before rangelist2, *and* we had changed
the rangelist2 element. */
if (i2 == lasti2 && i2 < rangelist2->nelts)
{
SVN_ERR(combine_with_lastrange(&working_elt2, *output,
consider_inheritance, pool));
i2++;
}
/* Copy any other remaining untouched rangelist2 elements. */
for (; i2 < rangelist2->nelts; i2++)
{
svn_merge_range_t *elt = APR_ARRAY_IDX(rangelist2, i2,
svn_merge_range_t *);
SVN_ERR(combine_with_lastrange(elt, *output,
consider_inheritance, pool));
}
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_rangelist_intersect(svn_rangelist_t **output,
const svn_rangelist_t *rangelist1,
const svn_rangelist_t *rangelist2,
svn_boolean_t consider_inheritance,
apr_pool_t *pool)
{
return rangelist_intersect_or_remove(output, rangelist1, rangelist2, FALSE,
consider_inheritance, pool);
}
svn_error_t *
svn_rangelist_remove(svn_rangelist_t **output,
const svn_rangelist_t *eraser,
const svn_rangelist_t *whiteboard,
svn_boolean_t consider_inheritance,
apr_pool_t *pool)
{
return rangelist_intersect_or_remove(output, eraser, whiteboard, TRUE,
consider_inheritance, pool);
}
svn_error_t *
svn_rangelist_diff(svn_rangelist_t **deleted, svn_rangelist_t **added,
const svn_rangelist_t *from, const svn_rangelist_t *to,
svn_boolean_t consider_inheritance,
apr_pool_t *pool)
{
/* The following diagrams illustrate some common range delta scenarios:
(from) deleted
r0 <===========(=========)============[=========]===========> rHEAD
[to] added
(from) deleted deleted
r0 <===========(=========[============]=========)===========> rHEAD
[to]
(from) deleted
r0 <===========(=========[============)=========]===========> rHEAD
[to] added
(from) deleted
r0 <===========[=========(============]=========)===========> rHEAD
[to] added
(from)
r0 <===========[=========(============)=========]===========> rHEAD
[to] added added
(from) d d d
r0 <===(=[=)=]=[==]=[=(=)=]=[=]=[=(===|===(=)==|=|==[=(=]=)=> rHEAD
[to] a a a a a a a
*/
/* The items that are present in from, but not in to, must have been
deleted. */
SVN_ERR(svn_rangelist_remove(deleted, to, from, consider_inheritance,
pool));
/* The items that are present in to, but not in from, must have been
added. */
return svn_rangelist_remove(added, from, to, consider_inheritance, pool);
}
struct mergeinfo_diff_baton
{
svn_mergeinfo_t from;
svn_mergeinfo_t to;
svn_mergeinfo_t deleted;
svn_mergeinfo_t added;
svn_boolean_t consider_inheritance;
apr_pool_t *pool;
};
/* This implements the 'svn_hash_diff_func_t' interface.
BATON is of type 'struct mergeinfo_diff_baton *'.
*/
static svn_error_t *
mergeinfo_hash_diff_cb(const void *key, apr_ssize_t klen,
enum svn_hash_diff_key_status status,
void *baton)
{
/* hash_a is FROM mergeinfo,
hash_b is TO mergeinfo. */
struct mergeinfo_diff_baton *cb = baton;
svn_rangelist_t *from_rangelist, *to_rangelist;
const char *path = key;
if (status == svn_hash_diff_key_both)
{
/* Record any deltas (additions or deletions). */
svn_rangelist_t *deleted_rangelist, *added_rangelist;
from_rangelist = apr_hash_get(cb->from, path, klen);
to_rangelist = apr_hash_get(cb->to, path, klen);
SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist,
from_rangelist, to_rangelist,
cb->consider_inheritance, cb->pool));
if (cb->deleted && deleted_rangelist->nelts > 0)
apr_hash_set(cb->deleted, apr_pstrmemdup(cb->pool, path, klen),
klen, deleted_rangelist);
if (cb->added && added_rangelist->nelts > 0)
apr_hash_set(cb->added, apr_pstrmemdup(cb->pool, path, klen),
klen, added_rangelist);
}
else if ((status == svn_hash_diff_key_a) && cb->deleted)
{
from_rangelist = apr_hash_get(cb->from, path, klen);
apr_hash_set(cb->deleted, apr_pstrmemdup(cb->pool, path, klen), klen,
svn_rangelist_dup(from_rangelist, cb->pool));
}
else if ((status == svn_hash_diff_key_b) && cb->added)
{
to_rangelist = apr_hash_get(cb->to, path, klen);
apr_hash_set(cb->added, apr_pstrmemdup(cb->pool, path, klen), klen,
svn_rangelist_dup(to_rangelist, cb->pool));
}
return SVN_NO_ERROR;
}
/* Record deletions and additions of entire range lists (by path
presence), and delegate to svn_rangelist_diff() for delta
calculations on a specific path. */
static svn_error_t *
walk_mergeinfo_hash_for_diff(svn_mergeinfo_t from, svn_mergeinfo_t to,
svn_mergeinfo_t deleted, svn_mergeinfo_t added,
svn_boolean_t consider_inheritance,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
struct mergeinfo_diff_baton mdb;
mdb.from = from;
mdb.to = to;
mdb.deleted = deleted;
mdb.added = added;
mdb.consider_inheritance = consider_inheritance;
mdb.pool = result_pool;
return svn_hash_diff(from, to, mergeinfo_hash_diff_cb, &mdb, scratch_pool);
}
svn_error_t *
svn_mergeinfo_diff2(svn_mergeinfo_t *deleted, svn_mergeinfo_t *added,
svn_mergeinfo_t from, svn_mergeinfo_t to,
svn_boolean_t consider_inheritance,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
if (from && to == NULL)
{
*deleted = svn_mergeinfo_dup(from, result_pool);
*added = svn_hash__make(result_pool);
}
else if (from == NULL && to)
{
*deleted = svn_hash__make(result_pool);
*added = svn_mergeinfo_dup(to, result_pool);
}
else
{
*deleted = svn_hash__make(result_pool);
*added = svn_hash__make(result_pool);
if (from && to)
{
SVN_ERR(walk_mergeinfo_hash_for_diff(from, to, *deleted, *added,
consider_inheritance,
result_pool, scratch_pool));
}
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_mergeinfo__equals(svn_boolean_t *is_equal,
svn_mergeinfo_t info1,
svn_mergeinfo_t info2,
svn_boolean_t consider_inheritance,
apr_pool_t *pool)
{
apr_hash_index_t *hi;
*is_equal = FALSE;
/* special cases: at least one side has no merge info */
if (info1 == NULL && info2 == NULL)
{
*is_equal = TRUE;
return SVN_NO_ERROR;
}
if (info1 == NULL || info2 == NULL)
return SVN_NO_ERROR;
/* trivial case: different number of paths -> unequal */
if (apr_hash_count(info1) != apr_hash_count(info2))
return SVN_NO_ERROR;
/* compare range lists for all paths */
for (hi = apr_hash_first(pool, info1); hi; hi = apr_hash_next(hi))
{
const char *key;
apr_ssize_t key_length;
svn_rangelist_t *lhs, *rhs;
int i;
svn_rangelist_t *deleted, *added;
/* get both path lists */
apr_hash_this(hi, (const void**)&key, &key_length, (void **)&lhs);
rhs = apr_hash_get(info2, key, key_length);
/* missing on one side? */
if (rhs == NULL)
return SVN_NO_ERROR;
/* quick compare: the range lists will often be a perfect match */
if (lhs->nelts == rhs->nelts)
{
for (i = 0; i < lhs->nelts; ++i)
{
svn_merge_range_t *lrange
= APR_ARRAY_IDX(lhs, i, svn_merge_range_t *);
svn_merge_range_t *rrange
= APR_ARRAY_IDX(rhs, i, svn_merge_range_t *);
/* range mismatch? -> needs detailed comparison */
if ( lrange->start != rrange->start
|| lrange->end != rrange->end)
break;
/* inheritance mismatch? -> merge info differs */
if ( consider_inheritance
&& lrange->inheritable != rrange->inheritable)
return SVN_NO_ERROR;
}
/* all ranges found to match -> next path */
if (i == lhs->nelts)
continue;
}
/* range lists differ but there are many ways to sort and aggregate
revisions into ranges. Do a full diff on them. */
SVN_ERR(svn_rangelist_diff(&deleted, &added, lhs, rhs,
consider_inheritance, pool));
if (deleted->nelts || added->nelts)
return SVN_NO_ERROR;
}
/* no mismatch found */
*is_equal = TRUE;
return SVN_NO_ERROR;
}
svn_error_t *
svn_mergeinfo_merge2(svn_mergeinfo_t mergeinfo,
svn_mergeinfo_t changes,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_hash_index_t *hi;
apr_pool_t *iterpool;
if (!apr_hash_count(changes))
return SVN_NO_ERROR;
iterpool = svn_pool_create(scratch_pool);
for (hi = apr_hash_first(scratch_pool, changes); hi; hi = apr_hash_next(hi))
{
const char *key;
apr_ssize_t klen;
svn_rangelist_t *to_insert;
svn_rangelist_t *target;
/* get ranges to insert and the target ranges list of that insertion */
apr_hash_this(hi, (const void**)&key, &klen, (void*)&to_insert);
target = apr_hash_get(mergeinfo, key, klen);
/* if range list exists, just expand on it.
* Otherwise, add new hash entry. */
if (target)
{
SVN_ERR(svn_rangelist_merge2(target, to_insert, result_pool,
iterpool));
svn_pool_clear(iterpool);
}
else
apr_hash_set(mergeinfo, key, klen, to_insert);
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_mergeinfo_catalog_merge(svn_mergeinfo_catalog_t mergeinfo_cat,
svn_mergeinfo_catalog_t changes_cat,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
int i = 0;
int j = 0;
apr_array_header_t *sorted_cat =
svn_sort__hash(mergeinfo_cat, svn_sort_compare_items_as_paths,
scratch_pool);
apr_array_header_t *sorted_changes =
svn_sort__hash(changes_cat, svn_sort_compare_items_as_paths,
scratch_pool);
while (i < sorted_cat->nelts && j < sorted_changes->nelts)
{
svn_sort__item_t cat_elt, change_elt;
int res;
cat_elt = APR_ARRAY_IDX(sorted_cat, i, svn_sort__item_t);
change_elt = APR_ARRAY_IDX(sorted_changes, j, svn_sort__item_t);
res = svn_sort_compare_items_as_paths(&cat_elt, &change_elt);
if (res == 0) /* Both catalogs have mergeinfo for a given path. */
{
svn_mergeinfo_t mergeinfo = cat_elt.value;
svn_mergeinfo_t changes_mergeinfo = change_elt.value;
SVN_ERR(svn_mergeinfo_merge2(mergeinfo, changes_mergeinfo,
result_pool, scratch_pool));
apr_hash_set(mergeinfo_cat, cat_elt.key, cat_elt.klen, mergeinfo);
i++;
j++;
}
else if (res < 0) /* Only MERGEINFO_CAT has mergeinfo for this path. */
{
i++;
}
else /* Only CHANGES_CAT has mergeinfo for this path. */
{
apr_hash_set(mergeinfo_cat,
apr_pstrdup(result_pool, change_elt.key),
change_elt.klen,
svn_mergeinfo_dup(change_elt.value, result_pool));
j++;
}
}
/* Copy back any remaining elements from the CHANGES_CAT catalog. */
for (; j < sorted_changes->nelts; j++)
{
svn_sort__item_t elt = APR_ARRAY_IDX(sorted_changes, j,
svn_sort__item_t);
apr_hash_set(mergeinfo_cat,
apr_pstrdup(result_pool, elt.key),
elt.klen,
svn_mergeinfo_dup(elt.value, result_pool));
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_mergeinfo_intersect2(svn_mergeinfo_t *mergeinfo,
svn_mergeinfo_t mergeinfo1,
svn_mergeinfo_t mergeinfo2,
svn_boolean_t consider_inheritance,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_hash_index_t *hi;
apr_pool_t *iterpool;
*mergeinfo = apr_hash_make(result_pool);
iterpool = svn_pool_create(scratch_pool);
/* ### TODO(reint): Do we care about the case when a path in one
### mergeinfo hash has inheritable mergeinfo, and in the other
### has non-inhertiable mergeinfo? It seems like that path
### itself should really be an intersection, while child paths
### should not be... */
for (hi = apr_hash_first(scratch_pool, mergeinfo1);
hi; hi = apr_hash_next(hi))
{
const char *path = svn__apr_hash_index_key(hi);
svn_rangelist_t *rangelist1 = svn__apr_hash_index_val(hi);
svn_rangelist_t *rangelist2;
svn_pool_clear(iterpool);
rangelist2 = svn_hash_gets(mergeinfo2, path);
if (rangelist2)
{
SVN_ERR(svn_rangelist_intersect(&rangelist2, rangelist1, rangelist2,
consider_inheritance, iterpool));
if (rangelist2->nelts > 0)
svn_hash_sets(*mergeinfo, apr_pstrdup(result_pool, path),
svn_rangelist_dup(rangelist2, result_pool));
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_mergeinfo_remove2(svn_mergeinfo_t *mergeinfo,
svn_mergeinfo_t eraser,
svn_mergeinfo_t whiteboard,
svn_boolean_t consider_inheritance,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
*mergeinfo = apr_hash_make(result_pool);
return walk_mergeinfo_hash_for_diff(whiteboard, eraser, *mergeinfo, NULL,
consider_inheritance, result_pool,
scratch_pool);
}
svn_error_t *
svn_rangelist_to_string(svn_string_t **output,
const svn_rangelist_t *rangelist,
apr_pool_t *pool)
{
svn_stringbuf_t *buf = svn_stringbuf_create_empty(pool);
if (rangelist->nelts > 0)
{
int i;
svn_merge_range_t *range;
/* Handle the elements that need commas at the end. */
for (i = 0; i < rangelist->nelts - 1; i++)
{
range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
svn_stringbuf_appendcstr(buf, range_to_string(range, pool));
svn_stringbuf_appendcstr(buf, ",");
}
/* Now handle the last element, which needs no comma. */
range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
svn_stringbuf_appendcstr(buf, range_to_string(range, pool));
}
*output = svn_stringbuf__morph_into_string(buf);
return SVN_NO_ERROR;
}
/* Converts a mergeinfo INPUT to an unparsed mergeinfo in OUTPUT. If PREFIX
is not NULL then prepend PREFIX to each line in OUTPUT. If INPUT contains
no elements, return the empty string. If INPUT contains any merge source
path keys that are relative then convert these to absolute paths in
*OUTPUT.
*/
static svn_error_t *
mergeinfo_to_stringbuf(svn_stringbuf_t **output,
svn_mergeinfo_t input,
const char *prefix,
apr_pool_t *pool)
{
*output = svn_stringbuf_create_empty(pool);
if (apr_hash_count(input) > 0)
{
apr_array_header_t *sorted =
svn_sort__hash(input, svn_sort_compare_items_as_paths, pool);
int i;
for (i = 0; i < sorted->nelts; i++)
{
svn_sort__item_t elt = APR_ARRAY_IDX(sorted, i, svn_sort__item_t);
svn_string_t *revlist;
SVN_ERR(svn_rangelist_to_string(&revlist, elt.value, pool));
svn_stringbuf_appendcstr(
*output,
apr_psprintf(pool, "%s%s%s:%s",
prefix ? prefix : "",
*((const char *) elt.key) == '/' ? "" : "/",
(const char *) elt.key,
revlist->data));
if (i < sorted->nelts - 1)
svn_stringbuf_appendcstr(*output, "\n");
}
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_mergeinfo_to_string(svn_string_t **output, svn_mergeinfo_t input,
apr_pool_t *pool)
{
svn_stringbuf_t *mergeinfo_buf;
SVN_ERR(mergeinfo_to_stringbuf(&mergeinfo_buf, input, NULL, pool));
*output = svn_stringbuf__morph_into_string(mergeinfo_buf);
return SVN_NO_ERROR;
}
svn_error_t *
svn_mergeinfo_sort(svn_mergeinfo_t input, apr_pool_t *pool)
{
apr_hash_index_t *hi;
for (hi = apr_hash_first(pool, input); hi; hi = apr_hash_next(hi))
{
apr_array_header_t *rl = svn__apr_hash_index_val(hi);
qsort(rl->elts, rl->nelts, rl->elt_size, svn_sort_compare_ranges);
}
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_mergeinfo__canonicalize_ranges(svn_mergeinfo_t mergeinfo,
+ apr_pool_t *scratch_pool)
+{
+ apr_hash_index_t *hi;
+
+ for (hi = apr_hash_first(scratch_pool, mergeinfo); hi; hi = apr_hash_next(hi))
+ {
+ apr_array_header_t *rl = svn__apr_hash_index_val(hi);
+
+ SVN_ERR(svn_rangelist__canonicalize(rl, scratch_pool));
+ }
+
return SVN_NO_ERROR;
}
svn_mergeinfo_catalog_t
svn_mergeinfo_catalog_dup(svn_mergeinfo_catalog_t mergeinfo_catalog,
apr_pool_t *pool)
{
svn_mergeinfo_t new_mergeinfo_catalog = apr_hash_make(pool);
apr_hash_index_t *hi;
for (hi = apr_hash_first(pool, mergeinfo_catalog);
hi;
hi = apr_hash_next(hi))
{
const char *key = svn__apr_hash_index_key(hi);
svn_mergeinfo_t val = svn__apr_hash_index_val(hi);
svn_hash_sets(new_mergeinfo_catalog, apr_pstrdup(pool, key),
svn_mergeinfo_dup(val, pool));
}
return new_mergeinfo_catalog;
}
svn_mergeinfo_t
svn_mergeinfo_dup(svn_mergeinfo_t mergeinfo, apr_pool_t *pool)
{
svn_mergeinfo_t new_mergeinfo = svn_hash__make(pool);
apr_hash_index_t *hi;
for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
{
const char *path = svn__apr_hash_index_key(hi);
apr_ssize_t pathlen = svn__apr_hash_index_klen(hi);
svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
apr_hash_set(new_mergeinfo, apr_pstrmemdup(pool, path, pathlen), pathlen,
svn_rangelist_dup(rangelist, pool));
}
return new_mergeinfo;
}
svn_error_t *
svn_mergeinfo_inheritable2(svn_mergeinfo_t *output,
svn_mergeinfo_t mergeinfo,
const char *path,
svn_revnum_t start,
svn_revnum_t end,
svn_boolean_t inheritable,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_hash_index_t *hi;
svn_mergeinfo_t inheritable_mergeinfo = apr_hash_make(result_pool);
for (hi = apr_hash_first(scratch_pool, mergeinfo);
hi;
hi = apr_hash_next(hi))
{
const char *key = svn__apr_hash_index_key(hi);
apr_ssize_t keylen = svn__apr_hash_index_klen(hi);
svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
svn_rangelist_t *inheritable_rangelist;
if (!path || svn_path_compare_paths(path, key) == 0)
SVN_ERR(svn_rangelist_inheritable2(&inheritable_rangelist, rangelist,
start, end, inheritable,
result_pool, scratch_pool));
else
inheritable_rangelist = svn_rangelist_dup(rangelist, result_pool);
/* Only add this rangelist if some ranges remain. A rangelist with
a path mapped to an empty rangelist is not syntactically valid */
if (inheritable_rangelist->nelts)
apr_hash_set(inheritable_mergeinfo,
apr_pstrmemdup(result_pool, key, keylen), keylen,
inheritable_rangelist);
}
*output = inheritable_mergeinfo;
return SVN_NO_ERROR;
}
svn_error_t *
svn_rangelist_inheritable2(svn_rangelist_t **inheritable_rangelist,
const svn_rangelist_t *rangelist,
svn_revnum_t start,
svn_revnum_t end,
svn_boolean_t inheritable,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
*inheritable_rangelist = apr_array_make(result_pool, 1,
sizeof(svn_merge_range_t *));
if (rangelist->nelts)
{
if (!SVN_IS_VALID_REVNUM(start)
|| !SVN_IS_VALID_REVNUM(end)
|| end < start)
{
int i;
/* We want all non-inheritable ranges removed. */
for (i = 0; i < rangelist->nelts; i++)
{
svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i,
svn_merge_range_t *);
if (range->inheritable == inheritable)
{
svn_merge_range_t *inheritable_range =
apr_palloc(result_pool, sizeof(*inheritable_range));
inheritable_range->start = range->start;
inheritable_range->end = range->end;
inheritable_range->inheritable = TRUE;
APR_ARRAY_PUSH(*inheritable_rangelist,
svn_merge_range_t *) = range;
}
}
}
else
{
/* We want only the non-inheritable ranges bound by START
and END removed. */
svn_rangelist_t *ranges_inheritable =
svn_rangelist__initialize(start, end, inheritable, scratch_pool);
if (rangelist->nelts)
SVN_ERR(svn_rangelist_remove(inheritable_rangelist,
ranges_inheritable,
rangelist,
TRUE,
result_pool));
}
}
return SVN_NO_ERROR;
}
svn_boolean_t
svn_mergeinfo__remove_empty_rangelists(svn_mergeinfo_t mergeinfo,
apr_pool_t *pool)
{
apr_hash_index_t *hi;
svn_boolean_t removed_some_ranges = FALSE;
if (mergeinfo)
{
for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
{
const char *path = svn__apr_hash_index_key(hi);
svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
if (rangelist->nelts == 0)
{
svn_hash_sets(mergeinfo, path, NULL);
removed_some_ranges = TRUE;
}
}
}
return removed_some_ranges;
}
svn_error_t *
svn_mergeinfo__remove_prefix_from_catalog(svn_mergeinfo_catalog_t *out_catalog,
svn_mergeinfo_catalog_t in_catalog,
const char *prefix_path,
apr_pool_t *pool)
{
apr_hash_index_t *hi;
SVN_ERR_ASSERT(prefix_path[0] == '/');
*out_catalog = apr_hash_make(pool);
for (hi = apr_hash_first(pool, in_catalog); hi; hi = apr_hash_next(hi))
{
const char *original_path = svn__apr_hash_index_key(hi);
svn_mergeinfo_t value = svn__apr_hash_index_val(hi);
const char *new_path;
new_path = svn_fspath__skip_ancestor(prefix_path, original_path);
SVN_ERR_ASSERT(new_path);
svn_hash_sets(*out_catalog, new_path, value);
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_mergeinfo__add_prefix_to_catalog(svn_mergeinfo_catalog_t *out_catalog,
svn_mergeinfo_catalog_t in_catalog,
const char *prefix_path,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_hash_index_t *hi;
*out_catalog = apr_hash_make(result_pool);
for (hi = apr_hash_first(scratch_pool, in_catalog);
hi;
hi = apr_hash_next(hi))
{
const char *original_path = svn__apr_hash_index_key(hi);
svn_mergeinfo_t value = svn__apr_hash_index_val(hi);
if (original_path[0] == '/')
original_path++;
svn_hash_sets(*out_catalog,
svn_dirent_join(prefix_path, original_path, result_pool),
value);
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_mergeinfo__add_suffix_to_mergeinfo(svn_mergeinfo_t *out_mergeinfo,
svn_mergeinfo_t mergeinfo,
const char *suffix_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_hash_index_t *hi;
SVN_ERR_ASSERT(suffix_relpath && svn_relpath_is_canonical(suffix_relpath));
*out_mergeinfo = apr_hash_make(result_pool);
for (hi = apr_hash_first(scratch_pool, mergeinfo);
hi;
hi = apr_hash_next(hi))
{
const char *fspath = svn__apr_hash_index_key(hi);
svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
svn_hash_sets(*out_mergeinfo,
svn_fspath__join(fspath, suffix_relpath, result_pool),
rangelist);
}
return SVN_NO_ERROR;
}
svn_rangelist_t *
svn_rangelist_dup(const svn_rangelist_t *rangelist, apr_pool_t *pool)
{
svn_rangelist_t *new_rl = apr_array_make(pool, rangelist->nelts,
sizeof(svn_merge_range_t *));
/* allocate target range buffer with a single operation */
svn_merge_range_t *copy = apr_palloc(pool, sizeof(*copy) * rangelist->nelts);
int i;
/* fill it iteratively and link it into the range list */
for (i = 0; i < rangelist->nelts; i++)
{
memcpy(copy + i,
APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *),
sizeof(*copy));
APR_ARRAY_PUSH(new_rl, svn_merge_range_t *) = copy + i;
}
return new_rl;
}
svn_merge_range_t *
svn_merge_range_dup(const svn_merge_range_t *range, apr_pool_t *pool)
{
svn_merge_range_t *new_range = apr_palloc(pool, sizeof(*new_range));
memcpy(new_range, range, sizeof(*new_range));
return new_range;
}
svn_boolean_t
svn_merge_range_contains_rev(const svn_merge_range_t *range, svn_revnum_t rev)
{
assert(SVN_IS_VALID_REVNUM(range->start));
assert(SVN_IS_VALID_REVNUM(range->end));
assert(range->start != range->end);
if (range->start < range->end)
return rev > range->start && rev <= range->end;
else
return rev > range->end && rev <= range->start;
}
svn_error_t *
svn_mergeinfo__catalog_to_formatted_string(svn_string_t **output,
svn_mergeinfo_catalog_t catalog,
const char *key_prefix,
const char *val_prefix,
apr_pool_t *pool)
{
svn_stringbuf_t *output_buf = NULL;
if (catalog && apr_hash_count(catalog))
{
int i;
apr_array_header_t *sorted_catalog =
svn_sort__hash(catalog, svn_sort_compare_items_as_paths, pool);
output_buf = svn_stringbuf_create_empty(pool);
for (i = 0; i < sorted_catalog->nelts; i++)
{
svn_sort__item_t elt =
APR_ARRAY_IDX(sorted_catalog, i, svn_sort__item_t);
const char *path1;
svn_mergeinfo_t mergeinfo;
svn_stringbuf_t *mergeinfo_output_buf;
path1 = elt.key;
mergeinfo = elt.value;
if (key_prefix)
svn_stringbuf_appendcstr(output_buf, key_prefix);
svn_stringbuf_appendcstr(output_buf, path1);
svn_stringbuf_appendcstr(output_buf, "\n");
SVN_ERR(mergeinfo_to_stringbuf(&mergeinfo_output_buf, mergeinfo,
val_prefix ? val_prefix : "", pool));
svn_stringbuf_appendstr(output_buf, mergeinfo_output_buf);
svn_stringbuf_appendcstr(output_buf, "\n");
}
}
#if SVN_DEBUG
else if (!catalog)
{
output_buf = svn_stringbuf_create(key_prefix ? key_prefix : "", pool);
svn_stringbuf_appendcstr(output_buf, _("NULL mergeinfo catalog\n"));
}
else if (apr_hash_count(catalog) == 0)
{
output_buf = svn_stringbuf_create(key_prefix ? key_prefix : "", pool);
svn_stringbuf_appendcstr(output_buf, _("empty mergeinfo catalog\n"));
}
#endif
/* If we have an output_buf, convert it to an svn_string_t;
otherwise, return a new string containing only a newline
character. */
if (output_buf)
*output = svn_stringbuf__morph_into_string(output_buf);
else
*output = svn_string_create("\n", pool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_mergeinfo__get_range_endpoints(svn_revnum_t *youngest_rev,
svn_revnum_t *oldest_rev,
svn_mergeinfo_t mergeinfo,
apr_pool_t *pool)
{
*youngest_rev = *oldest_rev = SVN_INVALID_REVNUM;
if (mergeinfo)
{
apr_hash_index_t *hi;
for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
{
svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
if (rangelist->nelts)
{
svn_merge_range_t *range = APR_ARRAY_IDX(rangelist,
rangelist->nelts - 1,
svn_merge_range_t *);
if (!SVN_IS_VALID_REVNUM(*youngest_rev)
|| (range->end > *youngest_rev))
*youngest_rev = range->end;
range = APR_ARRAY_IDX(rangelist, 0, svn_merge_range_t *);
if (!SVN_IS_VALID_REVNUM(*oldest_rev)
|| (range->start < *oldest_rev))
*oldest_rev = range->start;
}
}
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_mergeinfo__filter_catalog_by_ranges(svn_mergeinfo_catalog_t *filtered_cat,
svn_mergeinfo_catalog_t catalog,
svn_revnum_t youngest_rev,
svn_revnum_t oldest_rev,
svn_boolean_t include_range,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_hash_index_t *hi;
*filtered_cat = apr_hash_make(result_pool);
for (hi = apr_hash_first(scratch_pool, catalog);
hi;
hi = apr_hash_next(hi))
{
const char *path = svn__apr_hash_index_key(hi);
svn_mergeinfo_t mergeinfo = svn__apr_hash_index_val(hi);
svn_mergeinfo_t filtered_mergeinfo;
SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(&filtered_mergeinfo,
mergeinfo,
youngest_rev,
oldest_rev,
include_range,
result_pool,
scratch_pool));
if (apr_hash_count(filtered_mergeinfo))
svn_hash_sets(*filtered_cat,
apr_pstrdup(result_pool, path), filtered_mergeinfo);
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_mergeinfo__filter_mergeinfo_by_ranges(svn_mergeinfo_t *filtered_mergeinfo,
svn_mergeinfo_t mergeinfo,
svn_revnum_t youngest_rev,
svn_revnum_t oldest_rev,
svn_boolean_t include_range,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(oldest_rev));
SVN_ERR_ASSERT(oldest_rev < youngest_rev);
*filtered_mergeinfo = apr_hash_make(result_pool);
if (mergeinfo)
{
apr_hash_index_t *hi;
svn_rangelist_t *filter_rangelist =
svn_rangelist__initialize(oldest_rev, youngest_rev, TRUE,
scratch_pool);
for (hi = apr_hash_first(scratch_pool, mergeinfo);
hi;
hi = apr_hash_next(hi))
{
const char *path = svn__apr_hash_index_key(hi);
svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
if (rangelist->nelts)
{
svn_rangelist_t *new_rangelist;
SVN_ERR(rangelist_intersect_or_remove(
&new_rangelist, filter_rangelist, rangelist,
! include_range, FALSE, result_pool));
if (new_rangelist->nelts)
svn_hash_sets(*filtered_mergeinfo,
apr_pstrdup(result_pool, path), new_rangelist);
}
}
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_mergeinfo__adjust_mergeinfo_rangelists(svn_mergeinfo_t *adjusted_mergeinfo,
svn_mergeinfo_t mergeinfo,
svn_revnum_t offset,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_hash_index_t *hi;
*adjusted_mergeinfo = apr_hash_make(result_pool);
if (mergeinfo)
{
for (hi = apr_hash_first(scratch_pool, mergeinfo);
hi;
hi = apr_hash_next(hi))
{
int i;
const char *path = svn__apr_hash_index_key(hi);
svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
svn_rangelist_t *adjusted_rangelist =
apr_array_make(result_pool, rangelist->nelts,
sizeof(svn_merge_range_t *));
for (i = 0; i < rangelist->nelts; i++)
{
svn_merge_range_t *range =
APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
if (range->start + offset > 0 && range->end + offset > 0)
{
if (range->start + offset < 0)
range->start = 0;
else
range->start = range->start + offset;
if (range->end + offset < 0)
range->end = 0;
else
range->end = range->end + offset;
APR_ARRAY_PUSH(adjusted_rangelist, svn_merge_range_t *) =
range;
}
}
if (adjusted_rangelist->nelts)
svn_hash_sets(*adjusted_mergeinfo, apr_pstrdup(result_pool, path),
adjusted_rangelist);
}
}
return SVN_NO_ERROR;
}
svn_boolean_t
svn_mergeinfo__is_noninheritable(svn_mergeinfo_t mergeinfo,
apr_pool_t *scratch_pool)
{
if (mergeinfo)
{
apr_hash_index_t *hi;
for (hi = apr_hash_first(scratch_pool, mergeinfo);
hi;
hi = apr_hash_next(hi))
{
svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
int i;
for (i = 0; i < rangelist->nelts; i++)
{
svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i,
svn_merge_range_t *);
if (!range->inheritable)
return TRUE;
}
}
}
return FALSE;
}
svn_rangelist_t *
svn_rangelist__initialize(svn_revnum_t start,
svn_revnum_t end,
svn_boolean_t inheritable,
apr_pool_t *result_pool)
{
svn_rangelist_t *rangelist =
apr_array_make(result_pool, 1, sizeof(svn_merge_range_t *));
svn_merge_range_t *range = apr_pcalloc(result_pool, sizeof(*range));
range->start = start;
range->end = end;
range->inheritable = inheritable;
APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = range;
return rangelist;
}
svn_error_t *
svn_mergeinfo__mergeinfo_from_segments(svn_mergeinfo_t *mergeinfo_p,
const apr_array_header_t *segments,
apr_pool_t *pool)
{
svn_mergeinfo_t mergeinfo = apr_hash_make(pool);
int i;
/* Translate location segments into merge sources and ranges. */
for (i = 0; i < segments->nelts; i++)
{
svn_location_segment_t *segment =
APR_ARRAY_IDX(segments, i, svn_location_segment_t *);
svn_rangelist_t *path_ranges;
svn_merge_range_t *range;
const char *source_path;
/* No path segment? Skip it. */
if (! segment->path)
continue;
/* Prepend a leading slash to our path. */
source_path = apr_pstrcat(pool, "/", segment->path, (char *)NULL);
/* See if we already stored ranges for this path. If not, make
a new list. */
path_ranges = svn_hash_gets(mergeinfo, source_path);
if (! path_ranges)
path_ranges = apr_array_make(pool, 1, sizeof(range));
/* A svn_location_segment_t may have legitimately describe only
revision 0, but there is no corresponding representation for
this in a svn_merge_range_t. */
if (segment->range_start == 0 && segment->range_end == 0)
continue;
/* Build a merge range, push it onto the list of ranges, and for
good measure, (re)store it in the hash. */
range = apr_pcalloc(pool, sizeof(*range));
range->start = MAX(segment->range_start - 1, 0);
range->end = segment->range_end;
range->inheritable = TRUE;
APR_ARRAY_PUSH(path_ranges, svn_merge_range_t *) = range;
svn_hash_sets(mergeinfo, source_path, path_ranges);
}
*mergeinfo_p = mergeinfo;
return SVN_NO_ERROR;
}
svn_error_t *
svn_rangelist__merge_many(svn_rangelist_t *merged_rangelist,
svn_mergeinfo_t merge_history,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
if (apr_hash_count(merge_history))
{
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
apr_hash_index_t *hi;
for (hi = apr_hash_first(scratch_pool, merge_history);
hi;
hi = apr_hash_next(hi))
{
svn_rangelist_t *subtree_rangelist = svn__apr_hash_index_val(hi);
svn_pool_clear(iterpool);
SVN_ERR(svn_rangelist_merge2(merged_rangelist, subtree_rangelist,
result_pool, iterpool));
}
svn_pool_destroy(iterpool);
}
return SVN_NO_ERROR;
}
const char *
svn_inheritance_to_word(svn_mergeinfo_inheritance_t inherit)
{
switch (inherit)
{
case svn_mergeinfo_inherited:
return "inherited";
case svn_mergeinfo_nearest_ancestor:
return "nearest-ancestor";
default:
return "explicit";
}
}
svn_mergeinfo_inheritance_t
svn_inheritance_from_word(const char *word)
{
if (strcmp(word, "inherited") == 0)
return svn_mergeinfo_inherited;
if (strcmp(word, "nearest-ancestor") == 0)
return svn_mergeinfo_nearest_ancestor;
return svn_mergeinfo_explicit;
}
Index: vendor/subversion/dist/subversion/libsvn_subr/sqlite3wrapper.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_subr/sqlite3wrapper.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_subr/sqlite3wrapper.c (revision 286501)
@@ -1,63 +1,63 @@
/* sqlite3wrapper.c
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include "svn_private_config.h"
/* Include sqlite3 inline, making all symbols private. */
#ifdef SVN_SQLITE_INLINE
-# define SQLITE_OMIT_DEPRECATED
+# define SQLITE_OMIT_DEPRECATED 1
# define SQLITE_API static
# if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 6 || __APPLE_CC__))
# if !__APPLE_CC__ || __GNUC_MINOR__ >= 6
# pragma GCC diagnostic push
# endif
# pragma GCC diagnostic ignored "-Wunreachable-code"
# pragma GCC diagnostic ignored "-Wunused-function"
# pragma GCC diagnostic ignored "-Wcast-qual"
# pragma GCC diagnostic ignored "-Wunused"
# pragma GCC diagnostic ignored "-Wshadow"
# if __APPLE_CC__
# pragma GCC diagnostic ignored "-Wshorten-64-to-32"
# endif
# endif
# ifdef __APPLE__
# include <Availability.h>
# if __MAC_OS_X_VERSION_MIN_REQUIRED < 1060
/* <libkern/OSAtomic.h> is included on OS X by sqlite3.c, and
on old systems (Leopard or older), it cannot be compiled
with -std=c89 because it uses inline. This is a work-around. */
# define inline __inline__
# include <libkern/OSAtomic.h>
# undef inline
# endif
# endif
# define SQLITE_DEFAULT_FILE_PERMISSIONS 0666
# include <sqlite3.c>
# if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 6))
# pragma GCC diagnostic pop
# endif
/* Expose the sqlite API vtable and the two missing functions */
const sqlite3_api_routines *const svn_sqlite3__api_funcs = &sqlite3Apis;
int (*const svn_sqlite3__api_initialize)(void) = sqlite3_initialize;
int (*const svn_sqlite3__api_config)(int, ...) = sqlite3_config;
#endif
Index: vendor/subversion/dist/subversion/libsvn_subr/string.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_subr/string.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_subr/string.c (revision 286501)
@@ -1,1273 +1,1273 @@
/*
* string.c: routines to manipulate counted-length strings
* (svn_stringbuf_t and svn_string_t) and C strings.
*
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include <apr.h>
#include <string.h> /* for memcpy(), memcmp(), strlen() */
#include <apr_fnmatch.h>
#include "svn_string.h" /* loads "svn_types.h" and <apr_pools.h> */
#include "svn_ctype.h"
#include "private/svn_dep_compat.h"
#include "private/svn_string_private.h"
#include "svn_private_config.h"
/* Allocate the space for a memory buffer from POOL.
* Return a pointer to the new buffer in *DATA and its size in *SIZE.
* The buffer size will be at least MINIMUM_SIZE.
*
* N.B.: The stringbuf creation functions use this, but since stringbufs
* always consume at least 1 byte for the NUL terminator, the
* resulting data pointers will never be NULL.
*/
static APR_INLINE void
membuf_create(void **data, apr_size_t *size,
apr_size_t minimum_size, apr_pool_t *pool)
{
/* apr_palloc will allocate multiples of 8.
* Thus, we would waste some of that memory if we stuck to the
* smaller size. Note that this is safe even if apr_palloc would
* use some other aligment or none at all. */
minimum_size = APR_ALIGN_DEFAULT(minimum_size);
*data = (!minimum_size ? NULL : apr_palloc(pool, minimum_size));
*size = minimum_size;
}
/* Ensure that the size of a given memory buffer is at least MINIMUM_SIZE
* bytes. If *SIZE is already greater than or equal to MINIMUM_SIZE,
* this function does nothing.
*
* If *SIZE is 0, the allocated buffer size will be MINIMUM_SIZE
* rounded up to the nearest APR alignment boundary. Otherwse, *SIZE
* will be multiplied by a power of two such that the result is
* greater or equal to MINIMUM_SIZE. The pointer to the new buffer
* will be returned in *DATA, and its size in *SIZE.
*/
static APR_INLINE void
membuf_ensure(void **data, apr_size_t *size,
apr_size_t minimum_size, apr_pool_t *pool)
{
if (minimum_size > *size)
{
apr_size_t new_size = *size;
if (new_size == 0)
/* APR will increase odd allocation sizes to the next
* multiple for 8, for instance. Take advantage of that
* knowledge and allow for the extra size to be used. */
new_size = minimum_size;
else
while (new_size < minimum_size)
{
/* new_size is aligned; doubling it should keep it aligned */
const apr_size_t prev_size = new_size;
new_size *= 2;
/* check for apr_size_t overflow */
if (prev_size > new_size)
{
new_size = minimum_size;
break;
}
}
membuf_create(data, size, new_size, pool);
}
}
void
svn_membuf__create(svn_membuf_t *membuf, apr_size_t size, apr_pool_t *pool)
{
membuf_create(&membuf->data, &membuf->size, size, pool);
membuf->pool = pool;
}
void
svn_membuf__ensure(svn_membuf_t *membuf, apr_size_t size)
{
membuf_ensure(&membuf->data, &membuf->size, size, membuf->pool);
}
void
svn_membuf__resize(svn_membuf_t *membuf, apr_size_t size)
{
const void *const old_data = membuf->data;
const apr_size_t old_size = membuf->size;
membuf_ensure(&membuf->data, &membuf->size, size, membuf->pool);
if (membuf->data && old_data && old_data != membuf->data)
memcpy(membuf->data, old_data, old_size);
}
/* Always provide an out-of-line implementation of svn_membuf__zero */
#undef svn_membuf__zero
void
svn_membuf__zero(svn_membuf_t *membuf)
{
SVN_MEMBUF__ZERO(membuf);
}
/* Always provide an out-of-line implementation of svn_membuf__nzero */
#undef svn_membuf__nzero
void
svn_membuf__nzero(svn_membuf_t *membuf, apr_size_t size)
{
SVN_MEMBUF__NZERO(membuf, size);
}
static APR_INLINE svn_boolean_t
string_compare(const char *str1,
const char *str2,
apr_size_t len1,
apr_size_t len2)
{
/* easy way out :) */
if (len1 != len2)
return FALSE;
/* now the strings must have identical lenghths */
if ((memcmp(str1, str2, len1)) == 0)
return TRUE;
else
return FALSE;
}
static APR_INLINE apr_size_t
string_first_non_whitespace(const char *str, apr_size_t len)
{
apr_size_t i;
for (i = 0; i < len; i++)
{
if (! svn_ctype_isspace(str[i]))
return i;
}
/* if we get here, then the string must be entirely whitespace */
return len;
}
static APR_INLINE apr_size_t
find_char_backward(const char *str, apr_size_t len, char ch)
{
apr_size_t i = len;
while (i != 0)
{
if (str[--i] == ch)
return i;
}
/* char was not found, return len */
return len;
}
/* svn_string functions */
/* Return a new svn_string_t object, allocated in POOL, initialized with
* DATA and SIZE. Do not copy the contents of DATA, just store the pointer.
* SIZE is the length in bytes of DATA, excluding the required NUL
* terminator. */
static svn_string_t *
create_string(const char *data, apr_size_t size,
apr_pool_t *pool)
{
svn_string_t *new_string;
new_string = apr_palloc(pool, sizeof(*new_string));
new_string->data = data;
new_string->len = size;
return new_string;
}
/* A data buffer for a zero-length string (just a null terminator). Many
* svn_string_t instances may share this same buffer. */
static const char empty_buffer[1] = {0};
svn_string_t *
svn_string_create_empty(apr_pool_t *pool)
{
svn_string_t *new_string = apr_palloc(pool, sizeof(*new_string));
new_string->data = empty_buffer;
new_string->len = 0;
return new_string;
}
svn_string_t *
svn_string_ncreate(const char *bytes, apr_size_t size, apr_pool_t *pool)
{
void *mem;
char *data;
svn_string_t *new_string;
/* Allocate memory for svn_string_t and data in one chunk. */
mem = apr_palloc(pool, sizeof(*new_string) + size + 1);
data = (char*)mem + sizeof(*new_string);
new_string = mem;
new_string->data = data;
new_string->len = size;
memcpy(data, bytes, size);
/* Null termination is the convention -- even if we suspect the data
to be binary, it's not up to us to decide, it's the caller's
call. Heck, that's why they call it the caller! */
data[size] = '\0';
return new_string;
}
svn_string_t *
svn_string_create(const char *cstring, apr_pool_t *pool)
{
return svn_string_ncreate(cstring, strlen(cstring), pool);
}
svn_string_t *
svn_string_create_from_buf(const svn_stringbuf_t *strbuf, apr_pool_t *pool)
{
return svn_string_ncreate(strbuf->data, strbuf->len, pool);
}
svn_string_t *
svn_string_createv(apr_pool_t *pool, const char *fmt, va_list ap)
{
char *data = apr_pvsprintf(pool, fmt, ap);
/* wrap an svn_string_t around the new data */
return create_string(data, strlen(data), pool);
}
svn_string_t *
svn_string_createf(apr_pool_t *pool, const char *fmt, ...)
{
svn_string_t *str;
va_list ap;
va_start(ap, fmt);
str = svn_string_createv(pool, fmt, ap);
va_end(ap);
return str;
}
svn_boolean_t
svn_string_isempty(const svn_string_t *str)
{
return (str->len == 0);
}
svn_string_t *
svn_string_dup(const svn_string_t *original_string, apr_pool_t *pool)
{
return (svn_string_ncreate(original_string->data,
original_string->len, pool));
}
svn_boolean_t
svn_string_compare(const svn_string_t *str1, const svn_string_t *str2)
{
return
string_compare(str1->data, str2->data, str1->len, str2->len);
}
apr_size_t
svn_string_first_non_whitespace(const svn_string_t *str)
{
return
string_first_non_whitespace(str->data, str->len);
}
apr_size_t
svn_string_find_char_backward(const svn_string_t *str, char ch)
{
return find_char_backward(str->data, str->len, ch);
}
svn_string_t *
svn_stringbuf__morph_into_string(svn_stringbuf_t *strbuf)
{
/* In debug mode, detect attempts to modify the original STRBUF object.
*/
#ifdef SVN_DEBUG
strbuf->pool = NULL;
strbuf->blocksize = strbuf->len + 1;
#endif
/* Both, svn_string_t and svn_stringbuf_t are public API structures
* since the svn epoch. Thus, we can rely on their precise layout not
* to change.
*
* It just so happens that svn_string_t is structurally equivalent
* to the (data, len) sub-set of svn_stringbuf_t. There is also no
* difference in alignment and padding. So, we can just re-interpret
* that part of STRBUF as a svn_string_t.
*
* However, since svn_string_t does not know about the blocksize
* member in svn_stringbuf_t, any attempt to re-size the returned
* svn_string_t might invalidate the STRBUF struct. Hence, we consider
* the source STRBUF "consumed".
*
* Modifying the string character content is fine, though.
*/
return (svn_string_t *)&strbuf->data;
}
/* svn_stringbuf functions */
svn_stringbuf_t *
svn_stringbuf_create_empty(apr_pool_t *pool)
{
return svn_stringbuf_create_ensure(0, pool);
}
svn_stringbuf_t *
svn_stringbuf_create_ensure(apr_size_t blocksize, apr_pool_t *pool)
{
void *mem;
svn_stringbuf_t *new_string;
++blocksize; /* + space for '\0' */
/* Allocate memory for svn_string_t and data in one chunk. */
membuf_create(&mem, &blocksize, blocksize + sizeof(*new_string), pool);
/* Initialize header and string */
new_string = mem;
new_string->data = (char*)mem + sizeof(*new_string);
new_string->data[0] = '\0';
new_string->len = 0;
new_string->blocksize = blocksize - sizeof(*new_string);
new_string->pool = pool;
return new_string;
}
svn_stringbuf_t *
svn_stringbuf_ncreate(const char *bytes, apr_size_t size, apr_pool_t *pool)
{
svn_stringbuf_t *strbuf = svn_stringbuf_create_ensure(size, pool);
memcpy(strbuf->data, bytes, size);
/* Null termination is the convention -- even if we suspect the data
to be binary, it's not up to us to decide, it's the caller's
call. Heck, that's why they call it the caller! */
strbuf->data[size] = '\0';
strbuf->len = size;
return strbuf;
}
svn_stringbuf_t *
svn_stringbuf_create(const char *cstring, apr_pool_t *pool)
{
return svn_stringbuf_ncreate(cstring, strlen(cstring), pool);
}
svn_stringbuf_t *
svn_stringbuf_create_from_string(const svn_string_t *str, apr_pool_t *pool)
{
return svn_stringbuf_ncreate(str->data, str->len, pool);
}
svn_stringbuf_t *
svn_stringbuf_createv(apr_pool_t *pool, const char *fmt, va_list ap)
{
char *data = apr_pvsprintf(pool, fmt, ap);
apr_size_t size = strlen(data);
svn_stringbuf_t *new_string;
new_string = apr_palloc(pool, sizeof(*new_string));
new_string->data = data;
new_string->len = size;
new_string->blocksize = size + 1;
new_string->pool = pool;
return new_string;
}
svn_stringbuf_t *
svn_stringbuf_createf(apr_pool_t *pool, const char *fmt, ...)
{
svn_stringbuf_t *str;
va_list ap;
va_start(ap, fmt);
str = svn_stringbuf_createv(pool, fmt, ap);
va_end(ap);
return str;
}
void
svn_stringbuf_fillchar(svn_stringbuf_t *str, unsigned char c)
{
memset(str->data, c, str->len);
}
void
svn_stringbuf_set(svn_stringbuf_t *str, const char *value)
{
apr_size_t amt = strlen(value);
svn_stringbuf_ensure(str, amt);
memcpy(str->data, value, amt + 1);
str->len = amt;
}
void
svn_stringbuf_setempty(svn_stringbuf_t *str)
{
if (str->len > 0)
str->data[0] = '\0';
str->len = 0;
}
void
svn_stringbuf_chop(svn_stringbuf_t *str, apr_size_t nbytes)
{
if (nbytes > str->len)
str->len = 0;
else
str->len -= nbytes;
str->data[str->len] = '\0';
}
svn_boolean_t
svn_stringbuf_isempty(const svn_stringbuf_t *str)
{
return (str->len == 0);
}
void
svn_stringbuf_ensure(svn_stringbuf_t *str, apr_size_t minimum_size)
{
void *mem = NULL;
++minimum_size; /* + space for '\0' */
membuf_ensure(&mem, &str->blocksize, minimum_size, str->pool);
if (mem && mem != str->data)
{
if (str->data)
memcpy(mem, str->data, str->len + 1);
str->data = mem;
}
}
/* WARNING - Optimized code ahead!
* This function has been hand-tuned for performance. Please read
* the comments below before modifying the code.
*/
void
svn_stringbuf_appendbyte(svn_stringbuf_t *str, char byte)
{
char *dest;
apr_size_t old_len = str->len;
/* In most cases, there will be pre-allocated memory left
* to just write the new byte at the end of the used section
* and terminate the string properly.
*/
if (str->blocksize > old_len + 1)
{
/* The following read does not depend this write, so we
* can issue the write first to minimize register pressure:
* The value of old_len+1 is no longer needed; on most processors,
* dest[old_len+1] will be calculated implicitly as part of
* the addressing scheme.
*/
str->len = old_len+1;
/* Since the compiler cannot be sure that *src->data and *src
* don't overlap, we read src->data *once* before writing
* to *src->data. Replacing dest with str->data would force
* the compiler to read it again after the first byte.
*/
dest = str->data;
/* If not already available in a register as per ABI, load
* "byte" into the register (e.g. the one freed from old_len+1),
* then write it to the string buffer and terminate it properly.
*
* Including the "byte" fetch, all operations so far could be
* issued at once and be scheduled at the CPU's descression.
* Most likely, no-one will soon depend on the data that will be
* written in this function. So, no stalls there, either.
*/
dest[old_len] = byte;
dest[old_len+1] = '\0';
}
else
{
/* we need to re-allocate the string buffer
* -> let the more generic implementation take care of that part
*/
/* Depending on the ABI, "byte" is a register value. If we were
* to take its address directly, the compiler might decide to
* put in on the stack *unconditionally*, even if that would
* only be necessary for this block.
*/
char b = byte;
svn_stringbuf_appendbytes(str, &b, 1);
}
}
void
svn_stringbuf_appendbytes(svn_stringbuf_t *str, const char *bytes,
apr_size_t count)
{
apr_size_t total_len;
void *start_address;
total_len = str->len + count; /* total size needed */
/* svn_stringbuf_ensure adds 1 for null terminator. */
svn_stringbuf_ensure(str, total_len);
/* get address 1 byte beyond end of original bytestring */
start_address = (str->data + str->len);
memcpy(start_address, bytes, count);
str->len = total_len;
str->data[str->len] = '\0'; /* We don't know if this is binary
data or not, but convention is
to null-terminate. */
}
void
svn_stringbuf_appendstr(svn_stringbuf_t *targetstr,
const svn_stringbuf_t *appendstr)
{
svn_stringbuf_appendbytes(targetstr, appendstr->data, appendstr->len);
}
void
svn_stringbuf_appendcstr(svn_stringbuf_t *targetstr, const char *cstr)
{
svn_stringbuf_appendbytes(targetstr, cstr, strlen(cstr));
}
void
svn_stringbuf_insert(svn_stringbuf_t *str,
apr_size_t pos,
const char *bytes,
apr_size_t count)
{
if (bytes + count > str->data && bytes < str->data + str->blocksize)
{
/* special case: BYTES overlaps with this string -> copy the source */
- const char *temp = apr_pstrndup(str->pool, bytes, count);
+ const char *temp = apr_pmemdup(str->pool, bytes, count);
svn_stringbuf_insert(str, pos, temp, count);
}
else
{
if (pos > str->len)
pos = str->len;
svn_stringbuf_ensure(str, str->len + count);
memmove(str->data + pos + count, str->data + pos, str->len - pos + 1);
memcpy(str->data + pos, bytes, count);
str->len += count;
}
}
void
svn_stringbuf_remove(svn_stringbuf_t *str,
apr_size_t pos,
apr_size_t count)
{
if (pos > str->len)
pos = str->len;
if (pos + count > str->len)
count = str->len - pos;
memmove(str->data + pos, str->data + pos + count, str->len - pos - count + 1);
str->len -= count;
}
void
svn_stringbuf_replace(svn_stringbuf_t *str,
apr_size_t pos,
apr_size_t old_count,
const char *bytes,
apr_size_t new_count)
{
if (bytes + new_count > str->data && bytes < str->data + str->blocksize)
{
/* special case: BYTES overlaps with this string -> copy the source */
- const char *temp = apr_pstrndup(str->pool, bytes, new_count);
+ const char *temp = apr_pmemdup(str->pool, bytes, new_count);
svn_stringbuf_replace(str, pos, old_count, temp, new_count);
}
else
{
if (pos > str->len)
pos = str->len;
if (pos + old_count > str->len)
old_count = str->len - pos;
if (old_count < new_count)
{
apr_size_t delta = new_count - old_count;
svn_stringbuf_ensure(str, str->len + delta);
}
if (old_count != new_count)
memmove(str->data + pos + new_count, str->data + pos + old_count,
str->len - pos - old_count + 1);
memcpy(str->data + pos, bytes, new_count);
str->len += new_count - old_count;
}
}
svn_stringbuf_t *
svn_stringbuf_dup(const svn_stringbuf_t *original_string, apr_pool_t *pool)
{
return (svn_stringbuf_ncreate(original_string->data,
original_string->len, pool));
}
svn_boolean_t
svn_stringbuf_compare(const svn_stringbuf_t *str1,
const svn_stringbuf_t *str2)
{
return string_compare(str1->data, str2->data, str1->len, str2->len);
}
apr_size_t
svn_stringbuf_first_non_whitespace(const svn_stringbuf_t *str)
{
return string_first_non_whitespace(str->data, str->len);
}
void
svn_stringbuf_strip_whitespace(svn_stringbuf_t *str)
{
/* Find first non-whitespace character */
apr_size_t offset = svn_stringbuf_first_non_whitespace(str);
/* Go ahead! Waste some RAM, we've got pools! :) */
str->data += offset;
str->len -= offset;
str->blocksize -= offset;
/* Now that we've trimmed the front, trim the end, wasting more RAM. */
while ((str->len > 0) && svn_ctype_isspace(str->data[str->len - 1]))
str->len--;
str->data[str->len] = '\0';
}
apr_size_t
svn_stringbuf_find_char_backward(const svn_stringbuf_t *str, char ch)
{
return find_char_backward(str->data, str->len, ch);
}
svn_boolean_t
svn_string_compare_stringbuf(const svn_string_t *str1,
const svn_stringbuf_t *str2)
{
return string_compare(str1->data, str2->data, str1->len, str2->len);
}
/*** C string stuff. ***/
void
svn_cstring_split_append(apr_array_header_t *array,
const char *input,
const char *sep_chars,
svn_boolean_t chop_whitespace,
apr_pool_t *pool)
{
char *pats;
char *p;
pats = apr_pstrdup(pool, input); /* strtok wants non-const data */
p = svn_cstring_tokenize(sep_chars, &pats);
while (p)
{
if (chop_whitespace)
{
while (svn_ctype_isspace(*p))
p++;
{
char *e = p + (strlen(p) - 1);
while ((e >= p) && (svn_ctype_isspace(*e)))
e--;
*(++e) = '\0';
}
}
if (p[0] != '\0')
APR_ARRAY_PUSH(array, const char *) = p;
p = svn_cstring_tokenize(sep_chars, &pats);
}
return;
}
apr_array_header_t *
svn_cstring_split(const char *input,
const char *sep_chars,
svn_boolean_t chop_whitespace,
apr_pool_t *pool)
{
apr_array_header_t *a = apr_array_make(pool, 5, sizeof(input));
svn_cstring_split_append(a, input, sep_chars, chop_whitespace, pool);
return a;
}
svn_boolean_t svn_cstring_match_glob_list(const char *str,
const apr_array_header_t *list)
{
int i;
for (i = 0; i < list->nelts; i++)
{
const char *this_pattern = APR_ARRAY_IDX(list, i, char *);
if (apr_fnmatch(this_pattern, str, 0) == APR_SUCCESS)
return TRUE;
}
return FALSE;
}
svn_boolean_t
svn_cstring_match_list(const char *str, const apr_array_header_t *list)
{
int i;
for (i = 0; i < list->nelts; i++)
{
const char *this_str = APR_ARRAY_IDX(list, i, char *);
if (strcmp(this_str, str) == 0)
return TRUE;
}
return FALSE;
}
char *
svn_cstring_tokenize(const char *sep, char **str)
{
char *token;
const char * next;
char csep;
/* check parameters */
if ((sep == NULL) || (str == NULL) || (*str == NULL))
return NULL;
/* let APR handle edge cases and multiple separators */
csep = *sep;
if (csep == '\0' || sep[1] != '\0')
return apr_strtok(NULL, sep, str);
/* skip characters in sep (will terminate at '\0') */
token = *str;
while (*token == csep)
++token;
if (!*token) /* no more tokens */
return NULL;
/* skip valid token characters to terminate token and
* prepare for the next call (will terminate at '\0)
*/
next = strchr(token, csep);
if (next == NULL)
{
*str = token + strlen(token);
}
else
{
*(char *)next = '\0';
*str = (char *)next + 1;
}
return token;
}
int svn_cstring_count_newlines(const char *msg)
{
int count = 0;
const char *p;
for (p = msg; *p; p++)
{
if (*p == '\n')
{
count++;
if (*(p + 1) == '\r')
p++;
}
else if (*p == '\r')
{
count++;
if (*(p + 1) == '\n')
p++;
}
}
return count;
}
char *
svn_cstring_join(const apr_array_header_t *strings,
const char *separator,
apr_pool_t *pool)
{
svn_stringbuf_t *new_str = svn_stringbuf_create_empty(pool);
size_t sep_len = strlen(separator);
int i;
for (i = 0; i < strings->nelts; i++)
{
const char *string = APR_ARRAY_IDX(strings, i, const char *);
svn_stringbuf_appendbytes(new_str, string, strlen(string));
svn_stringbuf_appendbytes(new_str, separator, sep_len);
}
return new_str->data;
}
int
svn_cstring_casecmp(const char *str1, const char *str2)
{
for (;;)
{
const int a = *str1++;
const int b = *str2++;
const int cmp = svn_ctype_casecmp(a, b);
if (cmp || !a || !b)
return cmp;
}
}
svn_error_t *
svn_cstring_strtoui64(apr_uint64_t *n, const char *str,
apr_uint64_t minval, apr_uint64_t maxval,
int base)
{
apr_int64_t val;
char *endptr;
/* We assume errno is thread-safe. */
errno = 0; /* APR-0.9 doesn't always set errno */
/* ### We're throwing away half the number range here.
* ### APR needs a apr_strtoui64() function. */
val = apr_strtoi64(str, &endptr, base);
if (errno == EINVAL || endptr == str || str[0] == '\0' || *endptr != '\0')
return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
_("Could not convert '%s' into a number"),
str);
if ((errno == ERANGE && (val == APR_INT64_MIN || val == APR_INT64_MAX)) ||
val < 0 || (apr_uint64_t)val < minval || (apr_uint64_t)val > maxval)
/* ### Mark this for translation when gettext doesn't choke on macros. */
return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
"Number '%s' is out of range "
"'[%" APR_UINT64_T_FMT ", %" APR_UINT64_T_FMT "]'",
str, minval, maxval);
*n = val;
return SVN_NO_ERROR;
}
svn_error_t *
svn_cstring_atoui64(apr_uint64_t *n, const char *str)
{
return svn_error_trace(svn_cstring_strtoui64(n, str, 0,
APR_UINT64_MAX, 10));
}
svn_error_t *
svn_cstring_atoui(unsigned int *n, const char *str)
{
apr_uint64_t val;
SVN_ERR(svn_cstring_strtoui64(&val, str, 0, APR_UINT32_MAX, 10));
*n = (unsigned int)val;
return SVN_NO_ERROR;
}
svn_error_t *
svn_cstring_strtoi64(apr_int64_t *n, const char *str,
apr_int64_t minval, apr_int64_t maxval,
int base)
{
apr_int64_t val;
char *endptr;
/* We assume errno is thread-safe. */
errno = 0; /* APR-0.9 doesn't always set errno */
val = apr_strtoi64(str, &endptr, base);
if (errno == EINVAL || endptr == str || str[0] == '\0' || *endptr != '\0')
return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
_("Could not convert '%s' into a number"),
str);
if ((errno == ERANGE && (val == APR_INT64_MIN || val == APR_INT64_MAX)) ||
val < minval || val > maxval)
/* ### Mark this for translation when gettext doesn't choke on macros. */
return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
"Number '%s' is out of range "
"'[%" APR_INT64_T_FMT ", %" APR_INT64_T_FMT "]'",
str, minval, maxval);
*n = val;
return SVN_NO_ERROR;
}
svn_error_t *
svn_cstring_atoi64(apr_int64_t *n, const char *str)
{
return svn_error_trace(svn_cstring_strtoi64(n, str, APR_INT64_MIN,
APR_INT64_MAX, 10));
}
svn_error_t *
svn_cstring_atoi(int *n, const char *str)
{
apr_int64_t val;
SVN_ERR(svn_cstring_strtoi64(&val, str, APR_INT32_MIN, APR_INT32_MAX, 10));
*n = (int)val;
return SVN_NO_ERROR;
}
apr_status_t
svn__strtoff(apr_off_t *offset, const char *buf, char **end, int base)
{
#if !APR_VERSION_AT_LEAST(1,0,0)
errno = 0;
*offset = strtol(buf, end, base);
return APR_FROM_OS_ERROR(errno);
#else
return apr_strtoff(offset, buf, end, base);
#endif
}
/* "Precalculated" itoa values for 2 places (including leading zeros).
* For maximum performance, make sure all table entries are word-aligned.
*/
static const char decimal_table[100][4]
= { "00", "01", "02", "03", "04", "05", "06", "07", "08", "09"
, "10", "11", "12", "13", "14", "15", "16", "17", "18", "19"
, "20", "21", "22", "23", "24", "25", "26", "27", "28", "29"
, "30", "31", "32", "33", "34", "35", "36", "37", "38", "39"
, "40", "41", "42", "43", "44", "45", "46", "47", "48", "49"
, "50", "51", "52", "53", "54", "55", "56", "57", "58", "59"
, "60", "61", "62", "63", "64", "65", "66", "67", "68", "69"
, "70", "71", "72", "73", "74", "75", "76", "77", "78", "79"
, "80", "81", "82", "83", "84", "85", "86", "87", "88", "89"
, "90", "91", "92", "93", "94", "95", "96", "97", "98", "99"};
/* Copy the two bytes at SOURCE[0] and SOURCE[1] to DEST[0] and DEST[1] */
#define COPY_TWO_BYTES(dest,source)\
memcpy((dest), (source), 2)
apr_size_t
svn__ui64toa(char * dest, apr_uint64_t number)
{
char buffer[SVN_INT64_BUFFER_SIZE];
apr_uint32_t reduced; /* used for 32 bit DIV */
char* target;
/* Small numbers are by far the most common case.
* Therefore, we use special code.
*/
if (number < 100)
{
if (number < 10)
{
dest[0] = (char)('0' + number);
dest[1] = 0;
return 1;
}
else
{
COPY_TWO_BYTES(dest, decimal_table[(apr_size_t)number]);
dest[2] = 0;
return 2;
}
}
/* Standard code. Write string in pairs of chars back-to-front */
buffer[SVN_INT64_BUFFER_SIZE - 1] = 0;
target = &buffer[SVN_INT64_BUFFER_SIZE - 3];
/* Loop may be executed 0 .. 2 times. */
while (number >= 100000000)
{
/* Number is larger than 100^4, i.e. we can write 4x2 chars.
* Also, use 32 bit DIVs as these are about twice as fast.
*/
reduced = (apr_uint32_t)(number % 100000000);
number /= 100000000;
COPY_TWO_BYTES(target - 0, decimal_table[reduced % 100]);
reduced /= 100;
COPY_TWO_BYTES(target - 2, decimal_table[reduced % 100]);
reduced /= 100;
COPY_TWO_BYTES(target - 4, decimal_table[reduced % 100]);
reduced /= 100;
COPY_TWO_BYTES(target - 6, decimal_table[reduced % 100]);
target -= 8;
}
/* Now, the number fits into 32 bits, but may still be larger than 99 */
reduced = (apr_uint32_t)(number);
while (reduced >= 100)
{
COPY_TWO_BYTES(target, decimal_table[reduced % 100]);
reduced /= 100;
target -= 2;
}
/* The number is now smaller than 100 but larger than 1 */
COPY_TWO_BYTES(target, decimal_table[reduced]);
/* Correction for uneven count of places. */
if (reduced < 10)
++target;
/* Copy to target */
memcpy(dest, target, &buffer[SVN_INT64_BUFFER_SIZE] - target);
return &buffer[SVN_INT64_BUFFER_SIZE] - target - 1;
}
apr_size_t
svn__i64toa(char * dest, apr_int64_t number)
{
if (number >= 0)
return svn__ui64toa(dest, (apr_uint64_t)number);
*dest = '-';
return svn__ui64toa(dest + 1, (apr_uint64_t)(0-number)) + 1;
}
static void
ui64toa_sep(apr_uint64_t number, char seperator, char *buffer)
{
apr_size_t length = svn__ui64toa(buffer, number);
apr_size_t i;
for (i = length; i > 3; i -= 3)
{
memmove(&buffer[i - 2], &buffer[i - 3], length - i + 3);
buffer[i-3] = seperator;
length++;
}
buffer[length] = 0;
}
char *
svn__ui64toa_sep(apr_uint64_t number, char seperator, apr_pool_t *pool)
{
char buffer[2 * SVN_INT64_BUFFER_SIZE];
ui64toa_sep(number, seperator, buffer);
return apr_pstrdup(pool, buffer);
}
char *
svn__i64toa_sep(apr_int64_t number, char seperator, apr_pool_t *pool)
{
char buffer[2 * SVN_INT64_BUFFER_SIZE];
if (number < 0)
{
buffer[0] = '-';
ui64toa_sep((apr_uint64_t)(-number), seperator, &buffer[1]);
}
else
ui64toa_sep((apr_uint64_t)(number), seperator, buffer);
return apr_pstrdup(pool, buffer);
}
unsigned int
svn_cstring__similarity(const char *stra, const char *strb,
svn_membuf_t *buffer, apr_size_t *rlcs)
{
svn_string_t stringa, stringb;
stringa.data = stra;
stringa.len = strlen(stra);
stringb.data = strb;
stringb.len = strlen(strb);
return svn_string__similarity(&stringa, &stringb, buffer, rlcs);
}
unsigned int
svn_string__similarity(const svn_string_t *stringa,
const svn_string_t *stringb,
svn_membuf_t *buffer, apr_size_t *rlcs)
{
const char *stra = stringa->data;
const char *strb = stringb->data;
const apr_size_t lena = stringa->len;
const apr_size_t lenb = stringb->len;
const apr_size_t total = lena + lenb;
const char *enda = stra + lena;
const char *endb = strb + lenb;
apr_size_t lcs = 0;
/* Skip the common prefix ... */
while (stra < enda && strb < endb && *stra == *strb)
{
++stra; ++strb;
++lcs;
}
/* ... and the common suffix */
while (stra < enda && strb < endb)
{
--enda; --endb;
if (*enda != *endb)
{
++enda; ++endb;
break;
}
++lcs;
}
if (stra < enda && strb < endb)
{
const apr_size_t resta = enda - stra;
const apr_size_t restb = endb - strb;
const apr_size_t slots = (resta > restb ? restb : resta);
apr_size_t *curr, *prev;
const char *pstr;
/* The outer loop must iterate on the longer string. */
if (resta < restb)
{
pstr = stra;
stra = strb;
strb = pstr;
pstr = enda;
enda = endb;
endb = pstr;
}
/* Allocate two columns in the LCS matrix
### Optimize this to (slots + 2) instesd of 2 * (slots + 1) */
svn_membuf__ensure(buffer, 2 * (slots + 1) * sizeof(apr_size_t));
svn_membuf__nzero(buffer, (slots + 2) * sizeof(apr_size_t));
prev = buffer->data;
curr = prev + slots + 1;
/* Calculate LCS length of the remainder */
for (pstr = stra; pstr < enda; ++pstr)
{
int i;
for (i = 1; i <= slots; ++i)
{
if (*pstr == strb[i-1])
curr[i] = prev[i-1] + 1;
else
curr[i] = (curr[i-1] > prev[i] ? curr[i-1] : prev[i]);
}
/* Swap the buffers, making the previous one current */
{
apr_size_t *const temp = prev;
prev = curr;
curr = temp;
}
}
lcs += prev[slots];
}
if (rlcs)
*rlcs = lcs;
/* Return similarity ratio rounded to 4 significant digits */
if (total)
return(unsigned int)((2000 * lcs + total/2) / total);
else
return 1000;
}
Index: vendor/subversion/dist/subversion/libsvn_subr/version.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_subr/version.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_subr/version.c (revision 286501)
@@ -1,298 +1,298 @@
/*
* version.c: library version number and utilities
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include "svn_error.h"
#include "svn_version.h"
#include "sysinfo.h"
#include "svn_private_config.h"
#include "private/svn_subr_private.h"
const svn_version_t *
svn_subr_version(void)
{
SVN_VERSION_BODY;
}
svn_boolean_t svn_ver_compatible(const svn_version_t *my_version,
const svn_version_t *lib_version)
{
/* With normal development builds the matching rules are strict, to
avoid inadvertantly using the wrong libraries. For backward
compatibility testing use --disable-full-version-match to
configure 1.7 and then the libraries that get built can be used
to replace those in 1.6 or earlier builds. */
#ifndef SVN_DISABLE_FULL_VERSION_MATCH
if (lib_version->tag[0] != '\0')
/* Development library; require exact match. */
return svn_ver_equal(my_version, lib_version);
else if (my_version->tag[0] != '\0')
/* Development client; must be newer than the library
and have the same major and minor version. */
return (my_version->major == lib_version->major
&& my_version->minor == lib_version->minor
&& my_version->patch > lib_version->patch);
#endif
/* General compatibility rules for released versions. */
return (my_version->major == lib_version->major
&& my_version->minor <= lib_version->minor);
}
svn_boolean_t svn_ver_equal(const svn_version_t *my_version,
const svn_version_t *lib_version)
{
return (my_version->major == lib_version->major
&& my_version->minor == lib_version->minor
&& my_version->patch == lib_version->patch
&& 0 == strcmp(my_version->tag, lib_version->tag));
}
svn_error_t *
svn_ver__check_list2(const svn_version_t *my_version,
const svn_version_checklist_t *checklist,
svn_boolean_t (*comparator)(const svn_version_t *,
const svn_version_t *))
{
svn_error_t *err = SVN_NO_ERROR;
int i;
for (i = 0; checklist[i].label != NULL; ++i)
{
const svn_version_t *lib_version = checklist[i].version_query();
if (!comparator(my_version, lib_version))
err = svn_error_createf(SVN_ERR_VERSION_MISMATCH, err,
_("Version mismatch in '%s'%s:"
" found %d.%d.%d%s,"
" expected %d.%d.%d%s"),
checklist[i].label,
comparator == svn_ver_equal
? _(" (expecting equality)")
: comparator == svn_ver_compatible
? _(" (expecting compatibility)")
: "",
lib_version->major, lib_version->minor,
lib_version->patch, lib_version->tag,
my_version->major, my_version->minor,
my_version->patch, my_version->tag);
}
return err;
}
struct svn_version_extended_t
{
const char *build_date; /* Compilation date */
const char *build_time; /* Compilation time */
const char *build_host; /* Build canonical host name */
const char *copyright; /* Copyright notice (localized) */
const char *runtime_host; /* Runtime canonical host name */
const char *runtime_osname; /* Running OS release name */
/* Array of svn_version_ext_linked_lib_t describing dependent
libraries. */
const apr_array_header_t *linked_libs;
/* Array of svn_version_ext_loaded_lib_t describing loaded shared
libraries. */
const apr_array_header_t *loaded_libs;
};
const svn_version_extended_t *
svn_version_extended(svn_boolean_t verbose,
apr_pool_t *pool)
{
svn_version_extended_t *info = apr_pcalloc(pool, sizeof(*info));
info->build_date = __DATE__;
info->build_time = __TIME__;
info->build_host = SVN_BUILD_HOST;
info->copyright = apr_pstrdup
- (pool, _("Copyright (C) 2014 The Apache Software Foundation.\n"
+ (pool, _("Copyright (C) 2015 The Apache Software Foundation.\n"
"This software consists of contributions made by many people;\n"
"see the NOTICE file for more information.\n"
"Subversion is open source software, see "
"http://subversion.apache.org/\n"));
if (verbose)
{
info->runtime_host = svn_sysinfo__canonical_host(pool);
info->runtime_osname = svn_sysinfo__release_name(pool);
info->linked_libs = svn_sysinfo__linked_libs(pool);
info->loaded_libs = svn_sysinfo__loaded_libs(pool);
}
return info;
}
const char *
svn_version_ext_build_date(const svn_version_extended_t *ext_info)
{
return ext_info->build_date;
}
const char *
svn_version_ext_build_time(const svn_version_extended_t *ext_info)
{
return ext_info->build_time;
}
const char *
svn_version_ext_build_host(const svn_version_extended_t *ext_info)
{
return ext_info->build_host;
}
const char *
svn_version_ext_copyright(const svn_version_extended_t *ext_info)
{
return ext_info->copyright;
}
const char *
svn_version_ext_runtime_host(const svn_version_extended_t *ext_info)
{
return ext_info->runtime_host;
}
const char *
svn_version_ext_runtime_osname(const svn_version_extended_t *ext_info)
{
return ext_info->runtime_osname;
}
const apr_array_header_t *
svn_version_ext_linked_libs(const svn_version_extended_t *ext_info)
{
return ext_info->linked_libs;
}
const apr_array_header_t *
svn_version_ext_loaded_libs(const svn_version_extended_t *ext_info)
{
return ext_info->loaded_libs;
}
svn_error_t *
svn_version__parse_version_string(svn_version_t **version_p,
const char *version_string,
apr_pool_t *result_pool)
{
svn_error_t *err;
svn_version_t *version;
apr_array_header_t *pieces =
svn_cstring_split(version_string, ".", FALSE, result_pool);
if ((pieces->nelts < 2) || (pieces->nelts > 3))
return svn_error_createf(SVN_ERR_MALFORMED_VERSION_STRING, NULL,
_("Failed to parse version number string '%s'"),
version_string);
version = apr_pcalloc(result_pool, sizeof(*version));
version->tag = "";
/* Parse the major and minor integers strictly. */
err = svn_cstring_atoi(&(version->major),
APR_ARRAY_IDX(pieces, 0, const char *));
if (err)
return svn_error_createf(SVN_ERR_MALFORMED_VERSION_STRING, err,
_("Failed to parse version number string '%s'"),
version_string);
err = svn_cstring_atoi(&(version->minor),
APR_ARRAY_IDX(pieces, 1, const char *));
if (err)
return svn_error_createf(SVN_ERR_MALFORMED_VERSION_STRING, err,
_("Failed to parse version number string '%s'"),
version_string);
/* If there's a third component, we'll parse it, too. But we don't
require that it be present. */
if (pieces->nelts == 3)
{
const char *piece = APR_ARRAY_IDX(pieces, 2, const char *);
char *hyphen = strchr(piece, '-');
if (hyphen)
{
version->tag = apr_pstrdup(result_pool, hyphen + 1);
*hyphen = '\0';
}
err = svn_cstring_atoi(&(version->patch), piece);
if (err)
return svn_error_createf(SVN_ERR_MALFORMED_VERSION_STRING, err,
_("Failed to parse version number string '%s'"
),
version_string);
}
if (version->major < 0 || version->minor < 0 || version->patch < 0)
return svn_error_createf(SVN_ERR_MALFORMED_VERSION_STRING, err,
_("Failed to parse version number string '%s'"),
version_string);
*version_p = version;
return SVN_NO_ERROR;
}
svn_boolean_t
svn_version__at_least(svn_version_t *version,
int major,
int minor,
int patch)
{
/* Compare major versions. */
if (version->major < major)
return FALSE;
if (version->major > major)
return TRUE;
/* Major versions are the same. Compare minor versions. */
if (version->minor < minor)
return FALSE;
if (version->minor > minor)
return TRUE;
/* Major and minor versions are the same. Compare patch
versions. */
if (version->patch < patch)
return FALSE;
if (version->patch > patch)
return TRUE;
/* Major, minor, and patch versions are identical matches. But tags
in our schema are always used for versions not yet quite at the
given patch level. */
if (version->tag && version->tag[0])
return FALSE;
return TRUE;
}
Index: vendor/subversion/dist/subversion/libsvn_wc/adm_ops.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_wc/adm_ops.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_wc/adm_ops.c (revision 286501)
@@ -1,1401 +1,1402 @@
/*
* adm_ops.c: routines for affecting working copy administrative
* information. NOTE: this code doesn't know where the adm
* info is actually stored. Instead, generic handles to
* adm data are requested via a reference to some PATH
* (PATH being a regular, non-administrative directory or
* file in the working copy).
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include <string.h>
#include <stdlib.h>
#include <apr_pools.h>
#include <apr_hash.h>
#include <apr_time.h>
#include <apr_errno.h>
#include "svn_types.h"
#include "svn_pools.h"
#include "svn_string.h"
#include "svn_error.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_hash.h"
#include "svn_wc.h"
#include "svn_io.h"
#include "svn_time.h"
#include "svn_sorts.h"
#include "wc.h"
#include "adm_files.h"
#include "conflicts.h"
#include "workqueue.h"
#include "svn_private_config.h"
#include "private/svn_subr_private.h"
#include "private/svn_dep_compat.h"
struct svn_wc_committed_queue_t
{
/* The pool in which ->queue is allocated. */
apr_pool_t *pool;
/* Mapping (const char *) local_abspath to (committed_queue_item_t *). */
apr_hash_t *queue;
/* Is any item in the queue marked as 'recursive'? */
svn_boolean_t have_recursive;
};
typedef struct committed_queue_item_t
{
const char *local_abspath;
svn_boolean_t recurse;
svn_boolean_t no_unlock;
svn_boolean_t keep_changelist;
/* The pristine text checksum. */
const svn_checksum_t *sha1_checksum;
apr_hash_t *new_dav_cache;
} committed_queue_item_t;
apr_pool_t *
svn_wc__get_committed_queue_pool(const struct svn_wc_committed_queue_t *queue)
{
return queue->pool;
}
/*** Finishing updates and commits. ***/
/* Queue work items that will finish a commit of the file or directory
* LOCAL_ABSPATH in DB:
* - queue the removal of any "revert-base" props and text files;
* - queue an update of the DB entry for this node
*
* ### The Pristine Store equivalent should be:
* - remember the old BASE_NODE and WORKING_NODE pristine text c'sums;
* - queue an update of the DB entry for this node (incl. updating the
* BASE_NODE c'sum and setting the WORKING_NODE c'sum to NULL);
* - queue deletion of the old pristine texts by the remembered checksums.
*
* CHECKSUM is the checksum of the new text base for LOCAL_ABSPATH, and must
* be provided if there is one, else NULL.
*
* STATUS, KIND, PROP_MODS and OLD_CHECKSUM are the current in-db values of
* the node LOCAL_ABSPATH.
*/
static svn_error_t *
process_committed_leaf(svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t via_recurse,
svn_wc__db_status_t status,
svn_node_kind_t kind,
svn_boolean_t prop_mods,
const svn_checksum_t *old_checksum,
svn_revnum_t new_revnum,
apr_time_t new_changed_date,
const char *new_changed_author,
apr_hash_t *new_dav_cache,
svn_boolean_t no_unlock,
svn_boolean_t keep_changelist,
const svn_checksum_t *checksum,
apr_pool_t *scratch_pool)
{
svn_revnum_t new_changed_rev = new_revnum;
svn_skel_t *work_item = NULL;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
{
const char *adm_abspath;
if (kind == svn_node_dir)
adm_abspath = local_abspath;
else
adm_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
SVN_ERR(svn_wc__write_check(db, adm_abspath, scratch_pool));
}
if (status == svn_wc__db_status_deleted)
{
return svn_error_trace(
svn_wc__db_base_remove(
db, local_abspath,
FALSE /* keep_as_working */,
FALSE /* queue_deletes */,
TRUE /* remove_locks */,
(! via_recurse)
? new_revnum : SVN_INVALID_REVNUM,
NULL, NULL,
scratch_pool));
}
else if (status == svn_wc__db_status_not_present)
{
/* We are committing the leaf of a copy operation.
We leave the not-present marker to allow pulling in excluded
children of a copy.
The next update will remove the not-present marker. */
return SVN_NO_ERROR;
}
SVN_ERR_ASSERT(status == svn_wc__db_status_normal
|| status == svn_wc__db_status_incomplete
|| status == svn_wc__db_status_added);
if (kind != svn_node_dir)
{
/* If we sent a delta (meaning: post-copy modification),
then this file will appear in the queue and so we should have
its checksum already. */
if (checksum == NULL)
{
/* It was copied and not modified. We must have a text
base for it. And the node should have a checksum. */
SVN_ERR_ASSERT(old_checksum != NULL);
checksum = old_checksum;
/* Is the node completely unmodified and are we recursing? */
if (via_recurse && !prop_mods)
{
/* If a copied node itself is not modified, but the op_root of
the copy is committed we have to make sure that changed_rev,
changed_date and changed_author don't change or the working
copy used for committing will show different last modified
information then a clean checkout of exactly the same
revisions. (Issue #3676) */
SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL,
NULL, &new_changed_rev,
&new_changed_date,
&new_changed_author, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL,
db, local_abspath,
scratch_pool, scratch_pool));
}
}
SVN_ERR(svn_wc__wq_build_file_commit(&work_item,
db, local_abspath,
prop_mods,
scratch_pool, scratch_pool));
}
/* The new text base will be found in the pristine store by its checksum. */
SVN_ERR(svn_wc__db_global_commit(db, local_abspath,
new_revnum, new_changed_rev,
new_changed_date, new_changed_author,
checksum,
NULL /* new_children */,
new_dav_cache,
keep_changelist,
no_unlock,
work_item,
scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__process_committed_internal(svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t recurse,
svn_boolean_t top_of_recurse,
svn_revnum_t new_revnum,
apr_time_t new_date,
const char *rev_author,
apr_hash_t *new_dav_cache,
svn_boolean_t no_unlock,
svn_boolean_t keep_changelist,
const svn_checksum_t *sha1_checksum,
const svn_wc_committed_queue_t *queue,
apr_pool_t *scratch_pool)
{
svn_wc__db_status_t status;
svn_node_kind_t kind;
const svn_checksum_t *old_checksum;
svn_boolean_t prop_mods;
SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, &old_checksum, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, &prop_mods, NULL, NULL, NULL,
db, local_abspath,
scratch_pool, scratch_pool));
/* NOTE: be wary of making crazy semantic changes in this function, since
svn_wc_process_committed4() calls this. */
SVN_ERR(process_committed_leaf(db, local_abspath, !top_of_recurse,
status, kind, prop_mods, old_checksum,
new_revnum, new_date, rev_author,
new_dav_cache,
no_unlock, keep_changelist,
sha1_checksum,
scratch_pool));
/* Only check for recursion on nodes that have children */
if (kind != svn_node_file
|| status == svn_wc__db_status_not_present
|| status == svn_wc__db_status_excluded
|| status == svn_wc__db_status_server_excluded
/* Node deleted -> then no longer a directory */
|| status == svn_wc__db_status_deleted)
{
return SVN_NO_ERROR;
}
if (recurse)
{
const apr_array_header_t *children;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
int i;
/* Read PATH's entries; this is the absolute path. */
SVN_ERR(svn_wc__db_read_children(&children, db, local_abspath,
scratch_pool, iterpool));
/* Recursively loop over all children. */
for (i = 0; i < children->nelts; i++)
{
const char *name = APR_ARRAY_IDX(children, i, const char *);
const char *this_abspath;
const committed_queue_item_t *cqi;
svn_pool_clear(iterpool);
this_abspath = svn_dirent_join(local_abspath, name, iterpool);
sha1_checksum = NULL;
cqi = svn_hash_gets(queue->queue, this_abspath);
if (cqi != NULL)
sha1_checksum = cqi->sha1_checksum;
/* Recurse. Pass NULL for NEW_DAV_CACHE, because the
ones present in the current call are only applicable to
this one committed item. */
SVN_ERR(svn_wc__process_committed_internal(
db, this_abspath,
TRUE /* recurse */,
FALSE /* top_of_recurse */,
new_revnum, new_date,
rev_author,
NULL /* new_dav_cache */,
TRUE /* no_unlock */,
keep_changelist,
sha1_checksum,
queue,
iterpool));
}
svn_pool_destroy(iterpool);
}
return SVN_NO_ERROR;
}
apr_hash_t *
svn_wc__prop_array_to_hash(const apr_array_header_t *props,
apr_pool_t *result_pool)
{
int i;
apr_hash_t *prophash;
if (props == NULL || props->nelts == 0)
return NULL;
prophash = apr_hash_make(result_pool);
for (i = 0; i < props->nelts; i++)
{
const svn_prop_t *prop = APR_ARRAY_IDX(props, i, const svn_prop_t *);
if (prop->value != NULL)
svn_hash_sets(prophash, prop->name, prop->value);
}
return prophash;
}
svn_wc_committed_queue_t *
svn_wc_committed_queue_create(apr_pool_t *pool)
{
svn_wc_committed_queue_t *q;
q = apr_palloc(pool, sizeof(*q));
q->pool = pool;
q->queue = apr_hash_make(pool);
q->have_recursive = FALSE;
return q;
}
svn_error_t *
svn_wc_queue_committed3(svn_wc_committed_queue_t *queue,
svn_wc_context_t *wc_ctx,
const char *local_abspath,
svn_boolean_t recurse,
const apr_array_header_t *wcprop_changes,
svn_boolean_t remove_lock,
svn_boolean_t remove_changelist,
const svn_checksum_t *sha1_checksum,
apr_pool_t *scratch_pool)
{
committed_queue_item_t *cqi;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
queue->have_recursive |= recurse;
/* Use the same pool as the one QUEUE was allocated in,
to prevent lifetime issues. Intermediate operations
should use SCRATCH_POOL. */
/* Add to the array with paths and options */
cqi = apr_palloc(queue->pool, sizeof(*cqi));
cqi->local_abspath = local_abspath;
cqi->recurse = recurse;
cqi->no_unlock = !remove_lock;
cqi->keep_changelist = !remove_changelist;
cqi->sha1_checksum = sha1_checksum;
cqi->new_dav_cache = svn_wc__prop_array_to_hash(wcprop_changes, queue->pool);
svn_hash_sets(queue->queue, local_abspath, cqi);
return SVN_NO_ERROR;
}
/* Return TRUE if any item of QUEUE is a parent of ITEM and will be
processed recursively, return FALSE otherwise.
The algorithmic complexity of this search implementation is O(queue
length), but it's quite quick.
*/
static svn_boolean_t
have_recursive_parent(apr_hash_t *queue,
const committed_queue_item_t *item,
apr_pool_t *scratch_pool)
{
apr_hash_index_t *hi;
const char *local_abspath = item->local_abspath;
for (hi = apr_hash_first(scratch_pool, queue); hi; hi = apr_hash_next(hi))
{
const committed_queue_item_t *qi = svn__apr_hash_index_val(hi);
if (qi == item)
continue;
if (qi->recurse && svn_dirent_is_child(qi->local_abspath, local_abspath,
NULL))
return TRUE;
}
return FALSE;
}
svn_error_t *
svn_wc_process_committed_queue2(svn_wc_committed_queue_t *queue,
svn_wc_context_t *wc_ctx,
svn_revnum_t new_revnum,
const char *rev_date,
const char *rev_author,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
apr_array_header_t *sorted_queue;
int i;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
apr_time_t new_date;
apr_hash_t *run_wqs = apr_hash_make(scratch_pool);
apr_hash_index_t *hi;
if (rev_date)
SVN_ERR(svn_time_from_cstring(&new_date, rev_date, iterpool));
else
new_date = 0;
/* Process the queued items in order of their paths. (The requirement is
* probably just that a directory must be processed before its children.) */
sorted_queue = svn_sort__hash(queue->queue, svn_sort_compare_items_as_paths,
scratch_pool);
for (i = 0; i < sorted_queue->nelts; i++)
{
const svn_sort__item_t *sort_item
= &APR_ARRAY_IDX(sorted_queue, i, svn_sort__item_t);
const committed_queue_item_t *cqi = sort_item->value;
const char *wcroot_abspath;
svn_pool_clear(iterpool);
/* Skip this item if it is a child of a recursive item, because it has
been (or will be) accounted for when that recursive item was (or
will be) processed. */
if (queue->have_recursive && have_recursive_parent(queue->queue, cqi,
iterpool))
continue;
SVN_ERR(svn_wc__process_committed_internal(
wc_ctx->db, cqi->local_abspath,
cqi->recurse,
TRUE /* top_of_recurse */,
new_revnum, new_date, rev_author,
cqi->new_dav_cache,
cqi->no_unlock,
cqi->keep_changelist,
cqi->sha1_checksum, queue,
iterpool));
/* Don't run the wq now, but remember that we must call it for this
working copy */
SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath,
wc_ctx->db, cqi->local_abspath,
iterpool, iterpool));
if (! svn_hash_gets(run_wqs, wcroot_abspath))
{
wcroot_abspath = apr_pstrdup(scratch_pool, wcroot_abspath);
svn_hash_sets(run_wqs, wcroot_abspath, wcroot_abspath);
}
}
/* Make sure nothing happens if this function is called again. */
apr_hash_clear(queue->queue);
/* Ok; everything is committed now. Now we can start calling callbacks */
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
for (hi = apr_hash_first(scratch_pool, run_wqs);
hi;
hi = apr_hash_next(hi))
{
const char *wcroot_abspath = svn__apr_hash_index_key(hi);
svn_pool_clear(iterpool);
SVN_ERR(svn_wc__wq_run(wc_ctx->db, wcroot_abspath,
cancel_func, cancel_baton,
iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Schedule the single node at LOCAL_ABSPATH, of kind KIND, for addition in
* its parent directory in the WC. It will have the regular properties
* provided in PROPS, or none if that is NULL.
*
* If the node is a file, set its on-disk executable and read-only bits to
* match its properties and lock state,
* ### only if it has an svn:executable or svn:needs-lock property.
* ### This is to match the previous behaviour of setting its props
* afterwards by calling svn_wc_prop_set4(), but is not very clean.
*
* Sync the on-disk executable and read-only bits accordingly.
*/
static svn_error_t *
add_from_disk(svn_wc__db_t *db,
const char *local_abspath,
svn_node_kind_t kind,
const apr_hash_t *props,
apr_pool_t *scratch_pool)
{
if (kind == svn_node_file)
{
svn_skel_t *work_item = NULL;
if (props && (svn_prop_get_value(props, SVN_PROP_EXECUTABLE)
|| svn_prop_get_value(props, SVN_PROP_NEEDS_LOCK)))
SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, db, local_abspath,
scratch_pool, scratch_pool));
SVN_ERR(svn_wc__db_op_add_file(db, local_abspath, props, work_item,
scratch_pool));
if (work_item)
SVN_ERR(svn_wc__wq_run(db, local_abspath, NULL, NULL, scratch_pool));
}
else
{
SVN_ERR(svn_wc__db_op_add_directory(db, local_abspath, props, NULL,
scratch_pool));
}
return SVN_NO_ERROR;
}
/* Set *REPOS_ROOT_URL and *REPOS_UUID to the repository of the parent of
LOCAL_ABSPATH. REPOS_ROOT_URL and/or REPOS_UUID may be NULL if not
wanted. Check that the parent of LOCAL_ABSPATH is a versioned directory
in a state in which a new child node can be scheduled for addition;
return an error if not. */
static svn_error_t *
check_can_add_to_parent(const char **repos_root_url,
const char **repos_uuid,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
svn_wc__db_status_t parent_status;
svn_node_kind_t parent_kind;
svn_error_t *err;
SVN_ERR(svn_wc__write_check(db, parent_abspath, scratch_pool));
err = svn_wc__db_read_info(&parent_status, &parent_kind, NULL,
NULL, repos_root_url, repos_uuid, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL,
db, parent_abspath, result_pool, scratch_pool);
if (err
|| parent_status == svn_wc__db_status_not_present
|| parent_status == svn_wc__db_status_excluded
|| parent_status == svn_wc__db_status_server_excluded)
{
return
svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, err,
_("Can't find parent directory's node while"
" trying to add '%s'"),
svn_dirent_local_style(local_abspath,
scratch_pool));
}
else if (parent_status == svn_wc__db_status_deleted)
{
return
svn_error_createf(SVN_ERR_WC_SCHEDULE_CONFLICT, NULL,
_("Can't add '%s' to a parent directory"
" scheduled for deletion"),
svn_dirent_local_style(local_abspath,
scratch_pool));
}
else if (parent_kind != svn_node_dir)
return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
_("Can't schedule an addition of '%s'"
" below a not-directory node"),
svn_dirent_local_style(local_abspath,
scratch_pool));
/* If we haven't found the repository info yet, find it now. */
if ((repos_root_url && ! *repos_root_url)
|| (repos_uuid && ! *repos_uuid))
{
if (parent_status == svn_wc__db_status_added)
SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL,
repos_root_url, repos_uuid, NULL,
NULL, NULL, NULL,
db, parent_abspath,
result_pool, scratch_pool));
else
SVN_ERR(svn_wc__db_scan_base_repos(NULL,
repos_root_url, repos_uuid,
db, parent_abspath,
result_pool, scratch_pool));
}
return SVN_NO_ERROR;
}
/* Check that the on-disk item at LOCAL_ABSPATH can be scheduled for
* addition to its WC parent directory.
*
* Set *KIND_P to the kind of node to be added, *DB_ROW_EXISTS_P to whether
* it is already a versioned path, and if so, *IS_WC_ROOT_P to whether it's
* a WC root.
*
* ### The checks here, and the outputs, are geared towards svn_wc_add4().
*/
static svn_error_t *
check_can_add_node(svn_node_kind_t *kind_p,
svn_boolean_t *db_row_exists_p,
svn_boolean_t *is_wc_root_p,
svn_wc__db_t *db,
const char *local_abspath,
const char *copyfrom_url,
svn_revnum_t copyfrom_rev,
apr_pool_t *scratch_pool)
{
const char *base_name = svn_dirent_basename(local_abspath, scratch_pool);
svn_boolean_t is_wc_root;
svn_node_kind_t kind;
svn_boolean_t is_special;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR_ASSERT(!copyfrom_url || (svn_uri_is_canonical(copyfrom_url,
scratch_pool)
&& SVN_IS_VALID_REVNUM(copyfrom_rev)));
/* Check that the proposed node has an acceptable name. */
if (svn_wc_is_adm_dir(base_name, scratch_pool))
return svn_error_createf
(SVN_ERR_ENTRY_FORBIDDEN, NULL,
_("Can't create an entry with a reserved name while trying to add '%s'"),
svn_dirent_local_style(local_abspath, scratch_pool));
SVN_ERR(svn_path_check_valid(local_abspath, scratch_pool));
/* Make sure something's there; set KIND and *KIND_P. */
SVN_ERR(svn_io_check_special_path(local_abspath, &kind, &is_special,
scratch_pool));
if (kind == svn_node_none)
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("'%s' not found"),
svn_dirent_local_style(local_abspath,
scratch_pool));
if (kind == svn_node_unknown)
return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Unsupported node kind for path '%s'"),
svn_dirent_local_style(local_abspath,
scratch_pool));
if (kind_p)
*kind_p = kind;
/* Determine whether a DB row for this node EXISTS, and whether it
IS_WC_ROOT. If it exists, check that it is in an acceptable state for
adding the new node; if not, return an error. */
{
svn_wc__db_status_t status;
svn_boolean_t conflicted;
svn_boolean_t exists;
svn_error_t *err
= svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
&conflicted,
NULL, NULL, NULL, NULL, NULL, NULL,
db, local_abspath,
scratch_pool, scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
svn_error_clear(err);
exists = FALSE;
is_wc_root = FALSE;
}
else
{
is_wc_root = FALSE;
exists = TRUE;
/* Note that the node may be in conflict even if it does not
* exist on disk (certain tree conflict scenarios). */
if (conflicted)
return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
_("'%s' is an existing item in conflict; "
"please mark the conflict as resolved "
"before adding a new item here"),
svn_dirent_local_style(local_abspath,
scratch_pool));
switch (status)
{
case svn_wc__db_status_not_present:
break;
case svn_wc__db_status_deleted:
/* A working copy root should never have a WORKING_NODE */
SVN_ERR_ASSERT(!is_wc_root);
break;
case svn_wc__db_status_normal:
SVN_ERR(svn_wc__db_is_wcroot(&is_wc_root, db, local_abspath,
scratch_pool));
if (is_wc_root && copyfrom_url)
{
/* Integrate a sub working copy in a parent working copy
(legacy behavior) */
break;
}
else if (is_wc_root && is_special)
{
/* Adding a symlink to a working copy root.
(special_tests.py 23: externals as symlink targets) */
break;
}
/* else: Fall through in default error */
default:
return svn_error_createf(
SVN_ERR_ENTRY_EXISTS, NULL,
_("'%s' is already under version control"),
svn_dirent_local_style(local_abspath,
scratch_pool));
}
} /* err */
if (db_row_exists_p)
*db_row_exists_p = exists;
if (is_wc_root_p)
*is_wc_root_p = is_wc_root;
}
return SVN_NO_ERROR;
}
/* Convert the nested pristine working copy rooted at LOCAL_ABSPATH into
* a copied subtree in the outer working copy.
*
* LOCAL_ABSPATH must be the root of a nested working copy that has no
* local modifications. The parent directory of LOCAL_ABSPATH must be a
* versioned directory in the outer WC, and must belong to the same
* repository as the nested WC. The nested WC will be integrated into the
* parent's WC, and will no longer be a separate WC. */
static svn_error_t *
integrate_nested_wc_as_copy(svn_wc_context_t *wc_ctx,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
svn_wc__db_t *db = wc_ctx->db;
const char *moved_abspath;
/* Drop any references to the wc that is to be rewritten */
SVN_ERR(svn_wc__db_drop_root(db, local_abspath, scratch_pool));
/* Move the admin dir from the wc to a temporary location: MOVED_ABSPATH */
{
const char *tmpdir_abspath;
const char *moved_adm_abspath;
const char *adm_abspath;
SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmpdir_abspath, db,
svn_dirent_dirname(local_abspath,
scratch_pool),
scratch_pool, scratch_pool));
SVN_ERR(svn_io_open_unique_file3(NULL, &moved_abspath, tmpdir_abspath,
svn_io_file_del_on_close,
scratch_pool, scratch_pool));
SVN_ERR(svn_io_dir_make(moved_abspath, APR_OS_DEFAULT, scratch_pool));
adm_abspath = svn_wc__adm_child(local_abspath, "", scratch_pool);
moved_adm_abspath = svn_wc__adm_child(moved_abspath, "", scratch_pool);
SVN_ERR(svn_io_file_move(adm_abspath, moved_adm_abspath, scratch_pool));
}
/* Copy entries from temporary location into the main db */
SVN_ERR(svn_wc_copy3(wc_ctx, moved_abspath, local_abspath,
TRUE /* metadata_only */,
NULL, NULL, NULL, NULL, scratch_pool));
/* Cleanup the temporary admin dir */
SVN_ERR(svn_wc__db_drop_root(db, moved_abspath, scratch_pool));
SVN_ERR(svn_io_remove_dir2(moved_abspath, FALSE, NULL, NULL,
scratch_pool));
/* The subdir is now part of our parent working copy. Our caller assumes
that we return the new node locked, so obtain a lock if we didn't
receive the lock via our depth infinity lock */
{
svn_boolean_t owns_lock;
SVN_ERR(svn_wc__db_wclock_owns_lock(&owns_lock, db, local_abspath,
FALSE, scratch_pool));
if (!owns_lock)
SVN_ERR(svn_wc__db_wclock_obtain(db, local_abspath, 0, FALSE,
scratch_pool));
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_add4(svn_wc_context_t *wc_ctx,
const char *local_abspath,
svn_depth_t depth,
const char *copyfrom_url,
svn_revnum_t copyfrom_rev,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool)
{
svn_wc__db_t *db = wc_ctx->db;
svn_node_kind_t kind;
svn_boolean_t db_row_exists;
svn_boolean_t is_wc_root;
const char *repos_root_url;
const char *repos_uuid;
SVN_ERR(check_can_add_node(&kind, &db_row_exists, &is_wc_root,
db, local_abspath, copyfrom_url, copyfrom_rev,
scratch_pool));
/* Get REPOS_ROOT_URL and REPOS_UUID. Check that the
parent is a versioned directory in an acceptable state. */
SVN_ERR(check_can_add_to_parent(&repos_root_url, &repos_uuid,
db, local_abspath, scratch_pool,
scratch_pool));
/* If we're performing a repos-to-WC copy, check that the copyfrom
repository is the same as the parent dir's repository. */
if (copyfrom_url && !svn_uri__is_ancestor(repos_root_url, copyfrom_url))
return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("The URL '%s' has a different repository "
"root than its parent"), copyfrom_url);
/* Verify that we can actually integrate the inner working copy */
if (is_wc_root)
{
const char *repos_relpath, *inner_repos_root_url, *inner_repos_uuid;
const char *inner_url;
SVN_ERR(svn_wc__db_scan_base_repos(&repos_relpath,
&inner_repos_root_url,
&inner_repos_uuid,
db, local_abspath,
scratch_pool, scratch_pool));
if (strcmp(inner_repos_uuid, repos_uuid)
|| strcmp(repos_root_url, inner_repos_root_url))
return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Can't schedule the working copy at '%s' "
"from repository '%s' with uuid '%s' "
"for addition under a working copy from "
"repository '%s' with uuid '%s'."),
svn_dirent_local_style(local_abspath,
scratch_pool),
inner_repos_root_url, inner_repos_uuid,
repos_root_url, repos_uuid);
inner_url = svn_path_url_add_component2(repos_root_url, repos_relpath,
scratch_pool);
if (strcmp(copyfrom_url, inner_url))
return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Can't add '%s' with URL '%s', but with "
"the data from '%s'"),
svn_dirent_local_style(local_abspath,
scratch_pool),
copyfrom_url, inner_url);
}
if (!copyfrom_url) /* Case 2a: It's a simple add */
{
SVN_ERR(add_from_disk(db, local_abspath, kind, NULL,
scratch_pool));
if (kind == svn_node_dir && !db_row_exists)
{
/* If using the legacy 1.6 interface the parent lock may not
be recursive and add is expected to lock the new dir.
### Perhaps the lock should be created in the same
transaction that adds the node? */
svn_boolean_t owns_lock;
SVN_ERR(svn_wc__db_wclock_owns_lock(&owns_lock, db, local_abspath,
FALSE, scratch_pool));
if (!owns_lock)
SVN_ERR(svn_wc__db_wclock_obtain(db, local_abspath, 0, FALSE,
scratch_pool));
}
}
else if (!is_wc_root) /* Case 2b: It's a copy from the repository */
{
if (kind == svn_node_file)
{
/* This code should never be used, as it doesn't install proper
pristine and/or properties. But it was not an error in the old
version of this function.
===> Use svn_wc_add_repos_file4() directly! */
svn_stream_t *content = svn_stream_empty(scratch_pool);
SVN_ERR(svn_wc_add_repos_file4(wc_ctx, local_abspath,
content, NULL, NULL, NULL,
copyfrom_url, copyfrom_rev,
cancel_func, cancel_baton,
scratch_pool));
}
else
{
const char *repos_relpath =
svn_uri_skip_ancestor(repos_root_url, copyfrom_url, scratch_pool);
SVN_ERR(svn_wc__db_op_copy_dir(db, local_abspath,
apr_hash_make(scratch_pool),
copyfrom_rev, 0, NULL,
repos_relpath,
repos_root_url, repos_uuid,
copyfrom_rev,
- NULL /* children */, FALSE, depth,
+ NULL /* children */, depth,
+ FALSE /* is_move */,
NULL /* conflicts */,
NULL /* work items */,
scratch_pool));
}
}
else /* Case 1: Integrating a separate WC into this one, in place */
{
SVN_ERR(integrate_nested_wc_as_copy(wc_ctx, local_abspath,
scratch_pool));
}
/* Report the addition to the caller. */
if (notify_func != NULL)
{
svn_wc_notify_t *notify = svn_wc_create_notify(local_abspath,
svn_wc_notify_add,
scratch_pool);
notify->kind = kind;
(*notify_func)(notify_baton, notify, scratch_pool);
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_add_from_disk2(svn_wc_context_t *wc_ctx,
const char *local_abspath,
const apr_hash_t *props,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool)
{
svn_node_kind_t kind;
SVN_ERR(check_can_add_node(&kind, NULL, NULL, wc_ctx->db, local_abspath,
NULL, SVN_INVALID_REVNUM, scratch_pool));
SVN_ERR(check_can_add_to_parent(NULL, NULL, wc_ctx->db, local_abspath,
scratch_pool, scratch_pool));
/* Canonicalize and check the props */
if (props)
{
apr_hash_t *new_props;
SVN_ERR(svn_wc__canonicalize_props(
&new_props,
local_abspath, kind, props, FALSE /* skip_some_checks */,
scratch_pool, scratch_pool));
props = new_props;
}
/* Add to the DB and maybe update on-disk executable read-only bits */
SVN_ERR(add_from_disk(wc_ctx->db, local_abspath, kind, props,
scratch_pool));
/* Report the addition to the caller. */
if (notify_func != NULL)
{
svn_wc_notify_t *notify = svn_wc_create_notify(local_abspath,
svn_wc_notify_add,
scratch_pool);
notify->kind = kind;
notify->mime_type = svn_prop_get_value(props, SVN_PROP_MIME_TYPE);
(*notify_func)(notify_baton, notify, scratch_pool);
}
return SVN_NO_ERROR;
}
/* Return a path where nothing exists on disk, within the admin directory
belonging to the WCROOT_ABSPATH directory. */
static const char *
nonexistent_path(const char *wcroot_abspath, apr_pool_t *scratch_pool)
{
return svn_wc__adm_child(wcroot_abspath, SVN_WC__ADM_NONEXISTENT_PATH,
scratch_pool);
}
svn_error_t *
svn_wc_get_pristine_copy_path(const char *path,
const char **pristine_path,
apr_pool_t *pool)
{
svn_wc__db_t *db;
const char *local_abspath;
svn_error_t *err;
SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
SVN_ERR(svn_wc__db_open(&db, NULL, FALSE, TRUE, pool, pool));
/* DB is now open. This is seemingly a "light" function that a caller
may use repeatedly despite error return values. The rest of this
function should aggressively close DB, even in the error case. */
err = svn_wc__text_base_path_to_read(pristine_path, db, local_abspath,
pool, pool);
if (err && err->apr_err == SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
{
/* The node doesn't exist, so return a non-existent path located
in WCROOT/.svn/ */
const char *wcroot_abspath;
svn_error_clear(err);
err = svn_wc__db_get_wcroot(&wcroot_abspath, db, local_abspath,
pool, pool);
if (err == NULL)
*pristine_path = nonexistent_path(wcroot_abspath, pool);
}
return svn_error_compose_create(err, svn_wc__db_close(db));
}
svn_error_t *
svn_wc_get_pristine_contents2(svn_stream_t **contents,
svn_wc_context_t *wc_ctx,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
return svn_error_trace(svn_wc__get_pristine_contents(contents, NULL,
wc_ctx->db,
local_abspath,
result_pool,
scratch_pool));
}
typedef struct get_pristine_lazyopen_baton_t
{
svn_wc_context_t *wc_ctx;
const char *wri_abspath;
const svn_checksum_t *checksum;
} get_pristine_lazyopen_baton_t;
/* Implements svn_stream_lazyopen_func_t */
static svn_error_t *
get_pristine_lazyopen_func(svn_stream_t **stream,
void *baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
get_pristine_lazyopen_baton_t *b = baton;
const svn_checksum_t *sha1_checksum;
/* svn_wc__db_pristine_read() wants a SHA1, so if we have an MD5,
we'll use it to lookup the SHA1. */
if (b->checksum->kind == svn_checksum_sha1)
sha1_checksum = b->checksum;
else
SVN_ERR(svn_wc__db_pristine_get_sha1(&sha1_checksum, b->wc_ctx->db,
b->wri_abspath, b->checksum,
scratch_pool, scratch_pool));
SVN_ERR(svn_wc__db_pristine_read(stream, NULL, b->wc_ctx->db,
b->wri_abspath, sha1_checksum,
result_pool, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__get_pristine_contents_by_checksum(svn_stream_t **contents,
svn_wc_context_t *wc_ctx,
const char *wri_abspath,
const svn_checksum_t *checksum,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_boolean_t present;
*contents = NULL;
SVN_ERR(svn_wc__db_pristine_check(&present, wc_ctx->db, wri_abspath,
checksum, scratch_pool));
if (present)
{
get_pristine_lazyopen_baton_t *gpl_baton;
gpl_baton = apr_pcalloc(result_pool, sizeof(*gpl_baton));
gpl_baton->wc_ctx = wc_ctx;
gpl_baton->wri_abspath = wri_abspath;
gpl_baton->checksum = checksum;
*contents = svn_stream_lazyopen_create(get_pristine_lazyopen_func,
gpl_baton, FALSE, result_pool);
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_add_lock2(svn_wc_context_t *wc_ctx,
const char *local_abspath,
const svn_lock_t *lock,
apr_pool_t *scratch_pool)
{
svn_wc__db_lock_t db_lock;
svn_error_t *err;
const svn_string_t *needs_lock;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
/* ### Enable after fixing callers */
/*SVN_ERR(svn_wc__write_check(wc_ctx->db,
svn_dirent_dirname(local_abspath, scratch_pool),
scratch_pool));*/
db_lock.token = lock->token;
db_lock.owner = lock->owner;
db_lock.comment = lock->comment;
db_lock.date = lock->creation_date;
err = svn_wc__db_lock_add(wc_ctx->db, local_abspath, &db_lock, scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
/* Remap the error. */
svn_error_clear(err);
return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
_("'%s' is not under version control"),
svn_dirent_local_style(local_abspath,
scratch_pool));
}
/* if svn:needs-lock is present, then make the file read-write. */
err = svn_wc__internal_propget(&needs_lock, wc_ctx->db, local_abspath,
SVN_PROP_NEEDS_LOCK, scratch_pool,
scratch_pool);
if (err && err->apr_err == SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
{
/* The node has non wc representation (e.g. deleted), so
we don't want to touch the in-wc file */
svn_error_clear(err);
return SVN_NO_ERROR;
}
SVN_ERR(err);
if (needs_lock)
SVN_ERR(svn_io_set_file_read_write(local_abspath, FALSE, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_remove_lock2(svn_wc_context_t *wc_ctx,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
svn_error_t *err;
const svn_string_t *needs_lock;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
/* ### Enable after fixing callers */
/*SVN_ERR(svn_wc__write_check(wc_ctx->db,
svn_dirent_dirname(local_abspath, scratch_pool),
scratch_pool));*/
err = svn_wc__db_lock_remove(wc_ctx->db, local_abspath, scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
/* Remap the error. */
svn_error_clear(err);
return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
_("'%s' is not under version control"),
svn_dirent_local_style(local_abspath,
scratch_pool));
}
/* if svn:needs-lock is present, then make the file read-only. */
err = svn_wc__internal_propget(&needs_lock, wc_ctx->db, local_abspath,
SVN_PROP_NEEDS_LOCK, scratch_pool,
scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
return svn_error_trace(err);
svn_error_clear(err);
return SVN_NO_ERROR; /* Node is shadowed and/or deleted,
so we shouldn't apply its lock */
}
if (needs_lock)
SVN_ERR(svn_io_set_file_read_only(local_abspath, FALSE, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_set_changelist2(svn_wc_context_t *wc_ctx,
const char *local_abspath,
const char *new_changelist,
svn_depth_t depth,
const apr_array_header_t *changelist_filter,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool)
{
/* Assert that we aren't being asked to set an empty changelist. */
SVN_ERR_ASSERT(! (new_changelist && new_changelist[0] == '\0'));
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_op_set_changelist(wc_ctx->db, local_abspath,
new_changelist, changelist_filter,
depth, notify_func, notify_baton,
cancel_func, cancel_baton,
scratch_pool));
return SVN_NO_ERROR;
}
struct get_cl_fn_baton
{
svn_wc__db_t *db;
apr_hash_t *clhash;
svn_changelist_receiver_t callback_func;
void *callback_baton;
};
static svn_error_t *
get_node_changelist(const char *local_abspath,
svn_node_kind_t kind,
void *baton,
apr_pool_t *scratch_pool)
{
struct get_cl_fn_baton *b = baton;
const char *changelist;
SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, &changelist,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
b->db, local_abspath,
scratch_pool, scratch_pool));
if (svn_wc__internal_changelist_match(b->db, local_abspath, b->clhash,
scratch_pool))
SVN_ERR(b->callback_func(b->callback_baton, local_abspath,
changelist, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_get_changelists(svn_wc_context_t *wc_ctx,
const char *local_abspath,
svn_depth_t depth,
const apr_array_header_t *changelist_filter,
svn_changelist_receiver_t callback_func,
void *callback_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
struct get_cl_fn_baton gnb;
gnb.db = wc_ctx->db;
gnb.clhash = NULL;
gnb.callback_func = callback_func;
gnb.callback_baton = callback_baton;
if (changelist_filter)
SVN_ERR(svn_hash_from_cstring_keys(&gnb.clhash, changelist_filter,
scratch_pool));
return svn_error_trace(
svn_wc__internal_walk_children(wc_ctx->db, local_abspath, FALSE,
changelist_filter, get_node_changelist,
&gnb, depth,
cancel_func, cancel_baton,
scratch_pool));
}
svn_boolean_t
svn_wc__internal_changelist_match(svn_wc__db_t *db,
const char *local_abspath,
const apr_hash_t *clhash,
apr_pool_t *scratch_pool)
{
svn_error_t *err;
const char *changelist;
if (clhash == NULL)
return TRUE;
err = svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, &changelist,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
db, local_abspath, scratch_pool, scratch_pool);
if (err)
{
svn_error_clear(err);
return FALSE;
}
return (changelist
&& svn_hash_gets((apr_hash_t *)clhash, changelist) != NULL);
}
svn_boolean_t
svn_wc__changelist_match(svn_wc_context_t *wc_ctx,
const char *local_abspath,
const apr_hash_t *clhash,
apr_pool_t *scratch_pool)
{
return svn_wc__internal_changelist_match(wc_ctx->db, local_abspath, clhash,
scratch_pool);
}
Index: vendor/subversion/dist/subversion/libsvn_wc/cleanup.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_wc/cleanup.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_wc/cleanup.c (revision 286501)
@@ -1,231 +1,184 @@
/*
* cleanup.c: handle cleaning up workqueue items
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include <string.h>
#include "svn_wc.h"
#include "svn_error.h"
#include "svn_pools.h"
#include "svn_io.h"
#include "svn_dirent_uri.h"
#include "wc.h"
#include "adm_files.h"
#include "lock.h"
#include "workqueue.h"
#include "private/svn_wc_private.h"
#include "svn_private_config.h"
/*** Recursively do log things. ***/
/* */
static svn_error_t *
can_be_cleaned(int *wc_format,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
SVN_ERR(svn_wc__internal_check_wc(wc_format, db,
local_abspath, FALSE, scratch_pool));
/* a "version" of 0 means a non-wc directory */
if (*wc_format == 0)
return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
_("'%s' is not a working copy directory"),
svn_dirent_local_style(local_abspath,
scratch_pool));
if (*wc_format < SVN_WC__WC_NG_VERSION)
return svn_error_create(SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL,
_("Log format too old, please use "
"Subversion 1.6 or earlier"));
return SVN_NO_ERROR;
}
-/* Do a modifed check for LOCAL_ABSPATH, and all working children, to force
- timestamp repair. */
+/* Dummy svn_wc_status_func4_t implementation */
static svn_error_t *
-repair_timestamps(svn_wc__db_t *db,
- const char *local_abspath,
- svn_cancel_func_t cancel_func,
- void *cancel_baton,
- apr_pool_t *scratch_pool)
+status_dummy_callback(void *baton,
+ const char *local_abspath,
+ const svn_wc_status3_t *status,
+ apr_pool_t *scratch_pool)
{
- svn_node_kind_t kind;
- svn_wc__db_status_t status;
-
- if (cancel_func)
- SVN_ERR(cancel_func(cancel_baton));
-
- SVN_ERR(svn_wc__db_read_info(&status, &kind,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL,
- db, local_abspath, scratch_pool, scratch_pool));
-
- if (status == svn_wc__db_status_server_excluded
- || status == svn_wc__db_status_deleted
- || status == svn_wc__db_status_excluded
- || status == svn_wc__db_status_not_present)
- return SVN_NO_ERROR;
-
- if (kind == svn_node_file
- || kind == svn_node_symlink)
- {
- svn_boolean_t modified;
- SVN_ERR(svn_wc__internal_file_modified_p(&modified,
- db, local_abspath, FALSE,
- scratch_pool));
- }
- else if (kind == svn_node_dir)
- {
- apr_pool_t *iterpool = svn_pool_create(scratch_pool);
- const apr_array_header_t *children;
- int i;
-
- SVN_ERR(svn_wc__db_read_children_of_working_node(&children, db,
- local_abspath,
- scratch_pool,
- iterpool));
- for (i = 0; i < children->nelts; ++i)
- {
- const char *child_abspath;
-
- svn_pool_clear(iterpool);
-
- child_abspath = svn_dirent_join(local_abspath,
- APR_ARRAY_IDX(children, i,
- const char *),
- iterpool);
-
- SVN_ERR(repair_timestamps(db, child_abspath,
- cancel_func, cancel_baton, iterpool));
- }
- svn_pool_destroy(iterpool);
- }
-
return SVN_NO_ERROR;
}
/* */
static svn_error_t *
cleanup_internal(svn_wc__db_t *db,
const char *dir_abspath,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
int wc_format;
svn_boolean_t is_wcroot;
const char *lock_abspath;
/* Can we even work with this directory? */
SVN_ERR(can_be_cleaned(&wc_format, db, dir_abspath, scratch_pool));
/* We cannot obtain a lock on a directory that's within a locked
subtree, so always run cleanup from the lock owner. */
SVN_ERR(svn_wc__db_wclock_find_root(&lock_abspath, db, dir_abspath,
scratch_pool, scratch_pool));
if (lock_abspath)
dir_abspath = lock_abspath;
SVN_ERR(svn_wc__db_wclock_obtain(db, dir_abspath, -1, TRUE, scratch_pool));
/* Run our changes before the subdirectories. We may not have to recurse
if we blow away a subdir. */
if (wc_format >= SVN_WC__HAS_WORK_QUEUE)
SVN_ERR(svn_wc__wq_run(db, dir_abspath, cancel_func, cancel_baton,
scratch_pool));
SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, db, dir_abspath, scratch_pool));
#ifdef SVN_DEBUG
SVN_ERR(svn_wc__db_verify(db, dir_abspath, scratch_pool));
#endif
/* Perform these operations if we lock the entire working copy.
Note that we really need to check a wcroot value and not
svn_wc__check_wcroot() as that function, will just return true
once we start sharing databases with externals.
*/
if (is_wcroot)
{
/* Cleanup the tmp area of the admin subdir, if running the log has not
removed it! The logs have been run, so anything left here has no hope
of being useful. */
SVN_ERR(svn_wc__adm_cleanup_tmp_area(db, dir_abspath, scratch_pool));
/* Remove unreferenced pristine texts */
SVN_ERR(svn_wc__db_pristine_cleanup(db, dir_abspath, scratch_pool));
}
- SVN_ERR(repair_timestamps(db, dir_abspath, cancel_func, cancel_baton,
- scratch_pool));
+ /* Instead of implementing a separate repair step here, use the standard
+ status walker's optimized implementation, which performs repairs when
+ there is a lock. */
+ SVN_ERR(svn_wc__internal_walk_status(db, dir_abspath, svn_depth_infinity,
+ FALSE /* get_all */,
+ FALSE /* no_ignore */,
+ FALSE /* ignore_text_mods */,
+ NULL /* ignore patterns */,
+ status_dummy_callback, NULL,
+ cancel_func, cancel_baton,
+ scratch_pool));
/* All done, toss the lock */
SVN_ERR(svn_wc__db_wclock_release(db, dir_abspath, scratch_pool));
return SVN_NO_ERROR;
}
/* ### possibly eliminate the WC_CTX parameter? callers really shouldn't
### be doing anything *but* running a cleanup, and we need a special
### DB anyway. ... *shrug* ... consider later. */
svn_error_t *
svn_wc_cleanup3(svn_wc_context_t *wc_ctx,
const char *local_abspath,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
svn_wc__db_t *db;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
/* We need a DB that allows a non-empty work queue (though it *will*
auto-upgrade). We'll handle everything manually. */
SVN_ERR(svn_wc__db_open(&db,
NULL /* ### config */, FALSE, FALSE,
scratch_pool, scratch_pool));
SVN_ERR(cleanup_internal(db, local_abspath, cancel_func, cancel_baton,
scratch_pool));
/* The DAV cache suffers from flakiness from time to time, and the
pre-1.7 prescribed workarounds aren't as user-friendly in WC-NG. */
SVN_ERR(svn_wc__db_base_clear_dav_cache_recursive(db, local_abspath,
scratch_pool));
SVN_ERR(svn_wc__db_vacuum(db, local_abspath, scratch_pool));
/* We're done with this DB, so proactively close it. */
SVN_ERR(svn_wc__db_close(db));
return SVN_NO_ERROR;
}
Index: vendor/subversion/dist/subversion/libsvn_wc/conflicts.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_wc/conflicts.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_wc/conflicts.c (revision 286501)
@@ -1,3141 +1,3164 @@
/*
* conflicts.c: routines for managing conflict data.
* NOTE: this code doesn't know where the conflict is
* actually stored.
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include <string.h>
#include <apr_pools.h>
#include <apr_tables.h>
#include <apr_hash.h>
#include <apr_errno.h>
#include "svn_hash.h"
#include "svn_types.h"
#include "svn_pools.h"
#include "svn_string.h"
#include "svn_error.h"
#include "svn_dirent_uri.h"
#include "svn_wc.h"
#include "svn_io.h"
#include "svn_diff.h"
#include "wc.h"
#include "wc_db.h"
#include "conflicts.h"
#include "workqueue.h"
#include "props.h"
#include "private/svn_wc_private.h"
#include "private/svn_skel.h"
#include "private/svn_string_private.h"
#include "svn_private_config.h"
/* --------------------------------------------------------------------
* Conflict skel management
*/
svn_skel_t *
svn_wc__conflict_skel_create(apr_pool_t *result_pool)
{
svn_skel_t *conflict_skel = svn_skel__make_empty_list(result_pool);
/* Add empty CONFLICTS list */
svn_skel__prepend(svn_skel__make_empty_list(result_pool), conflict_skel);
/* Add empty WHY list */
svn_skel__prepend(svn_skel__make_empty_list(result_pool), conflict_skel);
return conflict_skel;
}
svn_error_t *
svn_wc__conflict_skel_is_complete(svn_boolean_t *complete,
const svn_skel_t *conflict_skel)
{
*complete = FALSE;
if (svn_skel__list_length(conflict_skel) < 2)
return svn_error_create(SVN_ERR_INCOMPLETE_DATA, NULL,
_("Not a conflict skel"));
if (svn_skel__list_length(conflict_skel->children) < 2)
return SVN_NO_ERROR; /* WHY is not set */
if (svn_skel__list_length(conflict_skel->children->next) == 0)
return SVN_NO_ERROR; /* No conflict set */
*complete = TRUE;
return SVN_NO_ERROR;
}
/* Serialize a svn_wc_conflict_version_t before the existing data in skel */
static svn_error_t *
conflict__prepend_location(svn_skel_t *skel,
const svn_wc_conflict_version_t *location,
svn_boolean_t allow_NULL,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_skel_t *loc;
SVN_ERR_ASSERT(location || allow_NULL);
if (!location)
{
svn_skel__prepend(svn_skel__make_empty_list(result_pool), skel);
return SVN_NO_ERROR;
}
/* ("subversion" repos_root_url repos_uuid repos_relpath rev kind) */
loc = svn_skel__make_empty_list(result_pool);
svn_skel__prepend_str(svn_node_kind_to_word(location->node_kind),
loc, result_pool);
svn_skel__prepend_int(location->peg_rev, loc, result_pool);
svn_skel__prepend_str(apr_pstrdup(result_pool, location->path_in_repos), loc,
result_pool);
if (!location->repos_uuid) /* Can theoretically be NULL */
svn_skel__prepend(svn_skel__make_empty_list(result_pool), loc);
else
svn_skel__prepend_str(location->repos_uuid, loc, result_pool);
svn_skel__prepend_str(apr_pstrdup(result_pool, location->repos_url), loc,
result_pool);
svn_skel__prepend_str(SVN_WC__CONFLICT_SRC_SUBVERSION, loc, result_pool);
svn_skel__prepend(loc, skel);
return SVN_NO_ERROR;
}
/* Deserialize a svn_wc_conflict_version_t from the skel.
Set *LOCATION to NULL when the data is not a svn_wc_conflict_version_t. */
static svn_error_t *
conflict__read_location(svn_wc_conflict_version_t **location,
const svn_skel_t *skel,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *repos_root_url;
const char *repos_uuid;
const char *repos_relpath;
svn_revnum_t revision;
apr_int64_t v;
svn_node_kind_t node_kind; /* note that 'none' is a legitimate value */
const char *kind_str;
const svn_skel_t *c = skel->children;
if (!svn_skel__matches_atom(c, SVN_WC__CONFLICT_SRC_SUBVERSION))
{
*location = NULL;
return SVN_NO_ERROR;
}
c = c->next;
repos_root_url = apr_pstrmemdup(result_pool, c->data, c->len);
c = c->next;
if (c->is_atom)
repos_uuid = apr_pstrmemdup(result_pool, c->data, c->len);
else
repos_uuid = NULL;
c = c->next;
repos_relpath = apr_pstrmemdup(result_pool, c->data, c->len);
c = c->next;
SVN_ERR(svn_skel__parse_int(&v, c, scratch_pool));
revision = (svn_revnum_t)v;
c = c->next;
kind_str = apr_pstrmemdup(scratch_pool, c->data, c->len);
node_kind = svn_node_kind_from_word(kind_str);
*location = svn_wc_conflict_version_create2(repos_root_url,
repos_uuid,
repos_relpath,
revision,
node_kind,
result_pool);
return SVN_NO_ERROR;
}
/* Get the operation part of CONFLICT_SKELL or NULL if no operation is set
at this time */
static svn_error_t *
conflict__get_operation(svn_skel_t **why,
const svn_skel_t *conflict_skel)
{
SVN_ERR_ASSERT(conflict_skel
&& conflict_skel->children
&& conflict_skel->children->next
&& !conflict_skel->children->next->is_atom);
*why = conflict_skel->children;
if (!(*why)->children)
*why = NULL; /* Operation is not set yet */
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__conflict_skel_set_op_update(svn_skel_t *conflict_skel,
const svn_wc_conflict_version_t *original,
const svn_wc_conflict_version_t *target,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_skel_t *why;
svn_skel_t *origins;
SVN_ERR_ASSERT(conflict_skel
&& conflict_skel->children
&& conflict_skel->children->next
&& !conflict_skel->children->next->is_atom);
SVN_ERR(conflict__get_operation(&why, conflict_skel));
SVN_ERR_ASSERT(why == NULL); /* No operation set */
why = conflict_skel->children;
origins = svn_skel__make_empty_list(result_pool);
SVN_ERR(conflict__prepend_location(origins, target, TRUE,
result_pool, scratch_pool));
SVN_ERR(conflict__prepend_location(origins, original, TRUE,
result_pool, scratch_pool));
svn_skel__prepend(origins, why);
svn_skel__prepend_str(SVN_WC__CONFLICT_OP_UPDATE, why, result_pool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__conflict_skel_set_op_switch(svn_skel_t *conflict_skel,
const svn_wc_conflict_version_t *original,
const svn_wc_conflict_version_t *target,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_skel_t *why;
svn_skel_t *origins;
SVN_ERR_ASSERT(conflict_skel
&& conflict_skel->children
&& conflict_skel->children->next
&& !conflict_skel->children->next->is_atom);
SVN_ERR(conflict__get_operation(&why, conflict_skel));
SVN_ERR_ASSERT(why == NULL); /* No operation set */
why = conflict_skel->children;
origins = svn_skel__make_empty_list(result_pool);
SVN_ERR(conflict__prepend_location(origins, target, TRUE,
result_pool, scratch_pool));
SVN_ERR(conflict__prepend_location(origins, original, TRUE,
result_pool, scratch_pool));
svn_skel__prepend(origins, why);
svn_skel__prepend_str(SVN_WC__CONFLICT_OP_SWITCH, why, result_pool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__conflict_skel_set_op_merge(svn_skel_t *conflict_skel,
const svn_wc_conflict_version_t *left,
const svn_wc_conflict_version_t *right,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_skel_t *why;
svn_skel_t *origins;
SVN_ERR_ASSERT(conflict_skel
&& conflict_skel->children
&& conflict_skel->children->next
&& !conflict_skel->children->next->is_atom);
SVN_ERR(conflict__get_operation(&why, conflict_skel));
SVN_ERR_ASSERT(why == NULL); /* No operation set */
why = conflict_skel->children;
origins = svn_skel__make_empty_list(result_pool);
SVN_ERR(conflict__prepend_location(origins, right, TRUE,
result_pool, scratch_pool));
SVN_ERR(conflict__prepend_location(origins, left, TRUE,
result_pool, scratch_pool));
svn_skel__prepend(origins, why);
svn_skel__prepend_str(SVN_WC__CONFLICT_OP_MERGE, why, result_pool);
return SVN_NO_ERROR;
}
/* Gets the conflict data of the specified type CONFLICT_TYPE from
CONFLICT_SKEL, or NULL if no such conflict is recorded */
static svn_error_t *
conflict__get_conflict(svn_skel_t **conflict,
const svn_skel_t *conflict_skel,
const char *conflict_type)
{
svn_skel_t *c;
SVN_ERR_ASSERT(conflict_skel
&& conflict_skel->children
&& conflict_skel->children->next
&& !conflict_skel->children->next->is_atom);
for(c = conflict_skel->children->next->children;
c;
c = c->next)
{
if (svn_skel__matches_atom(c->children, conflict_type))
{
*conflict = c;
return SVN_NO_ERROR;
}
}
*conflict = NULL;
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__conflict_skel_add_text_conflict(svn_skel_t *conflict_skel,
svn_wc__db_t *db,
const char *wri_abspath,
const char *mine_abspath,
const char *their_old_abspath,
const char *their_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_skel_t *text_conflict;
svn_skel_t *markers;
SVN_ERR(conflict__get_conflict(&text_conflict, conflict_skel,
SVN_WC__CONFLICT_KIND_TEXT));
SVN_ERR_ASSERT(!text_conflict); /* ### Use proper error? */
/* Current skel format
("text"
(OLD MINE OLD-THEIRS THEIRS)) */
text_conflict = svn_skel__make_empty_list(result_pool);
markers = svn_skel__make_empty_list(result_pool);
if (their_abspath)
{
const char *their_relpath;
SVN_ERR(svn_wc__db_to_relpath(&their_relpath,
db, wri_abspath, their_abspath,
result_pool, scratch_pool));
svn_skel__prepend_str(their_relpath, markers, result_pool);
}
else
svn_skel__prepend(svn_skel__make_empty_list(result_pool), markers);
if (mine_abspath)
{
const char *mine_relpath;
SVN_ERR(svn_wc__db_to_relpath(&mine_relpath,
db, wri_abspath, mine_abspath,
result_pool, scratch_pool));
svn_skel__prepend_str(mine_relpath, markers, result_pool);
}
else
svn_skel__prepend(svn_skel__make_empty_list(result_pool), markers);
if (their_old_abspath)
{
const char *original_relpath;
SVN_ERR(svn_wc__db_to_relpath(&original_relpath,
db, wri_abspath, their_old_abspath,
result_pool, scratch_pool));
svn_skel__prepend_str(original_relpath, markers, result_pool);
}
else
svn_skel__prepend(svn_skel__make_empty_list(result_pool), markers);
svn_skel__prepend(markers, text_conflict);
svn_skel__prepend_str(SVN_WC__CONFLICT_KIND_TEXT, text_conflict,
result_pool);
/* And add it to the conflict skel */
svn_skel__prepend(text_conflict, conflict_skel->children->next);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__conflict_skel_add_prop_conflict(svn_skel_t *conflict_skel,
svn_wc__db_t *db,
const char *wri_abspath,
const char *marker_abspath,
const apr_hash_t *mine_props,
const apr_hash_t *their_old_props,
const apr_hash_t *their_props,
const apr_hash_t *conflicted_prop_names,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_skel_t *prop_conflict;
svn_skel_t *props;
svn_skel_t *conflict_names;
svn_skel_t *markers;
apr_hash_index_t *hi;
SVN_ERR(conflict__get_conflict(&prop_conflict, conflict_skel,
SVN_WC__CONFLICT_KIND_PROP));
SVN_ERR_ASSERT(!prop_conflict); /* ### Use proper error? */
/* This function currently implements:
("prop"
("marker_relpath")
prop-conflicted_prop_names
old-props
mine-props
their-props)
NULL lists are recorded as "" */
/* ### Seems that this may not match what we read out. Read-out of
* 'theirs-old' comes as NULL. */
prop_conflict = svn_skel__make_empty_list(result_pool);
if (their_props)
{
SVN_ERR(svn_skel__unparse_proplist(&props, their_props, result_pool));
svn_skel__prepend(props, prop_conflict);
}
else
svn_skel__prepend_str("", prop_conflict, result_pool); /* No their_props */
if (mine_props)
{
SVN_ERR(svn_skel__unparse_proplist(&props, mine_props, result_pool));
svn_skel__prepend(props, prop_conflict);
}
else
svn_skel__prepend_str("", prop_conflict, result_pool); /* No mine_props */
if (their_old_props)
{
SVN_ERR(svn_skel__unparse_proplist(&props, their_old_props,
result_pool));
svn_skel__prepend(props, prop_conflict);
}
else
svn_skel__prepend_str("", prop_conflict, result_pool); /* No old_props */
conflict_names = svn_skel__make_empty_list(result_pool);
for (hi = apr_hash_first(scratch_pool, (apr_hash_t *)conflicted_prop_names);
hi;
hi = apr_hash_next(hi))
{
svn_skel__prepend_str(apr_pstrdup(result_pool,
svn__apr_hash_index_key(hi)),
conflict_names,
result_pool);
}
svn_skel__prepend(conflict_names, prop_conflict);
markers = svn_skel__make_empty_list(result_pool);
if (marker_abspath)
{
const char *marker_relpath;
SVN_ERR(svn_wc__db_to_relpath(&marker_relpath, db, wri_abspath,
marker_abspath,
result_pool, scratch_pool));
svn_skel__prepend_str(marker_relpath, markers, result_pool);
}
/*else // ### set via svn_wc__conflict_create_markers
svn_skel__prepend(svn_skel__make_empty_list(result_pool), markers);*/
svn_skel__prepend(markers, prop_conflict);
svn_skel__prepend_str(SVN_WC__CONFLICT_KIND_PROP, prop_conflict, result_pool);
/* And add it to the conflict skel */
svn_skel__prepend(prop_conflict, conflict_skel->children->next);
return SVN_NO_ERROR;
}
/* A map for svn_wc_conflict_reason_t values. */
static const svn_token_map_t local_change_map[] =
{
{ "edited", svn_wc_conflict_reason_edited },
{ "obstructed", svn_wc_conflict_reason_obstructed },
{ "deleted", svn_wc_conflict_reason_deleted },
{ "missing", svn_wc_conflict_reason_missing },
{ "unversioned", svn_wc_conflict_reason_unversioned },
{ "added", svn_wc_conflict_reason_added },
{ "replaced", svn_wc_conflict_reason_replaced },
{ "moved-away", svn_wc_conflict_reason_moved_away },
{ "moved-here", svn_wc_conflict_reason_moved_here },
{ NULL }
};
static const svn_token_map_t incoming_change_map[] =
{
{ "edited", svn_wc_conflict_action_edit },
{ "added", svn_wc_conflict_action_add },
{ "deleted", svn_wc_conflict_action_delete },
{ "replaced", svn_wc_conflict_action_replace },
{ NULL }
};
svn_error_t *
svn_wc__conflict_skel_add_tree_conflict(svn_skel_t *conflict_skel,
svn_wc__db_t *db,
const char *wri_abspath,
svn_wc_conflict_reason_t local_change,
svn_wc_conflict_action_t incoming_change,
const char *move_src_op_root_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_skel_t *tree_conflict;
svn_skel_t *markers;
SVN_ERR(conflict__get_conflict(&tree_conflict, conflict_skel,
SVN_WC__CONFLICT_KIND_TREE));
SVN_ERR_ASSERT(!tree_conflict); /* ### Use proper error? */
SVN_ERR_ASSERT(local_change == svn_wc_conflict_reason_moved_away
|| !move_src_op_root_abspath); /* ### Use proper error? */
tree_conflict = svn_skel__make_empty_list(result_pool);
if (local_change == svn_wc_conflict_reason_moved_away
&& move_src_op_root_abspath)
{
const char *move_src_op_root_relpath;
SVN_ERR(svn_wc__db_to_relpath(&move_src_op_root_relpath,
db, wri_abspath,
move_src_op_root_abspath,
result_pool, scratch_pool));
svn_skel__prepend_str(move_src_op_root_relpath, tree_conflict,
result_pool);
}
svn_skel__prepend_str(
svn_token__to_word(incoming_change_map, incoming_change),
tree_conflict, result_pool);
svn_skel__prepend_str(
svn_token__to_word(local_change_map, local_change),
tree_conflict, result_pool);
/* Tree conflicts have no marker files */
markers = svn_skel__make_empty_list(result_pool);
svn_skel__prepend(markers, tree_conflict);
svn_skel__prepend_str(SVN_WC__CONFLICT_KIND_TREE, tree_conflict,
result_pool);
/* And add it to the conflict skel */
svn_skel__prepend(tree_conflict, conflict_skel->children->next);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__conflict_skel_resolve(svn_boolean_t *completely_resolved,
svn_skel_t *conflict_skel,
svn_wc__db_t *db,
const char *wri_abspath,
svn_boolean_t resolve_text,
const char *resolve_prop,
svn_boolean_t resolve_tree,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_skel_t *op;
svn_skel_t **pconflict;
SVN_ERR(conflict__get_operation(&op, conflict_skel));
if (!op)
return svn_error_create(SVN_ERR_INCOMPLETE_DATA, NULL,
_("Not a completed conflict skel"));
/* We are going to drop items from a linked list. Instead of keeping
a pointer to the item we want to drop we store a pointer to the
pointer of what we may drop, to allow setting it to the next item. */
pconflict = &(conflict_skel->children->next->children);
while (*pconflict)
{
svn_skel_t *c = (*pconflict)->children;
if (resolve_text
&& svn_skel__matches_atom(c, SVN_WC__CONFLICT_KIND_TEXT))
{
/* Remove the text conflict from the linked list */
*pconflict = (*pconflict)->next;
continue;
}
else if (resolve_prop
&& svn_skel__matches_atom(c, SVN_WC__CONFLICT_KIND_PROP))
{
svn_skel_t **ppropnames = &(c->next->next->children);
if (resolve_prop[0] == '\0')
*ppropnames = NULL; /* remove all conflicted property names */
else
while (*ppropnames)
{
if (svn_skel__matches_atom(*ppropnames, resolve_prop))
{
*ppropnames = (*ppropnames)->next;
break;
}
ppropnames = &((*ppropnames)->next);
}
/* If no conflicted property names left */
if (!c->next->next->children)
{
/* Remove the propery conflict skel from the linked list */
*pconflict = (*pconflict)->next;
continue;
}
}
else if (resolve_tree
&& svn_skel__matches_atom(c, SVN_WC__CONFLICT_KIND_TREE))
{
/* Remove the tree conflict from the linked list */
*pconflict = (*pconflict)->next;
continue;
}
pconflict = &((*pconflict)->next);
}
if (completely_resolved)
{
/* Nice, we can just call the complete function */
svn_boolean_t complete_conflict;
SVN_ERR(svn_wc__conflict_skel_is_complete(&complete_conflict,
conflict_skel));
*completely_resolved = !complete_conflict;
}
return SVN_NO_ERROR;
}
/* A map for svn_wc_operation_t values. */
static const svn_token_map_t operation_map[] =
{
{ "", svn_wc_operation_none },
{ SVN_WC__CONFLICT_OP_UPDATE, svn_wc_operation_update },
{ SVN_WC__CONFLICT_OP_SWITCH, svn_wc_operation_switch },
{ SVN_WC__CONFLICT_OP_MERGE, svn_wc_operation_merge },
{ NULL }
};
svn_error_t *
svn_wc__conflict_read_info(svn_wc_operation_t *operation,
const apr_array_header_t **locations,
svn_boolean_t *text_conflicted,
svn_boolean_t *prop_conflicted,
svn_boolean_t *tree_conflicted,
svn_wc__db_t *db,
const char *wri_abspath,
const svn_skel_t *conflict_skel,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_skel_t *op;
const svn_skel_t *c;
SVN_ERR(conflict__get_operation(&op, conflict_skel));
if (!op)
return svn_error_create(SVN_ERR_INCOMPLETE_DATA, NULL,
_("Not a completed conflict skel"));
c = op->children;
if (operation)
{
int value = svn_token__from_mem(operation_map, c->data, c->len);
if (value != SVN_TOKEN_UNKNOWN)
*operation = value;
else
*operation = svn_wc_operation_none;
}
c = c->next;
if (locations && c->children)
{
const svn_skel_t *loc_skel;
svn_wc_conflict_version_t *loc;
apr_array_header_t *locs = apr_array_make(result_pool, 2, sizeof(loc));
for (loc_skel = c->children; loc_skel; loc_skel = loc_skel->next)
{
SVN_ERR(conflict__read_location(&loc, loc_skel, result_pool,
scratch_pool));
APR_ARRAY_PUSH(locs, svn_wc_conflict_version_t *) = loc;
}
*locations = locs;
}
else if (locations)
*locations = NULL;
if (text_conflicted)
{
svn_skel_t *c_skel;
SVN_ERR(conflict__get_conflict(&c_skel, conflict_skel,
SVN_WC__CONFLICT_KIND_TEXT));
*text_conflicted = (c_skel != NULL);
}
if (prop_conflicted)
{
svn_skel_t *c_skel;
SVN_ERR(conflict__get_conflict(&c_skel, conflict_skel,
SVN_WC__CONFLICT_KIND_PROP));
*prop_conflicted = (c_skel != NULL);
}
if (tree_conflicted)
{
svn_skel_t *c_skel;
SVN_ERR(conflict__get_conflict(&c_skel, conflict_skel,
SVN_WC__CONFLICT_KIND_TREE));
*tree_conflicted = (c_skel != NULL);
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__conflict_read_text_conflict(const char **mine_abspath,
const char **their_old_abspath,
const char **their_abspath,
svn_wc__db_t *db,
const char *wri_abspath,
const svn_skel_t *conflict_skel,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_skel_t *text_conflict;
const svn_skel_t *m;
SVN_ERR(conflict__get_conflict(&text_conflict, conflict_skel,
SVN_WC__CONFLICT_KIND_TEXT));
if (!text_conflict)
return svn_error_create(SVN_ERR_WC_MISSING, NULL, _("Conflict not set"));
m = text_conflict->children->next->children;
if (their_old_abspath)
{
if (m->is_atom)
{
const char *original_relpath;
original_relpath = apr_pstrmemdup(scratch_pool, m->data, m->len);
SVN_ERR(svn_wc__db_from_relpath(their_old_abspath,
db, wri_abspath, original_relpath,
result_pool, scratch_pool));
}
else
*their_old_abspath = NULL;
}
m = m->next;
if (mine_abspath)
{
if (m->is_atom)
{
const char *mine_relpath;
mine_relpath = apr_pstrmemdup(scratch_pool, m->data, m->len);
SVN_ERR(svn_wc__db_from_relpath(mine_abspath,
db, wri_abspath, mine_relpath,
result_pool, scratch_pool));
}
else
*mine_abspath = NULL;
}
m = m->next;
if (their_abspath)
{
if (m->is_atom)
{
const char *their_relpath;
their_relpath = apr_pstrmemdup(scratch_pool, m->data, m->len);
SVN_ERR(svn_wc__db_from_relpath(their_abspath,
db, wri_abspath, their_relpath,
result_pool, scratch_pool));
}
else
*their_abspath = NULL;
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__conflict_read_prop_conflict(const char **marker_abspath,
apr_hash_t **mine_props,
apr_hash_t **their_old_props,
apr_hash_t **their_props,
apr_hash_t **conflicted_prop_names,
svn_wc__db_t *db,
const char *wri_abspath,
const svn_skel_t *conflict_skel,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_skel_t *prop_conflict;
const svn_skel_t *c;
SVN_ERR(conflict__get_conflict(&prop_conflict, conflict_skel,
SVN_WC__CONFLICT_KIND_PROP));
if (!prop_conflict)
return svn_error_create(SVN_ERR_WC_MISSING, NULL, _("Conflict not set"));
c = prop_conflict->children;
c = c->next; /* Skip "prop" */
/* Get marker file */
if (marker_abspath)
{
const char *marker_relpath;
if (c->children && c->children->is_atom)
{
marker_relpath = apr_pstrmemdup(result_pool, c->children->data,
c->children->len);
SVN_ERR(svn_wc__db_from_relpath(marker_abspath, db, wri_abspath,
marker_relpath,
result_pool, scratch_pool));
}
else
*marker_abspath = NULL;
}
c = c->next;
/* Get conflicted properties */
if (conflicted_prop_names)
{
const svn_skel_t *name;
*conflicted_prop_names = apr_hash_make(result_pool);
for (name = c->children; name; name = name->next)
{
svn_hash_sets(*conflicted_prop_names,
apr_pstrmemdup(result_pool, name->data, name->len),
"");
}
}
c = c->next;
/* Get original properties */
if (their_old_props)
{
if (c->is_atom)
*their_old_props = apr_hash_make(result_pool);
else
SVN_ERR(svn_skel__parse_proplist(their_old_props, c, result_pool));
}
c = c->next;
/* Get mine properties */
if (mine_props)
{
if (c->is_atom)
*mine_props = apr_hash_make(result_pool);
else
SVN_ERR(svn_skel__parse_proplist(mine_props, c, result_pool));
}
c = c->next;
/* Get their properties */
if (their_props)
{
if (c->is_atom)
*their_props = apr_hash_make(result_pool);
else
SVN_ERR(svn_skel__parse_proplist(their_props, c, result_pool));
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__conflict_read_tree_conflict(svn_wc_conflict_reason_t *local_change,
svn_wc_conflict_action_t *incoming_change,
const char **move_src_op_root_abspath,
svn_wc__db_t *db,
const char *wri_abspath,
const svn_skel_t *conflict_skel,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_skel_t *tree_conflict;
const svn_skel_t *c;
svn_boolean_t is_moved_away = FALSE;
SVN_ERR(conflict__get_conflict(&tree_conflict, conflict_skel,
SVN_WC__CONFLICT_KIND_TREE));
if (!tree_conflict)
return svn_error_create(SVN_ERR_WC_MISSING, NULL, _("Conflict not set"));
c = tree_conflict->children;
c = c->next; /* Skip "tree" */
c = c->next; /* Skip markers */
{
int value = svn_token__from_mem(local_change_map, c->data, c->len);
if (local_change)
{
if (value != SVN_TOKEN_UNKNOWN)
*local_change = value;
else
*local_change = svn_wc_conflict_reason_edited;
}
is_moved_away = (value == svn_wc_conflict_reason_moved_away);
}
c = c->next;
if (incoming_change)
{
int value = svn_token__from_mem(incoming_change_map, c->data, c->len);
if (value != SVN_TOKEN_UNKNOWN)
*incoming_change = value;
else
*incoming_change = svn_wc_conflict_action_edit;
}
c = c->next;
if (move_src_op_root_abspath)
{
/* Only set for update and switch tree conflicts */
if (c && is_moved_away)
{
const char *move_src_op_root_relpath
= apr_pstrmemdup(scratch_pool, c->data, c->len);
SVN_ERR(svn_wc__db_from_relpath(move_src_op_root_abspath,
db, wri_abspath,
move_src_op_root_relpath,
result_pool, scratch_pool));
}
else
*move_src_op_root_abspath = NULL;
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__conflict_read_markers(const apr_array_header_t **markers,
svn_wc__db_t *db,
const char *wri_abspath,
const svn_skel_t *conflict_skel,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const svn_skel_t *conflict;
apr_array_header_t *list = NULL;
SVN_ERR_ASSERT(conflict_skel != NULL);
/* Walk the conflicts */
for (conflict = conflict_skel->children->next->children;
conflict;
conflict = conflict->next)
{
const svn_skel_t *marker;
/* Get the list of markers stored per conflict */
for (marker = conflict->children->next->children;
marker;
marker = marker->next)
{
/* Skip placeholders */
if (! marker->is_atom)
continue;
if (! list)
list = apr_array_make(result_pool, 4, sizeof(const char *));
SVN_ERR(svn_wc__db_from_relpath(
&APR_ARRAY_PUSH(list, const char*),
db, wri_abspath,
apr_pstrmemdup(scratch_pool, marker->data,
marker->len),
result_pool, scratch_pool));
}
}
*markers = list;
return SVN_NO_ERROR;
}
/* --------------------------------------------------------------------
*/
/* Helper for svn_wc__conflict_create_markers */
static svn_skel_t *
prop_conflict_skel_new(apr_pool_t *result_pool)
{
svn_skel_t *operation = svn_skel__make_empty_list(result_pool);
svn_skel_t *result = svn_skel__make_empty_list(result_pool);
svn_skel__prepend(operation, result);
return result;
}
/* Helper for prop_conflict_skel_add */
static void
prepend_prop_value(const svn_string_t *value,
svn_skel_t *skel,
apr_pool_t *result_pool)
{
svn_skel_t *value_skel = svn_skel__make_empty_list(result_pool);
if (value != NULL)
{
const void *dup = apr_pmemdup(result_pool, value->data, value->len);
svn_skel__prepend(svn_skel__mem_atom(dup, value->len, result_pool),
value_skel);
}
svn_skel__prepend(value_skel, skel);
}
/* Helper for svn_wc__conflict_create_markers */
static svn_error_t *
prop_conflict_skel_add(
svn_skel_t *skel,
const char *prop_name,
const svn_string_t *original_value,
const svn_string_t *mine_value,
const svn_string_t *incoming_value,
const svn_string_t *incoming_base_value,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_skel_t *prop_skel = svn_skel__make_empty_list(result_pool);
/* ### check that OPERATION has been filled in. */
/* See notes/wc-ng/conflict-storage */
prepend_prop_value(incoming_base_value, prop_skel, result_pool);
prepend_prop_value(incoming_value, prop_skel, result_pool);
prepend_prop_value(mine_value, prop_skel, result_pool);
prepend_prop_value(original_value, prop_skel, result_pool);
svn_skel__prepend_str(apr_pstrdup(result_pool, prop_name), prop_skel,
result_pool);
svn_skel__prepend_str(SVN_WC__CONFLICT_KIND_PROP, prop_skel, result_pool);
/* Now we append PROP_SKEL to the end of the provided conflict SKEL. */
svn_skel__append(skel, prop_skel);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__conflict_create_markers(svn_skel_t **work_items,
svn_wc__db_t *db,
const char *local_abspath,
svn_skel_t *conflict_skel,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_boolean_t prop_conflicted;
svn_wc_operation_t operation;
*work_items = NULL;
SVN_ERR(svn_wc__conflict_read_info(&operation, NULL,
NULL, &prop_conflicted, NULL,
db, local_abspath,
conflict_skel,
scratch_pool, scratch_pool));
if (prop_conflicted)
{
const char *marker_abspath = NULL;
svn_node_kind_t kind;
const char *marker_dir;
const char *marker_name;
const char *marker_relpath;
/* Ok, currently we have to do a few things for property conflicts:
- Create a marker file
- Create a WQ item that sets the marker name
- Create a WQ item that fills the marker with the expected data
This can be simplified once we really store conflict_skel in wc.db */
SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool));
if (kind == svn_node_dir)
{
marker_dir = local_abspath;
marker_name = SVN_WC__THIS_DIR_PREJ;
}
else
svn_dirent_split(&marker_dir, &marker_name, local_abspath,
scratch_pool);
SVN_ERR(svn_io_open_uniquely_named(NULL, &marker_abspath,
marker_dir,
marker_name,
SVN_WC__PROP_REJ_EXT,
svn_io_file_del_none,
scratch_pool, scratch_pool));
SVN_ERR(svn_wc__db_to_relpath(&marker_relpath, db, local_abspath,
marker_abspath, result_pool, result_pool));
/* And store the marker in the skel */
{
svn_skel_t *prop_conflict;
SVN_ERR(conflict__get_conflict(&prop_conflict, conflict_skel,
SVN_WC__CONFLICT_KIND_PROP));
svn_skel__prepend_str(marker_relpath, prop_conflict->children->next,
result_pool);
}
/* Store the data in the WQ item in the same format used as 1.7.
Once we store the data in DB it is easier to just read it back
from the workqueue */
{
svn_skel_t *prop_data;
apr_hash_index_t *hi;
apr_hash_t *old_props;
apr_hash_t *mine_props;
apr_hash_t *their_original_props;
apr_hash_t *their_props;
apr_hash_t *conflicted_props;
SVN_ERR(svn_wc__conflict_read_prop_conflict(NULL,
&mine_props,
&their_original_props,
&their_props,
&conflicted_props,
db, local_abspath,
conflict_skel,
scratch_pool,
scratch_pool));
if (operation == svn_wc_operation_merge)
SVN_ERR(svn_wc__db_read_pristine_props(&old_props, db, local_abspath,
scratch_pool, scratch_pool));
else
old_props = their_original_props;
prop_data = prop_conflict_skel_new(result_pool);
for (hi = apr_hash_first(scratch_pool, conflicted_props);
hi;
hi = apr_hash_next(hi))
{
const char *propname = svn__apr_hash_index_key(hi);
SVN_ERR(prop_conflict_skel_add(
prop_data, propname,
old_props
? svn_hash_gets(old_props, propname)
: NULL,
mine_props
? svn_hash_gets(mine_props, propname)
: NULL,
their_props
? svn_hash_gets(their_props, propname)
: NULL,
their_original_props
? svn_hash_gets(their_original_props, propname)
: NULL,
result_pool, scratch_pool));
}
SVN_ERR(svn_wc__wq_build_prej_install(work_items,
db, local_abspath,
prop_data,
scratch_pool, scratch_pool));
}
}
return SVN_NO_ERROR;
}
/* Helper function for the three apply_* functions below, used when
* merging properties together.
*
* Given property PROPNAME on LOCAL_ABSPATH, and four possible property
* values, generate four tmpfiles and pass them to CONFLICT_FUNC callback.
* This gives the client an opportunity to interactively resolve the
* property conflict.
*
* BASE_VAL/WORKING_VAL represent the current state of the working
* copy, and INCOMING_OLD_VAL/INCOMING_NEW_VAL represents the incoming
* propchange. Any of these values might be NULL, indicating either
* non-existence or intent-to-delete.
*
* If the callback isn't available, or if it responds with
* 'choose_postpone', then set *CONFLICT_REMAINS to TRUE and return.
*
* If the callback responds with a choice of 'base', 'theirs', 'mine',
* or 'merged', then install the proper value into ACTUAL_PROPS and
* set *CONFLICT_REMAINS to FALSE.
*/
static svn_error_t *
generate_propconflict(svn_boolean_t *conflict_remains,
svn_wc__db_t *db,
const char *local_abspath,
svn_wc_operation_t operation,
const svn_wc_conflict_version_t *left_version,
const svn_wc_conflict_version_t *right_version,
const char *propname,
const svn_string_t *base_val,
const svn_string_t *working_val,
const svn_string_t *incoming_old_val,
const svn_string_t *incoming_new_val,
svn_wc_conflict_resolver_func2_t conflict_func,
void *conflict_baton,
apr_pool_t *scratch_pool)
{
svn_wc_conflict_result_t *result = NULL;
svn_wc_conflict_description2_t *cdesc;
const char *dirpath = svn_dirent_dirname(local_abspath, scratch_pool);
svn_node_kind_t kind;
const svn_string_t *new_value = NULL;
SVN_ERR(svn_wc__db_read_kind(&kind, db, local_abspath,
FALSE /* allow_missing */,
FALSE /* show_deleted */,
FALSE /* show_hidden */,
scratch_pool));
if (kind == svn_node_none)
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("The node '%s' was not found."),
svn_dirent_local_style(local_abspath,
scratch_pool));
cdesc = svn_wc_conflict_description_create_prop2(
local_abspath,
(kind == svn_node_dir) ? svn_node_dir : svn_node_file,
propname, scratch_pool);
cdesc->operation = operation;
cdesc->src_left_version = left_version;
cdesc->src_right_version = right_version;
/* Create a tmpfile for each of the string_t's we've got. */
if (working_val)
{
const char *file_name;
SVN_ERR(svn_io_write_unique(&file_name, dirpath, working_val->data,
working_val->len,
svn_io_file_del_on_pool_cleanup,
scratch_pool));
cdesc->my_abspath = svn_dirent_join(dirpath, file_name, scratch_pool);
}
if (incoming_new_val)
{
const char *file_name;
SVN_ERR(svn_io_write_unique(&file_name, dirpath, incoming_new_val->data,
incoming_new_val->len,
svn_io_file_del_on_pool_cleanup,
scratch_pool));
cdesc->their_abspath = svn_dirent_join(dirpath, file_name, scratch_pool);
}
if (!base_val && !incoming_old_val)
{
/* If base and old are both NULL, then that's fine, we just let
base_file stay NULL as-is. Both agents are attempting to add a
new property. */
}
else if ((base_val && !incoming_old_val)
|| (!base_val && incoming_old_val))
{
/* If only one of base and old are defined, then we've got a
situation where one agent is attempting to add the property
for the first time, and the other agent is changing a
property it thinks already exists. In this case, we return
whichever older-value happens to be defined, so that the
conflict-callback can still attempt a 3-way merge. */
const svn_string_t *conflict_base_val = base_val ? base_val
: incoming_old_val;
const char *file_name;
SVN_ERR(svn_io_write_unique(&file_name, dirpath,
conflict_base_val->data,
conflict_base_val->len,
svn_io_file_del_on_pool_cleanup,
scratch_pool));
cdesc->base_abspath = svn_dirent_join(dirpath, file_name, scratch_pool);
}
else /* base and old are both non-NULL */
{
const svn_string_t *conflict_base_val;
const char *file_name;
if (! svn_string_compare(base_val, incoming_old_val))
{
/* What happens if 'base' and 'old' don't match up? In an
ideal situation, they would. But if they don't, this is
a classic example of a patch 'hunk' failing to apply due
to a lack of context. For example: imagine that the user
is busy changing the property from a value of "cat" to
"dog", but the incoming propchange wants to change the
same property value from "red" to "green". Total context
mismatch.
HOWEVER: we can still pass one of the two base values as
'base_file' to the callback anyway. It's still useful to
present the working and new values to the user to
compare. */
if (working_val && svn_string_compare(base_val, working_val))
conflict_base_val = incoming_old_val;
else
conflict_base_val = base_val;
}
else
{
conflict_base_val = base_val;
}
SVN_ERR(svn_io_write_unique(&file_name, dirpath, conflict_base_val->data,
conflict_base_val->len,
svn_io_file_del_on_pool_cleanup, scratch_pool));
cdesc->base_abspath = svn_dirent_join(dirpath, file_name, scratch_pool);
if (working_val && incoming_new_val)
{
svn_stream_t *mergestream;
svn_diff_t *diff;
svn_diff_file_options_t *options =
svn_diff_file_options_create(scratch_pool);
SVN_ERR(svn_stream_open_unique(&mergestream, &cdesc->merged_file,
NULL, svn_io_file_del_on_pool_cleanup,
scratch_pool, scratch_pool));
SVN_ERR(svn_diff_mem_string_diff3(&diff, conflict_base_val,
working_val,
incoming_new_val, options, scratch_pool));
SVN_ERR(svn_diff_mem_string_output_merge2
(mergestream, diff, conflict_base_val, working_val,
incoming_new_val, NULL, NULL, NULL, NULL,
svn_diff_conflict_display_modified_latest, scratch_pool));
SVN_ERR(svn_stream_close(mergestream));
}
}
if (!incoming_old_val && incoming_new_val)
cdesc->action = svn_wc_conflict_action_add;
else if (incoming_old_val && !incoming_new_val)
cdesc->action = svn_wc_conflict_action_delete;
else
cdesc->action = svn_wc_conflict_action_edit;
if (base_val && !working_val)
cdesc->reason = svn_wc_conflict_reason_deleted;
else if (!base_val && working_val)
cdesc->reason = svn_wc_conflict_reason_obstructed;
else
cdesc->reason = svn_wc_conflict_reason_edited;
/* Invoke the interactive conflict callback. */
{
SVN_ERR(conflict_func(&result, cdesc, conflict_baton, scratch_pool,
scratch_pool));
}
if (result == NULL)
{
*conflict_remains = TRUE;
return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
NULL, _("Conflict callback violated API:"
" returned no results"));
}
switch (result->choice)
{
default:
case svn_wc_conflict_choose_postpone:
{
*conflict_remains = TRUE;
break;
}
case svn_wc_conflict_choose_mine_full:
{
/* No need to change actual_props; it already contains working_val */
*conflict_remains = FALSE;
new_value = working_val;
break;
}
/* I think _mine_full and _theirs_full are appropriate for prop
behavior as well as the text behavior. There should even be
analogous behaviors for _mine and _theirs when those are
ready, namely: fold in all non-conflicting prop changes, and
then choose _mine side or _theirs side for conflicting ones. */
case svn_wc_conflict_choose_theirs_full:
{
*conflict_remains = FALSE;
new_value = incoming_new_val;
break;
}
case svn_wc_conflict_choose_base:
{
*conflict_remains = FALSE;
new_value = base_val;
break;
}
case svn_wc_conflict_choose_merged:
{
svn_stringbuf_t *merged_stringbuf;
if (!cdesc->merged_file && !result->merged_file)
return svn_error_create
(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
NULL, _("Conflict callback violated API:"
" returned no merged file"));
SVN_ERR(svn_stringbuf_from_file2(&merged_stringbuf,
result->merged_file ?
result->merged_file :
cdesc->merged_file,
scratch_pool));
new_value = svn_stringbuf__morph_into_string(merged_stringbuf);
*conflict_remains = FALSE;
break;
}
}
if (!*conflict_remains)
{
apr_hash_t *props;
/* For now, just set the property values. This should really do some of the
more advanced things from svn_wc_prop_set() */
SVN_ERR(svn_wc__db_read_props(&props, db, local_abspath, scratch_pool,
scratch_pool));
svn_hash_sets(props, propname, new_value);
SVN_ERR(svn_wc__db_op_set_props(db, local_abspath, props,
FALSE, NULL, NULL,
scratch_pool));
}
return SVN_NO_ERROR;
}
/* Resolve the text conflict on DB/LOCAL_ABSPATH in the manner specified
* by CHOICE.
*
* Set *WORK_ITEMS to new work items that will make the on-disk changes
* needed to complete the resolution (but not to mark it as resolved).
* Set *IS_RESOLVED to true if the conflicts are resolved; otherwise
* (which is only if CHOICE is 'postpone') to false.
*
* LEFT_ABSPATH, RIGHT_ABSPATH, and DETRANSLATED_TARGET are the
* input files to the 3-way merge that will be performed if CHOICE is
* 'theirs-conflict' or 'mine-conflict'. LEFT_ABSPATH is also the file
* that will be used if CHOICE is 'base', and RIGHT_ABSPATH if CHOICE is
* 'theirs-full'. MERGED_ABSPATH will be used if CHOICE is 'merged'.
*
* DETRANSLATED_TARGET is the detranslated version of 'mine' (see
* detranslate_wc_file() above). MERGE_OPTIONS are passed to the
* diff3 implementation in case a 3-way merge has to be carried out.
*/
static svn_error_t *
eval_text_conflict_func_result(svn_skel_t **work_items,
svn_boolean_t *is_resolved,
svn_wc__db_t *db,
const char *local_abspath,
svn_wc_conflict_choice_t choice,
const apr_array_header_t *merge_options,
const char *left_abspath,
const char *right_abspath,
const char *merged_abspath,
const char *detranslated_target,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *install_from_abspath = NULL;
svn_boolean_t remove_source = FALSE;
*work_items = NULL;
switch (choice)
{
/* If the callback wants to use one of the fulltexts
to resolve the conflict, so be it.*/
case svn_wc_conflict_choose_base:
{
install_from_abspath = left_abspath;
*is_resolved = TRUE;
break;
}
case svn_wc_conflict_choose_theirs_full:
{
install_from_abspath = right_abspath;
*is_resolved = TRUE;
break;
}
case svn_wc_conflict_choose_mine_full:
{
install_from_abspath = detranslated_target;
*is_resolved = TRUE;
break;
}
case svn_wc_conflict_choose_theirs_conflict:
case svn_wc_conflict_choose_mine_conflict:
{
const char *chosen_abspath;
const char *temp_dir;
svn_stream_t *chosen_stream;
svn_diff_t *diff;
svn_diff_conflict_display_style_t style;
svn_diff_file_options_t *diff3_options;
diff3_options = svn_diff_file_options_create(scratch_pool);
if (merge_options)
SVN_ERR(svn_diff_file_options_parse(diff3_options,
merge_options,
scratch_pool));
style = choice == svn_wc_conflict_choose_theirs_conflict
? svn_diff_conflict_display_latest
: svn_diff_conflict_display_modified;
SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir, db,
local_abspath,
scratch_pool, scratch_pool));
SVN_ERR(svn_stream_open_unique(&chosen_stream, &chosen_abspath,
temp_dir, svn_io_file_del_none,
scratch_pool, scratch_pool));
SVN_ERR(svn_diff_file_diff3_2(&diff,
left_abspath,
detranslated_target, right_abspath,
diff3_options, scratch_pool));
SVN_ERR(svn_diff_file_output_merge2(chosen_stream, diff,
left_abspath,
detranslated_target,
right_abspath,
/* markers ignored */
NULL, NULL,
NULL, NULL,
style,
scratch_pool));
SVN_ERR(svn_stream_close(chosen_stream));
install_from_abspath = chosen_abspath;
remove_source = TRUE;
*is_resolved = TRUE;
break;
}
/* For the case of 3-way file merging, we don't
really distinguish between these return values;
if the callback claims to have "generally
resolved" the situation, we still interpret
that as "OK, we'll assume the merged version is
good to use". */
case svn_wc_conflict_choose_merged:
{
install_from_abspath = merged_abspath;
*is_resolved = TRUE;
break;
}
case svn_wc_conflict_choose_postpone:
default:
{
/* Assume conflict remains. */
*is_resolved = FALSE;
return SVN_NO_ERROR;
}
}
- SVN_ERR_ASSERT(install_from_abspath != NULL);
+ if (install_from_abspath == NULL)
+ return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+ _("Conflict on '%s' could not be resolved "
+ "because the chosen version of the file "
+ "is not available."),
+ svn_dirent_local_style(local_abspath,
+ scratch_pool));
{
svn_skel_t *work_item;
SVN_ERR(svn_wc__wq_build_file_install(&work_item,
db, local_abspath,
install_from_abspath,
FALSE /* use_commit_times */,
FALSE /* record_fileinfo */,
result_pool, scratch_pool));
*work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, db, local_abspath,
result_pool, scratch_pool));
*work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
if (remove_source)
{
SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
db, local_abspath,
install_from_abspath,
result_pool, scratch_pool));
*work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
}
}
return SVN_NO_ERROR;
}
/* Create a new file in the same directory as LOCAL_ABSPATH, with the
same basename as LOCAL_ABSPATH, with a ".edited" extension, and set
*WORK_ITEM to a new work item that will copy and translate from the file
SOURCE_ABSPATH to that new file. It will be translated from repository-
normal form to working-copy form according to the versioned properties
of LOCAL_ABSPATH that are current when the work item is executed.
DB should have a write lock for the directory containing SOURCE.
Allocate *WORK_ITEM in RESULT_POOL. */
static svn_error_t *
save_merge_result(svn_skel_t **work_item,
svn_wc__db_t *db,
const char *local_abspath,
const char *source_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *edited_copy_abspath;
const char *dir_abspath;
const char *filename;
svn_dirent_split(&dir_abspath, &filename, local_abspath, scratch_pool);
/* ### Should use preserved-conflict-file-exts. */
/* Create the .edited file within this file's DIR_ABSPATH */
SVN_ERR(svn_io_open_uniquely_named(NULL,
&edited_copy_abspath,
dir_abspath,
filename,
".edited",
svn_io_file_del_none,
scratch_pool, scratch_pool));
SVN_ERR(svn_wc__wq_build_file_copy_translated(work_item,
db, local_abspath,
source_abspath,
edited_copy_abspath,
result_pool, scratch_pool));
return SVN_NO_ERROR;
}
/* Call the conflict resolver callback for a text conflict, and resolve
* the conflict if it tells us to do so.
*
* Assume that there is a text conflict on the path DB/LOCAL_ABSPATH.
*
* Call CONFLICT_FUNC with CONFLICT_BATON to find out whether and how
* it wants to resolve the conflict. Pass it a conflict description
* containing OPERATION, LEFT/RIGHT_ABSPATH, LEFT/RIGHT_VERSION,
* RESULT_TARGET and DETRANSLATED_TARGET.
*
* If the callback returns a resolution other than 'postpone', then
* perform that requested resolution and prepare to mark the conflict
* as resolved.
*
* Return *WORK_ITEMS that will do the on-disk work required to complete
* the resolution (but not to mark the conflict as resolved), and set
* *WAS_RESOLVED to true, if it was resolved. Set *WORK_ITEMS to NULL
* and *WAS_RESOLVED to FALSE otherwise.
*
* RESULT_TARGET is the path to the merged file produced by the internal
* or external 3-way merge, which may contain conflict markers, in
* repository normal form. DETRANSLATED_TARGET is the 'mine' version of
* the file, also in RNF.
*/
static svn_error_t *
resolve_text_conflict(svn_skel_t **work_items,
svn_boolean_t *was_resolved,
svn_wc__db_t *db,
const char *local_abspath,
const apr_array_header_t *merge_options,
svn_wc_operation_t operation,
const char *left_abspath,
const char *right_abspath,
const svn_wc_conflict_version_t *left_version,
const svn_wc_conflict_version_t *right_version,
const char *result_target,
const char *detranslated_target,
svn_wc_conflict_resolver_func2_t conflict_func,
void *conflict_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc_conflict_result_t *result;
svn_skel_t *work_item;
svn_wc_conflict_description2_t *cdesc;
apr_hash_t *props;
+ const char *mime_type;
*work_items = NULL;
*was_resolved = FALSE;
/* Give the conflict resolution callback a chance to clean
up the conflicts before we mark the file 'conflicted' */
SVN_ERR(svn_wc__db_read_props(&props, db, local_abspath,
scratch_pool, scratch_pool));
cdesc = svn_wc_conflict_description_create_text2(local_abspath,
scratch_pool);
- cdesc->is_binary = FALSE;
- cdesc->mime_type = svn_prop_get_value(props, SVN_PROP_MIME_TYPE);
+ mime_type = svn_prop_get_value(props, SVN_PROP_MIME_TYPE);
+ cdesc->is_binary = mime_type ? svn_mime_type_is_binary(mime_type) : FALSE;
+ cdesc->mime_type = mime_type;
cdesc->base_abspath = left_abspath;
cdesc->their_abspath = right_abspath;
cdesc->my_abspath = detranslated_target;
cdesc->merged_file = result_target;
cdesc->operation = operation;
cdesc->src_left_version = left_version;
cdesc->src_right_version = right_version;
SVN_ERR(conflict_func(&result, cdesc, conflict_baton, scratch_pool,
scratch_pool));
if (result == NULL)
return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
_("Conflict callback violated API:"
" returned no results"));
if (result->save_merged)
{
SVN_ERR(save_merge_result(work_items,
db, local_abspath,
/* Look for callback's own
merged-file first: */
result->merged_file
? result->merged_file
: result_target,
result_pool, scratch_pool));
}
if (result->choice != svn_wc_conflict_choose_postpone)
{
SVN_ERR(eval_text_conflict_func_result(&work_item,
was_resolved,
db, local_abspath,
result->choice,
merge_options,
left_abspath,
right_abspath,
/* ### Sure this is an abspath? */
result->merged_file
? result->merged_file
: result_target,
detranslated_target,
result_pool, scratch_pool));
*work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
}
else
*was_resolved = FALSE;
return SVN_NO_ERROR;
}
static svn_error_t *
setup_tree_conflict_desc(svn_wc_conflict_description2_t **desc,
svn_wc__db_t *db,
const char *local_abspath,
svn_wc_operation_t operation,
const svn_wc_conflict_version_t *left_version,
const svn_wc_conflict_version_t *right_version,
svn_wc_conflict_reason_t local_change,
svn_wc_conflict_action_t incoming_change,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_node_kind_t tc_kind;
if (left_version)
tc_kind = left_version->node_kind;
else if (right_version)
tc_kind = right_version->node_kind;
else
tc_kind = svn_node_file; /* Avoid assertion */
*desc = svn_wc_conflict_description_create_tree2(local_abspath, tc_kind,
operation,
left_version, right_version,
result_pool);
(*desc)->reason = local_change;
(*desc)->action = incoming_change;
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__conflict_invoke_resolver(svn_wc__db_t *db,
const char *local_abspath,
const svn_skel_t *conflict_skel,
const apr_array_header_t *merge_options,
svn_wc_conflict_resolver_func2_t resolver_func,
void *resolver_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
svn_boolean_t text_conflicted;
svn_boolean_t prop_conflicted;
svn_boolean_t tree_conflicted;
svn_wc_operation_t operation;
const apr_array_header_t *locations;
const svn_wc_conflict_version_t *left_version = NULL;
const svn_wc_conflict_version_t *right_version = NULL;
SVN_ERR(svn_wc__conflict_read_info(&operation, &locations,
&text_conflicted, &prop_conflicted,
&tree_conflicted,
db, local_abspath, conflict_skel,
scratch_pool, scratch_pool));
if (locations && locations->nelts > 0)
left_version = APR_ARRAY_IDX(locations, 0, const svn_wc_conflict_version_t *);
if (locations && locations->nelts > 1)
right_version = APR_ARRAY_IDX(locations, 1, const svn_wc_conflict_version_t *);
/* Quick and dirty compatibility wrapper. My guess would be that most resolvers
would want to look at all properties at the same time.
### svn currently only invokes this from the merge code to collect the list of
### conflicted paths. Eventually this code will be the base for 'svn resolve'
### and at that time the test coverage will improve
*/
if (prop_conflicted)
{
apr_hash_t *old_props;
apr_hash_t *mine_props;
apr_hash_t *their_props;
apr_hash_t *old_their_props;
apr_hash_t *conflicted;
apr_pool_t *iterpool;
apr_hash_index_t *hi;
svn_boolean_t mark_resolved = TRUE;
SVN_ERR(svn_wc__conflict_read_prop_conflict(NULL,
&mine_props,
&old_their_props,
&their_props,
&conflicted,
db, local_abspath,
conflict_skel,
scratch_pool, scratch_pool));
if (operation == svn_wc_operation_merge)
SVN_ERR(svn_wc__db_read_pristine_props(&old_props, db, local_abspath,
scratch_pool, scratch_pool));
else
old_props = old_their_props;
iterpool = svn_pool_create(scratch_pool);
for (hi = apr_hash_first(scratch_pool, conflicted);
hi;
hi = apr_hash_next(hi))
{
const char *propname = svn__apr_hash_index_key(hi);
svn_boolean_t conflict_remains = TRUE;
svn_pool_clear(iterpool);
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
SVN_ERR(generate_propconflict(&conflict_remains,
db, local_abspath,
operation,
left_version,
right_version,
propname,
old_props
? svn_hash_gets(old_props, propname)
: NULL,
mine_props
? svn_hash_gets(mine_props, propname)
: NULL,
old_their_props
? svn_hash_gets(old_their_props, propname)
: NULL,
their_props
? svn_hash_gets(their_props, propname)
: NULL,
resolver_func, resolver_baton,
iterpool));
if (conflict_remains)
mark_resolved = FALSE;
}
if (mark_resolved)
{
SVN_ERR(svn_wc__mark_resolved_prop_conflicts(db, local_abspath,
scratch_pool));
}
}
if (text_conflicted)
{
const char *mine_abspath;
const char *their_original_abspath;
const char *their_abspath;
svn_skel_t *work_items;
svn_boolean_t was_resolved;
SVN_ERR(svn_wc__conflict_read_text_conflict(&mine_abspath,
&their_original_abspath,
&their_abspath,
db, local_abspath,
conflict_skel,
scratch_pool, scratch_pool));
SVN_ERR(resolve_text_conflict(&work_items, &was_resolved,
db, local_abspath,
merge_options,
operation,
their_original_abspath, their_abspath,
left_version, right_version,
local_abspath, mine_abspath,
resolver_func, resolver_baton,
scratch_pool, scratch_pool));
if (was_resolved)
{
if (work_items)
{
SVN_ERR(svn_wc__db_wq_add(db, local_abspath, work_items,
scratch_pool));
SVN_ERR(svn_wc__wq_run(db, local_abspath,
cancel_func, cancel_baton,
scratch_pool));
}
SVN_ERR(svn_wc__mark_resolved_text_conflict(db, local_abspath,
scratch_pool));
}
}
if (tree_conflicted)
{
svn_wc_conflict_reason_t local_change;
svn_wc_conflict_action_t incoming_change;
svn_wc_conflict_result_t *result;
svn_wc_conflict_description2_t *desc;
SVN_ERR(svn_wc__conflict_read_tree_conflict(&local_change,
&incoming_change,
NULL,
db, local_abspath,
conflict_skel,
scratch_pool, scratch_pool));
SVN_ERR(setup_tree_conflict_desc(&desc,
db, local_abspath,
operation, left_version, right_version,
local_change, incoming_change,
scratch_pool, scratch_pool));
/* Tell the resolver func about this conflict. */
SVN_ERR(resolver_func(&result, desc, resolver_baton, scratch_pool,
scratch_pool));
/* Ignore the result. We cannot apply it here since this code runs
* during an update or merge operation. Tree conflicts are always
* postponed and resolved after the operation has completed. */
}
return SVN_NO_ERROR;
}
/* Read all property conflicts contained in CONFLICT_SKEL into
* individual conflict descriptions, and append those descriptions
* to the CONFLICTS array.
*
* If NOT create_tempfiles, always create a legacy property conflict
* descriptor.
*
* Use NODE_KIND, OPERATION and shallow copies of LEFT_VERSION and
* RIGHT_VERSION, rather than reading them from CONFLICT_SKEL.
*
* Allocate results in RESULT_POOL. SCRATCH_POOL is used for temporary
* allocations. */
static svn_error_t *
read_prop_conflicts(apr_array_header_t *conflicts,
svn_wc__db_t *db,
const char *local_abspath,
svn_skel_t *conflict_skel,
svn_boolean_t create_tempfiles,
svn_node_kind_t node_kind,
svn_wc_operation_t operation,
const svn_wc_conflict_version_t *left_version,
const svn_wc_conflict_version_t *right_version,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *prop_reject_file;
apr_hash_t *my_props;
apr_hash_t *their_old_props;
apr_hash_t *their_props;
apr_hash_t *conflicted_props;
apr_hash_index_t *hi;
apr_pool_t *iterpool;
SVN_ERR(svn_wc__conflict_read_prop_conflict(&prop_reject_file,
&my_props,
&their_old_props,
&their_props,
&conflicted_props,
db, local_abspath,
conflict_skel,
scratch_pool, scratch_pool));
if ((! create_tempfiles) || apr_hash_count(conflicted_props) == 0)
{
/* Legacy prop conflict with only a .reject file. */
svn_wc_conflict_description2_t *desc;
desc = svn_wc_conflict_description_create_prop2(local_abspath,
node_kind,
"", result_pool);
/* ### This should be changed. The prej file should be stored
* ### separately from the other files. We need to rev the
* ### conflict description struct for this. */
desc->their_abspath = apr_pstrdup(result_pool, prop_reject_file);
desc->operation = operation;
desc->src_left_version = left_version;
desc->src_right_version = right_version;
APR_ARRAY_PUSH(conflicts, svn_wc_conflict_description2_t*) = desc;
return SVN_NO_ERROR;
}
iterpool = svn_pool_create(scratch_pool);
for (hi = apr_hash_first(scratch_pool, conflicted_props);
hi;
hi = apr_hash_next(hi))
{
const char *propname = svn__apr_hash_index_key(hi);
svn_string_t *old_value;
svn_string_t *my_value;
svn_string_t *their_value;
svn_wc_conflict_description2_t *desc;
svn_pool_clear(iterpool);
desc = svn_wc_conflict_description_create_prop2(local_abspath,
node_kind,
propname,
result_pool);
desc->operation = operation;
desc->src_left_version = left_version;
desc->src_right_version = right_version;
desc->property_name = apr_pstrdup(result_pool, propname);
my_value = svn_hash_gets(my_props, propname);
their_value = svn_hash_gets(their_props, propname);
old_value = svn_hash_gets(their_old_props, propname);
/* Compute the incoming side of the conflict ('action'). */
if (their_value == NULL)
desc->action = svn_wc_conflict_action_delete;
else if (old_value == NULL)
desc->action = svn_wc_conflict_action_add;
else
desc->action = svn_wc_conflict_action_edit;
/* Compute the local side of the conflict ('reason'). */
if (my_value == NULL)
desc->reason = svn_wc_conflict_reason_deleted;
else if (old_value == NULL)
desc->reason = svn_wc_conflict_reason_added;
else
desc->reason = svn_wc_conflict_reason_edited;
/* ### This should be changed. The prej file should be stored
* ### separately from the other files. We need to rev the
* ### conflict description struct for this. */
desc->their_abspath = apr_pstrdup(result_pool, prop_reject_file);
/* ### This should be changed. The conflict description for
* ### props should contain these values as svn_string_t,
* ### rather than in temporary files. We need to rev the
* ### conflict description struct for this. */
if (my_value)
{
svn_stream_t *s;
apr_size_t len;
SVN_ERR(svn_stream_open_unique(&s, &desc->my_abspath, NULL,
svn_io_file_del_on_pool_cleanup,
result_pool, iterpool));
len = my_value->len;
SVN_ERR(svn_stream_write(s, my_value->data, &len));
SVN_ERR(svn_stream_close(s));
}
if (their_value)
{
svn_stream_t *s;
apr_size_t len;
/* ### Currently, their_abspath is used for the prop reject file.
* ### Put their value into merged instead...
* ### We need to rev the conflict description struct to fix this. */
SVN_ERR(svn_stream_open_unique(&s, &desc->merged_file, NULL,
svn_io_file_del_on_pool_cleanup,
result_pool, iterpool));
len = their_value->len;
SVN_ERR(svn_stream_write(s, their_value->data, &len));
SVN_ERR(svn_stream_close(s));
}
if (old_value)
{
svn_stream_t *s;
apr_size_t len;
SVN_ERR(svn_stream_open_unique(&s, &desc->base_abspath, NULL,
svn_io_file_del_on_pool_cleanup,
result_pool, iterpool));
len = old_value->len;
SVN_ERR(svn_stream_write(s, old_value->data, &len));
SVN_ERR(svn_stream_close(s));
}
APR_ARRAY_PUSH(conflicts, svn_wc_conflict_description2_t*) = desc;
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__read_conflicts(const apr_array_header_t **conflicts,
svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t create_tempfiles,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_skel_t *conflict_skel;
apr_array_header_t *cflcts;
svn_boolean_t prop_conflicted;
svn_boolean_t text_conflicted;
svn_boolean_t tree_conflicted;
svn_wc_operation_t operation;
const apr_array_header_t *locations;
const svn_wc_conflict_version_t *left_version = NULL;
const svn_wc_conflict_version_t *right_version = NULL;
SVN_ERR(svn_wc__db_read_conflict(&conflict_skel, db, local_abspath,
scratch_pool, scratch_pool));
if (!conflict_skel)
{
/* Some callers expect not NULL */
*conflicts = apr_array_make(result_pool, 0,
sizeof(svn_wc_conflict_description2_t*));;
return SVN_NO_ERROR;
}
SVN_ERR(svn_wc__conflict_read_info(&operation, &locations, &text_conflicted,
&prop_conflicted, &tree_conflicted,
db, local_abspath, conflict_skel,
result_pool, scratch_pool));
cflcts = apr_array_make(result_pool, 4,
sizeof(svn_wc_conflict_description2_t*));
if (locations && locations->nelts > 0)
left_version = APR_ARRAY_IDX(locations, 0, const svn_wc_conflict_version_t *);
if (locations && locations->nelts > 1)
right_version = APR_ARRAY_IDX(locations, 1, const svn_wc_conflict_version_t *);
if (prop_conflicted)
{
svn_node_kind_t node_kind
= left_version ? left_version->node_kind : svn_node_unknown;
SVN_ERR(read_prop_conflicts(cflcts, db, local_abspath, conflict_skel,
create_tempfiles, node_kind,
operation, left_version, right_version,
result_pool, scratch_pool));
}
if (text_conflicted)
{
+ apr_hash_t *props;
+ const char *mime_type;
svn_wc_conflict_description2_t *desc;
desc = svn_wc_conflict_description_create_text2(local_abspath,
result_pool);
desc->operation = operation;
desc->src_left_version = left_version;
desc->src_right_version = right_version;
+ SVN_ERR(svn_wc__db_read_props(&props, db, local_abspath,
+ scratch_pool, scratch_pool));
+ mime_type = svn_prop_get_value(props, SVN_PROP_MIME_TYPE);
+ desc->is_binary = mime_type ? svn_mime_type_is_binary(mime_type) : FALSE;
+ desc->mime_type = mime_type;
+
SVN_ERR(svn_wc__conflict_read_text_conflict(&desc->my_abspath,
&desc->base_abspath,
&desc->their_abspath,
db, local_abspath,
conflict_skel,
result_pool, scratch_pool));
desc->merged_file = apr_pstrdup(result_pool, local_abspath);
APR_ARRAY_PUSH(cflcts, svn_wc_conflict_description2_t*) = desc;
}
if (tree_conflicted)
{
svn_wc_conflict_reason_t local_change;
svn_wc_conflict_action_t incoming_change;
svn_wc_conflict_description2_t *desc;
SVN_ERR(svn_wc__conflict_read_tree_conflict(&local_change,
&incoming_change,
NULL,
db, local_abspath,
conflict_skel,
scratch_pool, scratch_pool));
SVN_ERR(setup_tree_conflict_desc(&desc,
db, local_abspath,
operation, left_version, right_version,
local_change, incoming_change,
result_pool, scratch_pool));
APR_ARRAY_PUSH(cflcts, const svn_wc_conflict_description2_t *) = desc;
}
*conflicts = cflcts;
return SVN_NO_ERROR;
}
/*** Resolving a conflict automatically ***/
/* Prepare to delete an artifact file at ARTIFACT_FILE_ABSPATH in the
* working copy at DB/WRI_ABSPATH.
*
* Set *WORK_ITEMS to a new work item that, when run, will delete the
* artifact file; or to NULL if there is no file to delete.
*
* Set *FILE_FOUND to TRUE if the artifact file is found on disk and its
* node kind is 'file'; otherwise do not change *FILE_FOUND. FILE_FOUND
* may be NULL if not required.
*/
static svn_error_t *
remove_artifact_file_if_exists(svn_skel_t **work_items,
svn_boolean_t *file_found,
svn_wc__db_t *db,
const char *wri_abspath,
const char *artifact_file_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
*work_items = NULL;
if (artifact_file_abspath)
{
svn_node_kind_t node_kind;
SVN_ERR(svn_io_check_path(artifact_file_abspath, &node_kind,
scratch_pool));
if (node_kind == svn_node_file)
{
SVN_ERR(svn_wc__wq_build_file_remove(work_items,
db, wri_abspath,
artifact_file_abspath,
result_pool, scratch_pool));
if (file_found)
*file_found = TRUE;
}
}
return SVN_NO_ERROR;
}
/*
* Resolve the text conflict found in DB/LOCAL_ABSPATH according
* to CONFLICT_CHOICE.
*
* It is not an error if there is no text conflict. If a text conflict
* existed and was resolved, set *DID_RESOLVE to TRUE, else set it to FALSE.
*
* Note: When there are no conflict markers to remove there is no existing
* text conflict; just a database containing old information, which we should
* remove to avoid checking all the time. Resolving a text conflict by
* removing all the marker files is a fully supported scenario since
* Subversion 1.0.
*/
static svn_error_t *
resolve_text_conflict_on_node(svn_boolean_t *did_resolve,
svn_wc__db_t *db,
const char *local_abspath,
svn_wc_conflict_choice_t conflict_choice,
const char *merged_file,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
const char *conflict_old = NULL;
const char *conflict_new = NULL;
const char *conflict_working = NULL;
const char *auto_resolve_src;
svn_skel_t *work_item;
svn_skel_t *work_items = NULL;
svn_skel_t *conflicts;
svn_wc_operation_t operation;
svn_boolean_t text_conflicted;
*did_resolve = FALSE;
SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath,
scratch_pool, scratch_pool));
if (!conflicts)
return SVN_NO_ERROR;
SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, &text_conflicted,
NULL, NULL, db, local_abspath, conflicts,
scratch_pool, scratch_pool));
if (!text_conflicted)
return SVN_NO_ERROR;
SVN_ERR(svn_wc__conflict_read_text_conflict(&conflict_working,
&conflict_old,
&conflict_new,
db, local_abspath, conflicts,
scratch_pool, scratch_pool));
/* Handle automatic conflict resolution before the temporary files are
* deleted, if necessary. */
switch (conflict_choice)
{
case svn_wc_conflict_choose_base:
auto_resolve_src = conflict_old;
break;
case svn_wc_conflict_choose_mine_full:
auto_resolve_src = conflict_working;
break;
case svn_wc_conflict_choose_theirs_full:
auto_resolve_src = conflict_new;
break;
case svn_wc_conflict_choose_merged:
auto_resolve_src = merged_file;
break;
case svn_wc_conflict_choose_theirs_conflict:
case svn_wc_conflict_choose_mine_conflict:
{
if (conflict_old && conflict_working && conflict_new)
{
const char *temp_dir;
svn_stream_t *tmp_stream = NULL;
svn_diff_t *diff;
svn_diff_conflict_display_style_t style =
conflict_choice == svn_wc_conflict_choose_theirs_conflict
? svn_diff_conflict_display_latest
: svn_diff_conflict_display_modified;
SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir, db,
local_abspath,
scratch_pool,
scratch_pool));
SVN_ERR(svn_stream_open_unique(&tmp_stream,
&auto_resolve_src,
temp_dir,
svn_io_file_del_on_pool_cleanup,
scratch_pool, scratch_pool));
SVN_ERR(svn_diff_file_diff3_2(&diff,
conflict_old,
conflict_working,
conflict_new,
svn_diff_file_options_create(
scratch_pool),
scratch_pool));
SVN_ERR(svn_diff_file_output_merge2(tmp_stream, diff,
conflict_old,
conflict_working,
conflict_new,
/* markers ignored */
NULL, NULL, NULL, NULL,
style,
scratch_pool));
SVN_ERR(svn_stream_close(tmp_stream));
}
else
auto_resolve_src = NULL;
break;
}
default:
return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
_("Invalid 'conflict_result' argument"));
}
if (auto_resolve_src)
{
SVN_ERR(svn_wc__wq_build_file_copy_translated(
&work_item, db, local_abspath,
auto_resolve_src, local_abspath, scratch_pool, scratch_pool));
work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, db,
local_abspath,
scratch_pool, scratch_pool));
work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
}
/* Legacy behavior: Only report text conflicts as resolved when at least
one conflict marker file exists.
If not the UI shows the conflict as already resolved
(and in this case we just remove the in-db conflict) */
SVN_ERR(remove_artifact_file_if_exists(&work_item, did_resolve,
db, local_abspath, conflict_old,
scratch_pool, scratch_pool));
work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
SVN_ERR(remove_artifact_file_if_exists(&work_item, did_resolve,
db, local_abspath, conflict_new,
scratch_pool, scratch_pool));
work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
SVN_ERR(remove_artifact_file_if_exists(&work_item, did_resolve,
db, local_abspath, conflict_working,
scratch_pool, scratch_pool));
work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath,
TRUE, FALSE, FALSE,
work_items, scratch_pool));
SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
scratch_pool));
return SVN_NO_ERROR;
}
/*
* Resolve the property conflicts found in DB/LOCAL_ABSPATH according
* to CONFLICT_CHOICE.
*
* It is not an error if there is no prop conflict. If a prop conflict
* existed and was resolved, set *DID_RESOLVE to TRUE, else set it to FALSE.
*
* Note: When there are no conflict markers on-disk to remove there is
* no existing text conflict (unless we are still in the process of
* creating the text conflict and we didn't register a marker file yet).
* In this case the database contains old information, which we should
* remove to avoid checking the next time. Resolving a property conflict
* by just removing the marker file is a fully supported scenario since
* Subversion 1.0.
*
* ### TODO [JAF] The '*_full' and '*_conflict' choices should differ.
* In my opinion, 'mine_full'/'theirs_full' should select
* the entire set of properties from 'mine' or 'theirs' respectively,
* while 'mine_conflict'/'theirs_conflict' should select just the
* properties that are in conflict. Or, '_full' should select the
* entire property whereas '_conflict' should do a text merge within
* each property, selecting hunks. Or all three kinds of behaviour
* should be available (full set of props, full value of conflicting
* props, or conflicting text hunks).
* ### BH: If we make *_full select the full set of properties, we should
* check if we shouldn't make it also select the full text for files.
*
* ### TODO [JAF] All this complexity should not be down here in libsvn_wc
* but in a layer above.
*
* ### TODO [JAF] Options for 'base' should be like options for 'mine' and
* for 'theirs' -- choose full set of props, full value of conflicting
* props, or conflicting text hunks.
*
*/
static svn_error_t *
resolve_prop_conflict_on_node(svn_boolean_t *did_resolve,
svn_wc__db_t *db,
const char *local_abspath,
const char *conflicted_propname,
svn_wc_conflict_choice_t conflict_choice,
const char *merged_file,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
const char *prop_reject_file;
apr_hash_t *mine_props;
apr_hash_t *their_old_props;
apr_hash_t *their_props;
apr_hash_t *conflicted_props;
apr_hash_t *old_props;
apr_hash_t *resolve_from = NULL;
svn_skel_t *work_items = NULL;
svn_skel_t *conflicts;
svn_wc_operation_t operation;
svn_boolean_t prop_conflicted;
*did_resolve = FALSE;
SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath,
scratch_pool, scratch_pool));
if (!conflicts)
return SVN_NO_ERROR;
SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, &prop_conflicted,
NULL, db, local_abspath, conflicts,
scratch_pool, scratch_pool));
if (!prop_conflicted)
return SVN_NO_ERROR;
SVN_ERR(svn_wc__conflict_read_prop_conflict(&prop_reject_file,
&mine_props, &their_old_props,
&their_props, &conflicted_props,
db, local_abspath, conflicts,
scratch_pool, scratch_pool));
if (operation == svn_wc_operation_merge)
SVN_ERR(svn_wc__db_read_pristine_props(&old_props, db, local_abspath,
scratch_pool, scratch_pool));
else
old_props = their_old_props;
/* We currently handle *_conflict as *_full as this argument is currently
always applied for all conflicts on a node at the same time. Giving
an error would break some tests that assumed that this would just
resolve property conflicts to working.
An alternative way to handle these conflicts would be to just copy all
property state from mine/theirs on the _full option instead of just the
conflicted properties. In some ways this feels like a sensible option as
that would take both properties and text from mine/theirs, but when not
both properties and text are conflicted we would fail in doing so.
*/
switch (conflict_choice)
{
case svn_wc_conflict_choose_base:
resolve_from = their_old_props ? their_old_props : old_props;
break;
case svn_wc_conflict_choose_mine_full:
case svn_wc_conflict_choose_mine_conflict:
resolve_from = mine_props;
break;
case svn_wc_conflict_choose_theirs_full:
case svn_wc_conflict_choose_theirs_conflict:
resolve_from = their_props;
break;
case svn_wc_conflict_choose_merged:
if (merged_file && conflicted_propname[0] != '\0')
{
apr_hash_t *actual_props;
svn_stream_t *stream;
svn_string_t *merged_propval;
SVN_ERR(svn_wc__db_read_props(&actual_props, db, local_abspath,
scratch_pool, scratch_pool));
resolve_from = actual_props;
SVN_ERR(svn_stream_open_readonly(&stream, merged_file,
scratch_pool, scratch_pool));
SVN_ERR(svn_string_from_stream(&merged_propval, stream,
scratch_pool, scratch_pool));
svn_hash_sets(resolve_from, conflicted_propname, merged_propval);
}
else
resolve_from = NULL;
break;
default:
return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
_("Invalid 'conflict_result' argument"));
}
if (conflicted_props && apr_hash_count(conflicted_props) && resolve_from)
{
apr_hash_index_t *hi;
apr_hash_t *actual_props;
SVN_ERR(svn_wc__db_read_props(&actual_props, db, local_abspath,
scratch_pool, scratch_pool));
for (hi = apr_hash_first(scratch_pool, conflicted_props);
hi;
hi = apr_hash_next(hi))
{
const char *propname = svn__apr_hash_index_key(hi);
svn_string_t *new_value = NULL;
new_value = svn_hash_gets(resolve_from, propname);
svn_hash_sets(actual_props, propname, new_value);
}
SVN_ERR(svn_wc__db_op_set_props(db, local_abspath, actual_props,
FALSE, NULL, NULL,
scratch_pool));
}
/* Legacy behavior: Only report property conflicts as resolved when the
property reject file exists
If not the UI shows the conflict as already resolved
(and in this case we just remove the in-db conflict) */
{
svn_skel_t *work_item;
SVN_ERR(remove_artifact_file_if_exists(&work_item, did_resolve,
db, local_abspath, prop_reject_file,
scratch_pool, scratch_pool));
work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
}
SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, FALSE, TRUE, FALSE,
work_items, scratch_pool));
SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
scratch_pool));
return SVN_NO_ERROR;
}
/*
* Resolve the tree conflict found in DB/LOCAL_ABSPATH according to
* CONFLICT_CHOICE.
*
* It is not an error if there is no tree conflict. If a tree conflict
* existed and was resolved, set *DID_RESOLVE to TRUE, else set it to FALSE.
*
* It is not an error if there is no tree conflict.
*/
static svn_error_t *
resolve_tree_conflict_on_node(svn_boolean_t *did_resolve,
svn_wc__db_t *db,
const char *local_abspath,
svn_wc_conflict_choice_t conflict_choice,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
svn_wc_conflict_reason_t reason;
svn_wc_conflict_action_t action;
svn_skel_t *conflicts;
svn_wc_operation_t operation;
svn_boolean_t tree_conflicted;
*did_resolve = FALSE;
SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath,
scratch_pool, scratch_pool));
if (!conflicts)
return SVN_NO_ERROR;
SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL,
&tree_conflicted, db, local_abspath,
conflicts, scratch_pool, scratch_pool));
if (!tree_conflicted)
return SVN_NO_ERROR;
SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action, NULL,
db, local_abspath,
conflicts,
scratch_pool, scratch_pool));
if (operation == svn_wc_operation_update
|| operation == svn_wc_operation_switch)
{
if (reason == svn_wc_conflict_reason_deleted ||
reason == svn_wc_conflict_reason_replaced)
{
if (conflict_choice == svn_wc_conflict_choose_merged)
{
/* Break moves for any children moved out of this directory,
* and leave this directory deleted. */
SVN_ERR(svn_wc__db_resolve_break_moved_away_children(
db, local_abspath, notify_func, notify_baton,
scratch_pool));
*did_resolve = TRUE;
}
else if (conflict_choice == svn_wc_conflict_choose_mine_conflict)
{
/* Raised moved-away conflicts on any children moved out of
* this directory, and leave this directory deleted.
* The newly conflicted moved-away children will be updated
* if they are resolved with 'mine_conflict' as well. */
SVN_ERR(svn_wc__db_resolve_delete_raise_moved_away(
db, local_abspath, notify_func, notify_baton,
scratch_pool));
*did_resolve = TRUE;
}
else
return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
NULL,
_("Tree conflict can only be resolved to "
"'working' or 'mine-conflict' state; "
"'%s' not resolved"),
svn_dirent_local_style(local_abspath,
scratch_pool));
}
else if (reason == svn_wc_conflict_reason_moved_away
&& action == svn_wc_conflict_action_edit)
{
/* After updates, we can resolve local moved-away
* vs. any incoming change, either by updating the
* moved-away node (mine-conflict) or by breaking the
* move (theirs-conflict). */
if (conflict_choice == svn_wc_conflict_choose_mine_conflict)
{
SVN_ERR(svn_wc__db_update_moved_away_conflict_victim(
db, local_abspath,
notify_func, notify_baton,
cancel_func, cancel_baton,
scratch_pool));
*did_resolve = TRUE;
}
else if (conflict_choice == svn_wc_conflict_choose_merged)
{
/* We must break the move if the user accepts the current
* working copy state instead of updating the move.
* Else the move would be left in an invalid state. */
/* ### This breaks the move but leaves the conflict
### involving the move until
### svn_wc__db_op_mark_resolved. */
SVN_ERR(svn_wc__db_resolve_break_moved_away(db, local_abspath,
notify_func,
notify_baton,
scratch_pool));
*did_resolve = TRUE;
}
else
return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
NULL,
_("Tree conflict can only be resolved to "
"'working' or 'mine-conflict' state; "
"'%s' not resolved"),
svn_dirent_local_style(local_abspath,
scratch_pool));
}
}
if (! *did_resolve && conflict_choice != svn_wc_conflict_choose_merged)
{
/* For other tree conflicts, there is no way to pick
* theirs-full or mine-full, etc. Throw an error if the
* user expects us to be smarter than we really are. */
return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
NULL,
_("Tree conflict can only be "
"resolved to 'working' state; "
"'%s' not resolved"),
svn_dirent_local_style(local_abspath,
scratch_pool));
}
SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, FALSE, FALSE, TRUE,
NULL, scratch_pool));
SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__mark_resolved_text_conflict(svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
svn_boolean_t ignored_result;
return svn_error_trace(resolve_text_conflict_on_node(
&ignored_result,
db, local_abspath,
svn_wc_conflict_choose_merged, NULL,
NULL, NULL,
scratch_pool));
}
svn_error_t *
svn_wc__mark_resolved_prop_conflicts(svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
svn_boolean_t ignored_result;
return svn_error_trace(resolve_prop_conflict_on_node(
&ignored_result,
db, local_abspath, "",
svn_wc_conflict_choose_merged, NULL,
NULL, NULL,
scratch_pool));
}
/* Baton for conflict_status_walker */
struct conflict_status_walker_baton
{
svn_wc__db_t *db;
svn_boolean_t resolve_text;
const char *resolve_prop;
svn_boolean_t resolve_tree;
svn_wc_conflict_choice_t conflict_choice;
svn_wc_conflict_resolver_func2_t conflict_func;
void *conflict_baton;
svn_cancel_func_t cancel_func;
void *cancel_baton;
svn_wc_notify_func2_t notify_func;
void *notify_baton;
};
/* Implements svn_wc_status4_t to walk all conflicts to resolve.
*/
static svn_error_t *
conflict_status_walker(void *baton,
const char *local_abspath,
const svn_wc_status3_t *status,
apr_pool_t *scratch_pool)
{
struct conflict_status_walker_baton *cswb = baton;
svn_wc__db_t *db = cswb->db;
const apr_array_header_t *conflicts;
apr_pool_t *iterpool;
int i;
svn_boolean_t resolved = FALSE;
if (!status->conflicted)
return SVN_NO_ERROR;
iterpool = svn_pool_create(scratch_pool);
SVN_ERR(svn_wc__read_conflicts(&conflicts, db, local_abspath, TRUE,
scratch_pool, iterpool));
for (i = 0; i < conflicts->nelts; i++)
{
const svn_wc_conflict_description2_t *cd;
svn_boolean_t did_resolve;
svn_wc_conflict_choice_t my_choice = cswb->conflict_choice;
const char *merged_file = NULL;
cd = APR_ARRAY_IDX(conflicts, i, const svn_wc_conflict_description2_t *);
+
+ if ((cd->kind == svn_wc_conflict_kind_property && !cswb->resolve_prop)
+ || (cd->kind == svn_wc_conflict_kind_text && !cswb->resolve_text)
+ || (cd->kind == svn_wc_conflict_kind_tree && !cswb->resolve_tree))
+ {
+ continue; /* Easy out. Don't call resolver func and ignore result */
+ }
svn_pool_clear(iterpool);
if (my_choice == svn_wc_conflict_choose_unspecified)
{
svn_wc_conflict_result_t *result;
if (!cswb->conflict_func)
return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
_("No conflict-callback and no "
"pre-defined conflict-choice provided"));
SVN_ERR(cswb->conflict_func(&result, cd, cswb->conflict_baton,
iterpool, iterpool));
my_choice = result->choice;
merged_file = result->merged_file;
/* ### Bug: ignores result->save_merged */
}
if (my_choice == svn_wc_conflict_choose_postpone)
continue;
switch (cd->kind)
{
case svn_wc_conflict_kind_tree:
if (!cswb->resolve_tree)
break;
SVN_ERR(resolve_tree_conflict_on_node(&did_resolve,
db,
local_abspath,
my_choice,
cswb->notify_func,
cswb->notify_baton,
cswb->cancel_func,
cswb->cancel_baton,
iterpool));
resolved = TRUE;
break;
case svn_wc_conflict_kind_text:
if (!cswb->resolve_text)
break;
SVN_ERR(resolve_text_conflict_on_node(&did_resolve,
db,
local_abspath,
my_choice,
merged_file,
cswb->cancel_func,
cswb->cancel_baton,
iterpool));
if (did_resolve)
resolved = TRUE;
break;
case svn_wc_conflict_kind_property:
if (!cswb->resolve_prop)
break;
if (*cswb->resolve_prop != '\0' &&
strcmp(cswb->resolve_prop, cd->property_name) != 0)
{
break; /* This is not the property we want to resolve. */
}
SVN_ERR(resolve_prop_conflict_on_node(&did_resolve,
db,
local_abspath,
cd->property_name,
my_choice,
merged_file,
cswb->cancel_func,
cswb->cancel_baton,
iterpool));
if (did_resolve)
resolved = TRUE;
break;
default:
/* We can't resolve other conflict types */
break;
}
}
/* Notify */
if (cswb->notify_func && resolved)
cswb->notify_func(cswb->notify_baton,
svn_wc_create_notify(local_abspath,
svn_wc_notify_resolved,
iterpool),
iterpool);
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__resolve_conflicts(svn_wc_context_t *wc_ctx,
const char *local_abspath,
svn_depth_t depth,
svn_boolean_t resolve_text,
const char *resolve_prop,
svn_boolean_t resolve_tree,
svn_wc_conflict_choice_t conflict_choice,
svn_wc_conflict_resolver_func2_t conflict_func,
void *conflict_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool)
{
svn_node_kind_t kind;
svn_boolean_t conflicted;
struct conflict_status_walker_baton cswb;
/* ### the underlying code does NOT support resolving individual
### properties. bail out if the caller tries it. */
if (resolve_prop != NULL && *resolve_prop != '\0')
return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
U_("Resolving a single property is not (yet) "
"supported."));
/* ### Just a versioned check? */
/* Conflicted is set to allow invoking on actual only nodes */
SVN_ERR(svn_wc__db_read_info(NULL, &kind, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, &conflicted,
NULL, NULL, NULL, NULL, NULL, NULL,
wc_ctx->db, local_abspath,
scratch_pool, scratch_pool));
/* When the implementation still used the entry walker, depth
unknown was translated to infinity. */
if (kind != svn_node_dir)
depth = svn_depth_empty;
else if (depth == svn_depth_unknown)
depth = svn_depth_infinity;
cswb.db = wc_ctx->db;
cswb.resolve_text = resolve_text;
cswb.resolve_prop = resolve_prop;
cswb.resolve_tree = resolve_tree;
cswb.conflict_choice = conflict_choice;
cswb.conflict_func = conflict_func;
cswb.conflict_baton = conflict_baton;
cswb.cancel_func = cancel_func;
cswb.cancel_baton = cancel_baton;
cswb.notify_func = notify_func;
cswb.notify_baton = notify_baton;
if (notify_func)
notify_func(notify_baton,
svn_wc_create_notify(local_abspath,
svn_wc_notify_conflict_resolver_starting,
scratch_pool),
scratch_pool);
SVN_ERR(svn_wc_walk_status(wc_ctx,
local_abspath,
depth,
FALSE /* get_all */,
FALSE /* no_ignore */,
TRUE /* ignore_text_mods */,
NULL /* ignore_patterns */,
conflict_status_walker, &cswb,
cancel_func, cancel_baton,
scratch_pool));
if (notify_func)
notify_func(notify_baton,
svn_wc_create_notify(local_abspath,
svn_wc_notify_conflict_resolver_done,
scratch_pool),
scratch_pool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_resolved_conflict5(svn_wc_context_t *wc_ctx,
const char *local_abspath,
svn_depth_t depth,
svn_boolean_t resolve_text,
const char *resolve_prop,
svn_boolean_t resolve_tree,
svn_wc_conflict_choice_t conflict_choice,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool)
{
return svn_error_trace(svn_wc__resolve_conflicts(wc_ctx, local_abspath,
depth, resolve_text,
resolve_prop, resolve_tree,
conflict_choice,
NULL, NULL,
cancel_func, cancel_baton,
notify_func, notify_baton,
scratch_pool));
}
/* Constructor for the result-structure returned by conflict callbacks. */
svn_wc_conflict_result_t *
svn_wc_create_conflict_result(svn_wc_conflict_choice_t choice,
const char *merged_file,
apr_pool_t *pool)
{
svn_wc_conflict_result_t *result = apr_pcalloc(pool, sizeof(*result));
result->choice = choice;
result->merged_file = merged_file;
result->save_merged = FALSE;
/* If we add more fields to svn_wc_conflict_result_t, add them here. */
return result;
}
Index: vendor/subversion/dist/subversion/libsvn_wc/copy.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_wc/copy.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_wc/copy.c (revision 286501)
@@ -1,1048 +1,1056 @@
/*
* copy.c: wc 'copy' functionality.
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
/* ==================================================================== */
/*** Includes. ***/
#include <string.h>
#include "svn_pools.h"
#include "svn_error.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_hash.h"
#include "wc.h"
#include "workqueue.h"
#include "props.h"
#include "conflicts.h"
#include "svn_private_config.h"
#include "private/svn_wc_private.h"
/*** Code. ***/
/* Make a copy of the filesystem node (or tree if RECURSIVE) at
SRC_ABSPATH under a temporary name in the directory
TMPDIR_ABSPATH and return the absolute path of the copy in
*DST_ABSPATH. Return the node kind of SRC_ABSPATH in *KIND. If
SRC_ABSPATH doesn't exist then set *DST_ABSPATH to NULL to indicate
that no copy was made. */
static svn_error_t *
copy_to_tmpdir(svn_skel_t **work_item,
svn_node_kind_t *kind,
svn_wc__db_t *db,
const char *src_abspath,
const char *dst_abspath,
const char *tmpdir_abspath,
svn_boolean_t file_copy,
svn_boolean_t unversioned,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_boolean_t is_special;
svn_io_file_del_t delete_when;
const char *dst_tmp_abspath;
svn_node_kind_t dsk_kind;
if (!kind)
kind = &dsk_kind;
*work_item = NULL;
SVN_ERR(svn_io_check_special_path(src_abspath, kind, &is_special,
scratch_pool));
if (*kind == svn_node_none)
{
return SVN_NO_ERROR;
}
else if (*kind == svn_node_unknown)
{
return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
_("Source '%s' is unexpected kind"),
svn_dirent_local_style(src_abspath,
scratch_pool));
}
else if (*kind == svn_node_dir || is_special)
delete_when = svn_io_file_del_on_close;
else /* the default case: (*kind == svn_node_file) */
delete_when = svn_io_file_del_none;
/* ### Do we need a pool cleanup to remove the copy? We can't use
### svn_io_file_del_on_pool_cleanup above because a) it won't
### handle the directory case and b) we need to be able to remove
### the cleanup before queueing the move work item. */
if (file_copy && !unversioned)
{
svn_boolean_t modified;
/* It's faster to look for mods on the source now, as
the timestamp might match, than to examine the
destination later as the destination timestamp will
never match. */
SVN_ERR(svn_wc__internal_file_modified_p(&modified,
db, src_abspath,
FALSE, scratch_pool));
if (!modified)
{
/* Why create a temp copy if we can just reinstall from pristine? */
SVN_ERR(svn_wc__wq_build_file_install(work_item,
db, dst_abspath, NULL, FALSE,
TRUE,
result_pool, scratch_pool));
return SVN_NO_ERROR;
}
}
/* Set DST_TMP_ABSPATH to a temporary unique path. If *KIND is file, leave
a file there and then overwrite it; otherwise leave no node on disk at
that path. In the latter case, something else might use that path
before we get around to using it a moment later, but never mind. */
SVN_ERR(svn_io_open_unique_file3(NULL, &dst_tmp_abspath, tmpdir_abspath,
delete_when, scratch_pool, scratch_pool));
if (*kind == svn_node_dir)
{
if (file_copy)
SVN_ERR(svn_io_copy_dir_recursively(
src_abspath,
tmpdir_abspath,
svn_dirent_basename(dst_tmp_abspath, scratch_pool),
TRUE, /* copy_perms */
cancel_func, cancel_baton,
scratch_pool));
else
SVN_ERR(svn_io_dir_make(dst_tmp_abspath, APR_OS_DEFAULT, scratch_pool));
}
else if (!is_special)
SVN_ERR(svn_io_copy_file(src_abspath, dst_tmp_abspath,
TRUE /* copy_perms */,
scratch_pool));
else
SVN_ERR(svn_io_copy_link(src_abspath, dst_tmp_abspath, scratch_pool));
if (file_copy)
{
/* Remove 'read-only' from the destination file; it's a local add now. */
SVN_ERR(svn_io_set_file_read_write(dst_tmp_abspath,
FALSE, scratch_pool));
}
SVN_ERR(svn_wc__wq_build_file_move(work_item, db, dst_abspath,
dst_tmp_abspath, dst_abspath,
result_pool, scratch_pool));
return SVN_NO_ERROR;
}
/* Copy the versioned file SRC_ABSPATH in DB to the path DST_ABSPATH in DB.
If METADATA_ONLY is true, copy only the versioned metadata,
otherwise copy both the versioned metadata and the filesystem node (even
if it is the wrong kind, and recursively if it is a dir).
If IS_MOVE is true, record move information in working copy meta
data in addition to copying the file.
If the versioned file has a text conflict, and the .mine file exists in
the filesystem, copy the .mine file to DST_ABSPATH. Otherwise, copy the
versioned file itself.
This also works for versioned symlinks that are stored in the db as
svn_node_file with svn:special set. */
static svn_error_t *
copy_versioned_file(svn_wc__db_t *db,
const char *src_abspath,
const char *dst_abspath,
const char *dst_op_root_abspath,
const char *tmpdir_abspath,
svn_boolean_t metadata_only,
svn_boolean_t conflicted,
svn_boolean_t is_move,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool)
{
svn_skel_t *work_items = NULL;
/* In case we are copying from one WC to another (e.g. an external dir),
ensure the destination WC has a copy of the pristine text. */
/* Prepare a temp copy of the filesystem node. It is usually a file, but
copy recursively if it's a dir. */
if (!metadata_only)
{
const char *my_src_abspath = NULL;
svn_boolean_t handle_as_unversioned = FALSE;
/* By default, take the copy source as given. */
my_src_abspath = src_abspath;
if (conflicted)
{
svn_skel_t *conflict;
const char *conflict_working;
svn_error_t *err;
/* Is there a text conflict at the source path? */
SVN_ERR(svn_wc__db_read_conflict(&conflict, db, src_abspath,
scratch_pool, scratch_pool));
err = svn_wc__conflict_read_text_conflict(&conflict_working, NULL, NULL,
db, src_abspath, conflict,
scratch_pool,
scratch_pool);
if (err && err->apr_err == SVN_ERR_WC_MISSING)
{
/* not text conflicted */
svn_error_clear(err);
conflict_working = NULL;
}
else
SVN_ERR(err);
if (conflict_working)
{
svn_node_kind_t working_kind;
/* Does the ".mine" file exist? */
SVN_ERR(svn_io_check_path(conflict_working, &working_kind,
scratch_pool));
if (working_kind == svn_node_file)
{
/* Don't perform unmodified/pristine optimization */
handle_as_unversioned = TRUE;
my_src_abspath = conflict_working;
}
}
}
SVN_ERR(copy_to_tmpdir(&work_items, NULL, db, my_src_abspath,
dst_abspath, tmpdir_abspath,
TRUE /* file_copy */,
handle_as_unversioned /* unversioned */,
cancel_func, cancel_baton,
scratch_pool, scratch_pool));
}
/* Copy the (single) node's metadata, and move the new filesystem node
into place. */
SVN_ERR(svn_wc__db_op_copy(db, src_abspath, dst_abspath,
dst_op_root_abspath, is_move, work_items,
scratch_pool));
if (notify_func)
{
svn_wc_notify_t *notify
= svn_wc_create_notify(dst_abspath, svn_wc_notify_add,
scratch_pool);
notify->kind = svn_node_file;
/* When we notify that we performed a copy, make sure we already did */
if (work_items != NULL)
SVN_ERR(svn_wc__wq_run(db, dst_abspath,
cancel_func, cancel_baton, scratch_pool));
(*notify_func)(notify_baton, notify, scratch_pool);
}
return SVN_NO_ERROR;
}
/* Copy the versioned dir SRC_ABSPATH in DB to the path DST_ABSPATH in DB,
recursively. If METADATA_ONLY is true, copy only the versioned metadata,
otherwise copy both the versioned metadata and the filesystem nodes (even
if they are the wrong kind, and including unversioned children).
If IS_MOVE is true, record move information in working copy meta
data in addition to copying the directory.
WITHIN_ONE_WC is TRUE if the copy/move is within a single working copy (root)
*/
static svn_error_t *
copy_versioned_dir(svn_wc__db_t *db,
const char *src_abspath,
const char *dst_abspath,
const char *dst_op_root_abspath,
const char *tmpdir_abspath,
svn_boolean_t metadata_only,
svn_boolean_t is_move,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool)
{
svn_skel_t *work_items = NULL;
const char *dir_abspath = svn_dirent_dirname(dst_abspath, scratch_pool);
apr_hash_t *versioned_children;
apr_hash_t *conflicted_children;
apr_hash_t *disk_children;
apr_hash_index_t *hi;
svn_node_kind_t disk_kind;
apr_pool_t *iterpool;
/* Prepare a temp copy of the single filesystem node (usually a dir). */
if (!metadata_only)
{
SVN_ERR(copy_to_tmpdir(&work_items, &disk_kind,
db, src_abspath, dst_abspath,
tmpdir_abspath,
FALSE /* file_copy */,
FALSE /* unversioned */,
cancel_func, cancel_baton,
scratch_pool, scratch_pool));
}
/* Copy the (single) node's metadata, and move the new filesystem node
into place. */
SVN_ERR(svn_wc__db_op_copy(db, src_abspath, dst_abspath,
dst_op_root_abspath, is_move, work_items,
scratch_pool));
if (notify_func)
{
svn_wc_notify_t *notify
= svn_wc_create_notify(dst_abspath, svn_wc_notify_add,
scratch_pool);
notify->kind = svn_node_dir;
/* When we notify that we performed a copy, make sure we already did */
if (work_items != NULL)
SVN_ERR(svn_wc__wq_run(db, dir_abspath,
cancel_func, cancel_baton, scratch_pool));
(*notify_func)(notify_baton, notify, scratch_pool);
}
if (!metadata_only && disk_kind == svn_node_dir)
/* All filesystem children, versioned and unversioned. We're only
interested in their names, so we can pass TRUE as the only_check_type
param. */
SVN_ERR(svn_io_get_dirents3(&disk_children, src_abspath, TRUE,
scratch_pool, scratch_pool));
else
disk_children = NULL;
/* Copy all the versioned children */
iterpool = svn_pool_create(scratch_pool);
SVN_ERR(svn_wc__db_read_children_info(&versioned_children,
&conflicted_children,
db, src_abspath,
scratch_pool, iterpool));
for (hi = apr_hash_first(scratch_pool, versioned_children);
hi;
hi = apr_hash_next(hi))
{
const char *child_name, *child_src_abspath, *child_dst_abspath;
struct svn_wc__db_info_t *info;
svn_pool_clear(iterpool);
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
child_name = svn__apr_hash_index_key(hi);
info = svn__apr_hash_index_val(hi);
child_src_abspath = svn_dirent_join(src_abspath, child_name, iterpool);
child_dst_abspath = svn_dirent_join(dst_abspath, child_name, iterpool);
if (info->op_root)
SVN_ERR(svn_wc__db_op_copy_shadowed_layer(db,
child_src_abspath,
child_dst_abspath,
is_move,
scratch_pool));
if (info->status == svn_wc__db_status_normal
|| info->status == svn_wc__db_status_added)
{
/* We have more work to do than just changing the DB */
if (info->kind == svn_node_file)
{
/* We should skip this node if this child is a file external
(issues #3589, #4000) */
if (!info->file_external)
SVN_ERR(copy_versioned_file(db,
child_src_abspath,
child_dst_abspath,
dst_op_root_abspath,
tmpdir_abspath,
metadata_only, info->conflicted,
is_move,
cancel_func, cancel_baton,
NULL, NULL,
iterpool));
}
else if (info->kind == svn_node_dir)
SVN_ERR(copy_versioned_dir(db,
child_src_abspath, child_dst_abspath,
dst_op_root_abspath, tmpdir_abspath,
metadata_only, is_move,
cancel_func, cancel_baton, NULL, NULL,
iterpool));
else
return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
_("cannot handle node kind for '%s'"),
svn_dirent_local_style(child_src_abspath,
scratch_pool));
}
else if (info->status == svn_wc__db_status_deleted
|| info->status == svn_wc__db_status_not_present
|| info->status == svn_wc__db_status_excluded)
{
/* This will be copied as some kind of deletion. Don't touch
any actual files */
SVN_ERR(svn_wc__db_op_copy(db, child_src_abspath,
child_dst_abspath, dst_op_root_abspath,
is_move, NULL, iterpool));
/* Don't recurse on children while all we do is creating not-present
children */
}
else if (info->status == svn_wc__db_status_incomplete)
{
/* Should go ahead and copy incomplete to incomplete? Try to
copy as much as possible, or give up early? */
return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
_("Cannot handle status of '%s'"),
svn_dirent_local_style(child_src_abspath,
iterpool));
}
else
{
SVN_ERR_ASSERT(info->status == svn_wc__db_status_server_excluded);
return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
_("Cannot copy '%s' excluded by server"),
svn_dirent_local_style(child_src_abspath,
iterpool));
}
if (disk_children
&& (info->status == svn_wc__db_status_normal
|| info->status == svn_wc__db_status_added))
{
/* Remove versioned child as it has been handled */
svn_hash_sets(disk_children, child_name, NULL);
}
}
/* Copy the remaining filesystem children, which are unversioned, skipping
any conflict-marker files. */
if (disk_children && apr_hash_count(disk_children))
{
apr_hash_t *marker_files;
SVN_ERR(svn_wc__db_get_conflict_marker_files(&marker_files, db,
src_abspath, scratch_pool,
scratch_pool));
work_items = NULL;
for (hi = apr_hash_first(scratch_pool, disk_children); hi;
hi = apr_hash_next(hi))
{
const char *name = svn__apr_hash_index_key(hi);
const char *unver_src_abspath, *unver_dst_abspath;
svn_skel_t *work_item;
if (svn_wc_is_adm_dir(name, iterpool))
continue;
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
svn_pool_clear(iterpool);
unver_src_abspath = svn_dirent_join(src_abspath, name, iterpool);
unver_dst_abspath = svn_dirent_join(dst_abspath, name, iterpool);
if (marker_files &&
svn_hash_gets(marker_files, unver_src_abspath))
continue;
SVN_ERR(copy_to_tmpdir(&work_item, NULL, db, unver_src_abspath,
unver_dst_abspath, tmpdir_abspath,
TRUE /* recursive */, TRUE /* unversioned */,
cancel_func, cancel_baton,
scratch_pool, iterpool));
if (work_item)
work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
}
SVN_ERR(svn_wc__db_wq_add(db, dst_abspath, work_items, iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* The guts of svn_wc_copy3() and svn_wc_move().
* The additional parameter IS_MOVE indicates whether this is a copy or
* a move operation.
*
* If MOVE_DEGRADED_TO_COPY is not NULL and a move had to be degraded
* to a copy, then set *MOVE_DEGRADED_TO_COPY. */
static svn_error_t *
copy_or_move(svn_boolean_t *move_degraded_to_copy,
svn_wc_context_t *wc_ctx,
const char *src_abspath,
const char *dst_abspath,
svn_boolean_t metadata_only,
svn_boolean_t is_move,
svn_boolean_t allow_mixed_revisions,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool)
{
svn_wc__db_t *db = wc_ctx->db;
svn_node_kind_t src_db_kind;
const char *dstdir_abspath;
svn_boolean_t conflicted;
const char *tmpdir_abspath;
const char *src_wcroot_abspath;
const char *dst_wcroot_abspath;
svn_boolean_t within_one_wc;
svn_wc__db_status_t src_status;
svn_error_t *err;
SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
dstdir_abspath = svn_dirent_dirname(dst_abspath, scratch_pool);
/* Ensure DSTDIR_ABSPATH belongs to the same repository as SRC_ABSPATH;
throw an error if not. */
{
svn_wc__db_status_t dstdir_status;
const char *src_repos_root_url, *dst_repos_root_url;
const char *src_repos_uuid, *dst_repos_uuid;
const char *src_repos_relpath;
err = svn_wc__db_read_info(&src_status, &src_db_kind, NULL,
&src_repos_relpath, &src_repos_root_url,
&src_repos_uuid, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, &conflicted, NULL, NULL, NULL, NULL,
NULL, NULL,
db, src_abspath, scratch_pool, scratch_pool);
if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
{
/* Replicate old error code and text */
svn_error_clear(err);
return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
_("'%s' is not under version control"),
svn_dirent_local_style(src_abspath,
scratch_pool));
}
else
SVN_ERR(err);
/* Do this now, as we know the right data is cached */
SVN_ERR(svn_wc__db_get_wcroot(&src_wcroot_abspath, db, src_abspath,
scratch_pool, scratch_pool));
switch (src_status)
{
case svn_wc__db_status_deleted:
return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
_("Deleted node '%s' can't be copied."),
svn_dirent_local_style(src_abspath,
scratch_pool));
case svn_wc__db_status_excluded:
case svn_wc__db_status_server_excluded:
case svn_wc__db_status_not_present:
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("The node '%s' was not found."),
svn_dirent_local_style(src_abspath,
scratch_pool));
default:
break;
}
if (is_move && ! strcmp(src_abspath, src_wcroot_abspath))
{
return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
_("'%s' is the root of a working copy and "
"cannot be moved"),
svn_dirent_local_style(src_abspath,
scratch_pool));
}
if (is_move && src_repos_relpath && !src_repos_relpath[0])
{
return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
_("'%s' represents the repository root "
"and cannot be moved"),
svn_dirent_local_style(src_abspath,
scratch_pool));
}
err = svn_wc__db_read_info(&dstdir_status, NULL, NULL, NULL,
&dst_repos_root_url, &dst_repos_uuid, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL,
db, dstdir_abspath,
scratch_pool, scratch_pool);
if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
{
/* An unversioned destination directory exists on disk. */
svn_error_clear(err);
return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
_("'%s' is not under version control"),
svn_dirent_local_style(dstdir_abspath,
scratch_pool));
}
else
SVN_ERR(err);
/* Do this now, as we know the right data is cached */
SVN_ERR(svn_wc__db_get_wcroot(&dst_wcroot_abspath, db, dstdir_abspath,
scratch_pool, scratch_pool));
if (!src_repos_root_url)
{
if (src_status == svn_wc__db_status_added)
SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL,
&src_repos_root_url,
&src_repos_uuid, NULL, NULL, NULL,
NULL,
db, src_abspath,
scratch_pool, scratch_pool));
else
/* If not added, the node must have a base or we can't copy */
SVN_ERR(svn_wc__db_scan_base_repos(NULL, &src_repos_root_url,
&src_repos_uuid,
db, src_abspath,
scratch_pool, scratch_pool));
}
if (!dst_repos_root_url)
{
if (dstdir_status == svn_wc__db_status_added)
SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL,
&dst_repos_root_url,
&dst_repos_uuid, NULL, NULL, NULL,
NULL,
db, dstdir_abspath,
scratch_pool, scratch_pool));
else
/* If not added, the node must have a base or we can't copy */
SVN_ERR(svn_wc__db_scan_base_repos(NULL, &dst_repos_root_url,
&dst_repos_uuid,
db, dstdir_abspath,
scratch_pool, scratch_pool));
}
if (strcmp(src_repos_root_url, dst_repos_root_url) != 0
|| strcmp(src_repos_uuid, dst_repos_uuid) != 0)
return svn_error_createf(
SVN_ERR_WC_INVALID_SCHEDULE, NULL,
_("Cannot copy to '%s', as it is not from repository '%s'; "
"it is from '%s'"),
svn_dirent_local_style(dst_abspath, scratch_pool),
src_repos_root_url, dst_repos_root_url);
if (dstdir_status == svn_wc__db_status_deleted)
return svn_error_createf(
SVN_ERR_WC_INVALID_SCHEDULE, NULL,
_("Cannot copy to '%s' as it is scheduled for deletion"),
svn_dirent_local_style(dst_abspath, scratch_pool));
/* ### should report dstdir_abspath instead of dst_abspath? */
}
/* TODO(#2843): Rework the error report. */
/* Check if the copy target is missing or hidden and thus not exist on the
disk, before actually doing the file copy. */
{
svn_wc__db_status_t dst_status;
err = svn_wc__db_read_info(&dst_status, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
db, dst_abspath, scratch_pool, scratch_pool);
if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
svn_error_clear(err);
if (!err)
switch (dst_status)
{
case svn_wc__db_status_excluded:
return svn_error_createf(
SVN_ERR_ENTRY_EXISTS, NULL,
_("'%s' is already under version control "
"but is excluded."),
svn_dirent_local_style(dst_abspath, scratch_pool));
case svn_wc__db_status_server_excluded:
return svn_error_createf(
SVN_ERR_ENTRY_EXISTS, NULL,
_("'%s' is already under version control"),
svn_dirent_local_style(dst_abspath, scratch_pool));
case svn_wc__db_status_deleted:
case svn_wc__db_status_not_present:
break; /* OK to add */
default:
return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,
_("There is already a versioned item '%s'"),
svn_dirent_local_style(dst_abspath,
scratch_pool));
}
}
/* Check that the target path is not obstructed, if required. */
if (!metadata_only)
{
svn_node_kind_t dst_kind;
/* (We need only to check the root of the copy, not every path inside
copy_versioned_file/_dir.) */
SVN_ERR(svn_io_check_path(dst_abspath, &dst_kind, scratch_pool));
if (dst_kind != svn_node_none)
return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,
_("'%s' already exists and is in the way"),
svn_dirent_local_style(dst_abspath,
scratch_pool));
}
SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmpdir_abspath, db,
dstdir_abspath,
scratch_pool, scratch_pool));
within_one_wc = (strcmp(src_wcroot_abspath, dst_wcroot_abspath) == 0);
if (is_move
&& !within_one_wc)
{
if (move_degraded_to_copy)
*move_degraded_to_copy = TRUE;
is_move = FALSE;
}
if (!within_one_wc)
SVN_ERR(svn_wc__db_pristine_transfer(db, src_abspath, dst_wcroot_abspath,
cancel_func, cancel_baton,
scratch_pool));
if (src_db_kind == svn_node_file
|| src_db_kind == svn_node_symlink)
{
err = copy_versioned_file(db, src_abspath, dst_abspath, dst_abspath,
tmpdir_abspath,
metadata_only, conflicted, is_move,
cancel_func, cancel_baton,
notify_func, notify_baton,
scratch_pool);
}
else
{
if (is_move
&& src_status == svn_wc__db_status_normal)
{
svn_revnum_t min_rev;
svn_revnum_t max_rev;
/* Verify that the move source is a single-revision subtree. */
SVN_ERR(svn_wc__db_min_max_revisions(&min_rev, &max_rev, db,
src_abspath, FALSE, scratch_pool));
if (SVN_IS_VALID_REVNUM(min_rev) && SVN_IS_VALID_REVNUM(max_rev) &&
min_rev != max_rev)
{
if (!allow_mixed_revisions)
return svn_error_createf(SVN_ERR_WC_MIXED_REVISIONS, NULL,
_("Cannot move mixed-revision "
"subtree '%s' [%ld:%ld]; "
"try updating it first"),
svn_dirent_local_style(src_abspath,
scratch_pool),
min_rev, max_rev);
is_move = FALSE;
if (move_degraded_to_copy)
*move_degraded_to_copy = TRUE;
}
}
err = copy_versioned_dir(db, src_abspath, dst_abspath, dst_abspath,
tmpdir_abspath, metadata_only, is_move,
cancel_func, cancel_baton,
notify_func, notify_baton,
scratch_pool);
}
if (err && svn_error_find_cause(err, SVN_ERR_CANCELLED))
return svn_error_trace(err);
if (is_move)
err = svn_error_compose_create(err,
svn_wc__db_op_handle_move_back(NULL,
db, dst_abspath, src_abspath,
NULL /* work_items */,
scratch_pool));
/* Run the work queue with the remaining work */
SVN_ERR(svn_error_compose_create(
err,
svn_wc__wq_run(db, dst_abspath,
cancel_func, cancel_baton,
scratch_pool)));
return SVN_NO_ERROR;
}
/* Public Interface */
svn_error_t *
svn_wc_copy3(svn_wc_context_t *wc_ctx,
const char *src_abspath,
const char *dst_abspath,
svn_boolean_t metadata_only,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool)
{
/* Verify that we have the required write lock. */
SVN_ERR(svn_wc__write_check(wc_ctx->db,
svn_dirent_dirname(dst_abspath, scratch_pool),
scratch_pool));
return svn_error_trace(copy_or_move(NULL, wc_ctx, src_abspath, dst_abspath,
metadata_only, FALSE /* is_move */,
TRUE /* allow_mixed_revisions */,
cancel_func, cancel_baton,
notify_func, notify_baton,
scratch_pool));
}
/* Remove the conflict markers of NODE_ABSPATH, that were left over after
copying NODE_ABSPATH from SRC_ABSPATH.
Only use this function when you know what you're doing. This function
explicitly ignores some case insensitivity issues!
*/
static svn_error_t *
remove_node_conflict_markers(svn_wc__db_t *db,
const char *src_abspath,
const char *node_abspath,
apr_pool_t *scratch_pool)
{
svn_skel_t *conflict;
SVN_ERR(svn_wc__db_read_conflict(&conflict, db, src_abspath,
scratch_pool, scratch_pool));
/* Do we have conflict markers that should be removed? */
if (conflict != NULL)
{
const apr_array_header_t *markers;
int i;
const char *src_dir = svn_dirent_dirname(src_abspath, scratch_pool);
const char *dst_dir = svn_dirent_dirname(node_abspath, scratch_pool);
SVN_ERR(svn_wc__conflict_read_markers(&markers, db, src_abspath,
conflict,
scratch_pool, scratch_pool));
/* No iterpool: Maximum number of possible conflict markers is 4 */
for (i = 0; markers && (i < markers->nelts); i++)
{
const char *marker_abspath;
const char *child_relpath;
- const char *child_abpath;
+ const char *child_abspath;
marker_abspath = APR_ARRAY_IDX(markers, i, const char *);
- child_relpath = svn_dirent_is_child(src_dir, marker_abspath, NULL);
+ child_relpath = svn_dirent_skip_ancestor(src_dir, marker_abspath);
if (child_relpath)
{
- child_abpath = svn_dirent_join(dst_dir, child_relpath,
- scratch_pool);
+ child_abspath = svn_dirent_join(dst_dir, child_relpath,
+ scratch_pool);
- SVN_ERR(svn_io_remove_file2(child_abpath, TRUE, scratch_pool));
+ SVN_ERR(svn_io_remove_file2(child_abspath, TRUE, scratch_pool));
}
}
}
return SVN_NO_ERROR;
}
/* Remove all the conflict markers below SRC_DIR_ABSPATH, that were left over
after copying WC_DIR_ABSPATH from SRC_DIR_ABSPATH.
This function doesn't remove the conflict markers on WC_DIR_ABSPATH
itself!
Only use this function when you know what you're doing. This function
explicitly ignores some case insensitivity issues!
*/
static svn_error_t *
remove_all_conflict_markers(svn_wc__db_t *db,
const char *src_dir_abspath,
- const char *wc_dir_abspath,
+ const char *dst_dir_abspath,
apr_pool_t *scratch_pool)
{
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
apr_hash_t *nodes;
apr_hash_t *conflicts; /* Unused */
apr_hash_index_t *hi;
/* Reuse a status helper to obtain all subdirs and conflicts in a single
db transaction. */
/* ### This uses a rifle to kill a fly. But at least it doesn't use heavy
artillery. */
SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts, db,
src_dir_abspath,
scratch_pool, iterpool));
for (hi = apr_hash_first(scratch_pool, nodes);
hi;
hi = apr_hash_next(hi))
{
const char *name = svn__apr_hash_index_key(hi);
struct svn_wc__db_info_t *info = svn__apr_hash_index_val(hi);
if (info->conflicted)
{
svn_pool_clear(iterpool);
SVN_ERR(remove_node_conflict_markers(
db,
svn_dirent_join(src_dir_abspath, name, iterpool),
- svn_dirent_join(wc_dir_abspath, name, iterpool),
+ svn_dirent_join(dst_dir_abspath, name, iterpool),
iterpool));
}
if (info->kind == svn_node_dir)
{
svn_pool_clear(iterpool);
SVN_ERR(remove_all_conflict_markers(
db,
svn_dirent_join(src_dir_abspath, name, iterpool),
- svn_dirent_join(wc_dir_abspath, name, iterpool),
+ svn_dirent_join(dst_dir_abspath, name, iterpool),
iterpool));
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__move2(svn_wc_context_t *wc_ctx,
const char *src_abspath,
const char *dst_abspath,
svn_boolean_t metadata_only,
svn_boolean_t allow_mixed_revisions,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool)
{
svn_wc__db_t *db = wc_ctx->db;
svn_boolean_t move_degraded_to_copy = FALSE;
svn_node_kind_t kind;
svn_boolean_t conflicted;
/* Verify that we have the required write locks. */
SVN_ERR(svn_wc__write_check(wc_ctx->db,
svn_dirent_dirname(src_abspath, scratch_pool),
scratch_pool));
SVN_ERR(svn_wc__write_check(wc_ctx->db,
svn_dirent_dirname(dst_abspath, scratch_pool),
scratch_pool));
SVN_ERR(copy_or_move(&move_degraded_to_copy,
wc_ctx, src_abspath, dst_abspath,
TRUE /* metadata_only */,
TRUE /* is_move */,
allow_mixed_revisions,
cancel_func, cancel_baton,
notify_func, notify_baton,
scratch_pool));
/* An interrupt at this point will leave the new copy marked as
moved-here but the source has not yet been deleted or marked as
moved-to. */
/* Should we be using a workqueue for this move? It's not clear.
What should happen if the copy above is interrupted? The user
may want to abort the move and a workqueue might interfere with
that.
BH: On Windows it is not unlikely to encounter an access denied on
this line. Installing the move in the workqueue via the copy_or_move
might make it hard to recover from that situation, while the DB
is still in a valid state. So be careful when switching this over
to the workqueue. */
if (!metadata_only)
SVN_ERR(svn_io_file_rename(src_abspath, dst_abspath, scratch_pool));
SVN_ERR(svn_wc__db_read_info(NULL, &kind, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
&conflicted, NULL, NULL, NULL,
NULL, NULL, NULL,
db, src_abspath,
scratch_pool, scratch_pool));
if (kind == svn_node_dir)
SVN_ERR(remove_all_conflict_markers(db, src_abspath, dst_abspath,
scratch_pool));
if (conflicted)
- SVN_ERR(remove_node_conflict_markers(db, src_abspath, dst_abspath,
- scratch_pool));
+ {
+ /* When we moved a directory, we moved the conflict markers
+ with the target... if we moved a file we only moved the
+ file itself and the markers are still in the old location */
+ SVN_ERR(remove_node_conflict_markers(db, src_abspath,
+ (kind == svn_node_dir)
+ ? dst_abspath
+ : src_abspath,
+ scratch_pool));
+ }
SVN_ERR(svn_wc__db_op_delete(db, src_abspath,
move_degraded_to_copy ? NULL : dst_abspath,
TRUE /* delete_dir_externals */,
NULL /* conflict */, NULL /* work_items */,
cancel_func, cancel_baton,
notify_func, notify_baton,
scratch_pool));
return SVN_NO_ERROR;
}
Index: vendor/subversion/dist/subversion/libsvn_wc/diff.h
===================================================================
--- vendor/subversion/dist/subversion/libsvn_wc/diff.h (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_wc/diff.h (revision 286501)
@@ -1,148 +1,165 @@
/*
* lock.h: routines for diffing local files and directories.
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#ifndef SVN_LIBSVN_WC_DIFF_H
#define SVN_LIBSVN_WC_DIFF_H
#include <apr_pools.h>
#include <apr_hash.h>
#include "svn_types.h"
#include "svn_error.h"
#include "svn_wc.h"
#include "wc_db.h"
#include "private/svn_diff_tree.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* A function to diff locally added and locally copied files.
Reports the file LOCAL_ABSPATH as ADDED file with relpath RELPATH to
PROCESSOR with as parent baton PROCESSOR_PARENT_BATON.
The node is expected to have status svn_wc__db_status_normal, or
svn_wc__db_status_added. When DIFF_PRISTINE is TRUE, report the pristine
version of LOCAL_ABSPATH as ADDED. In this case an
svn_wc__db_status_deleted may shadow an added or deleted node.
-
- If CHANGELIST_HASH is not NULL and LOCAL_ABSPATH's changelist is not
- in the changelist, don't report the node.
*/
svn_error_t *
svn_wc__diff_local_only_file(svn_wc__db_t *db,
const char *local_abspath,
const char *relpath,
const svn_diff_tree_processor_t *processor,
void *processor_parent_baton,
- apr_hash_t *changelist_hash,
svn_boolean_t diff_pristine,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool);
/* A function to diff locally added and locally copied directories.
Reports the directory LOCAL_ABSPATH and everything below it (limited by
DEPTH) as added with relpath RELPATH to PROCESSOR with as parent baton
PROCESSOR_PARENT_BATON.
The node is expected to have status svn_wc__db_status_normal, or
svn_wc__db_status_added. When DIFF_PRISTINE is TRUE, report the pristine
version of LOCAL_ABSPATH as ADDED. In this case an
svn_wc__db_status_deleted may shadow an added or deleted node.
-
- If CHANGELIST_HASH is not NULL and LOCAL_ABSPATH's changelist is not
- in the changelist, don't report the node.
*/
svn_error_t *
svn_wc__diff_local_only_dir(svn_wc__db_t *db,
const char *local_abspath,
const char *relpath,
svn_depth_t depth,
const svn_diff_tree_processor_t *processor,
void *processor_parent_baton,
- apr_hash_t *changelist_hash,
svn_boolean_t diff_pristine,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool);
/* Reports the BASE-file LOCAL_ABSPATH as deleted to PROCESSOR with relpath
RELPATH, revision REVISION and parent baton PROCESSOR_PARENT_BATON.
If REVISION is invalid, the revision as stored in BASE is used.
The node is expected to have status svn_wc__db_status_normal in BASE. */
svn_error_t *
svn_wc__diff_base_only_file(svn_wc__db_t *db,
const char *local_abspath,
const char *relpath,
svn_revnum_t revision,
const svn_diff_tree_processor_t *processor,
void *processor_parent_baton,
apr_pool_t *scratch_pool);
/* Reports the BASE-directory LOCAL_ABSPATH and everything below it (limited
by DEPTH) as deleted to PROCESSOR with relpath RELPATH and parent baton
PROCESSOR_PARENT_BATON.
If REVISION is invalid, the revision as stored in BASE is used.
The node is expected to have status svn_wc__db_status_normal in BASE. */
svn_error_t *
svn_wc__diff_base_only_dir(svn_wc__db_t *db,
const char *local_abspath,
const char *relpath,
svn_revnum_t revision,
svn_depth_t depth,
const svn_diff_tree_processor_t *processor,
void *processor_parent_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool);
/* Diff the file PATH against the text base of its BASE layer. At this
* stage we are dealing with a file that does exist in the working copy.
*/
svn_error_t *
svn_wc__diff_base_working_diff(svn_wc__db_t *db,
const char *local_abspath,
const char *relpath,
svn_revnum_t revision,
- apr_hash_t *changelist_hash,
const svn_diff_tree_processor_t *processor,
void *processor_dir_baton,
svn_boolean_t diff_pristine,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool);
+
+/* Return a tree processor filter that filters by changelist membership.
+ *
+ * This filter only passes on the changes for a file if the file's path
+ * (in the WC) is assigned to one of the changelists in @a changelist_hash.
+ * It also passes on the opening and closing of each directory that contains
+ * such a change, and possibly also of other directories, but not addition
+ * or deletion or changes to a directory.
+ *
+ * If @a changelist_hash is null then no filtering is performed and the
+ * returned diff processor is driven exactly like the input @a processor.
+ *
+ * @a wc_ctx is the WC context and @a root_local_abspath is the WC path of
+ * the root of the diff (for which relpath = "" in the diff processor).
+ *
+ * Allocate the returned diff processor in @a result_pool, or if no
+ * filtering is required then the input pointer @a processor itself may be
+ * returned.
+ */
+const svn_diff_tree_processor_t *
+svn_wc__changelist_filter_tree_processor_create(
+ const svn_diff_tree_processor_t *processor,
+ svn_wc_context_t *wc_ctx,
+ const char *root_local_abspath,
+ apr_hash_t *changelist_hash,
+ apr_pool_t *result_pool);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* SVN_LIBSVN_WC_DIFF_H */
Index: vendor/subversion/dist/subversion/libsvn_wc/diff_editor.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_wc/diff_editor.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_wc/diff_editor.c (revision 286501)
@@ -1,2751 +1,3100 @@
/*
* diff_editor.c -- The diff editor for comparing the working copy against the
* repository.
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
/*
* This code uses an svn_delta_editor_t editor driven by
* svn_wc_crawl_revisions (like the update command) to retrieve the
* differences between the working copy and the requested repository
* version. Rather than updating the working copy, this new editor creates
* temporary files that contain the pristine repository versions. When the
* crawler closes the files the editor calls back to a client layer
* function to compare the working copy and the temporary file. There is
* only ever one temporary file in existence at any time.
*
* When the crawler closes a directory, the editor then calls back to the
* client layer to compare any remaining files that may have been modified
* locally. Added directories do not have corresponding temporary
* directories created, as they are not needed.
*
* The diff result from this editor is a combination of the restructuring
* operations from the repository with the local restructurings since checking
* out.
*
* ### TODO: Make sure that we properly support and report multi layered
* operations instead of only simple file replacements.
*
* ### TODO: Replacements where the node kind changes needs support. It
* mostly works when the change is in the repository, but not when it is
* in the working copy.
*
* ### TODO: Do we need to support copyfrom?
*
*/
#include <apr_hash.h>
#include <apr_md5.h>
#include <assert.h>
#include "svn_error.h"
#include "svn_pools.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_hash.h"
#include "svn_sorts.h"
#include "private/svn_subr_private.h"
#include "private/svn_wc_private.h"
#include "private/svn_diff_tree.h"
#include "private/svn_editor.h"
#include "wc.h"
#include "props.h"
#include "adm_files.h"
#include "translate.h"
#include "diff.h"
#include "svn_private_config.h"
/*-------------------------------------------------------------------------*/
/* Overall crawler editor baton.
*/
struct edit_baton_t
{
/* A wc db. */
svn_wc__db_t *db;
/* A diff tree processor, receiving the result of the diff. */
const svn_diff_tree_processor_t *processor;
/* A boolean indicating whether local additions should be reported before
remote deletes. The processor can transform adds in deletes and deletes
in adds, but it can't reorder the output. */
svn_boolean_t local_before_remote;
/* ANCHOR/TARGET represent the base of the hierarchy to be compared. */
const char *target;
const char *anchor_abspath;
/* Target revision */
svn_revnum_t revnum;
/* Was the root opened? */
svn_boolean_t root_opened;
/* How does this diff descend as seen from target? */
svn_depth_t depth;
/* Should this diff ignore node ancestry? */
svn_boolean_t ignore_ancestry;
/* Possibly diff repos against text-bases instead of working files. */
svn_boolean_t diff_pristine;
- /* Hash whose keys are const char * changelist names. */
- apr_hash_t *changelist_hash;
-
/* Cancel function/baton */
svn_cancel_func_t cancel_func;
void *cancel_baton;
apr_pool_t *pool;
};
/* Directory level baton.
*/
struct dir_baton_t
{
/* Reference to parent directory baton (or NULL for the root) */
struct dir_baton_t *parent_baton;
/* The depth at which this directory should be diffed. */
svn_depth_t depth;
/* The name and path of this directory as if they would be/are in the
local working copy. */
const char *name;
const char *relpath;
const char *local_abspath;
/* TRUE if the file is added by the editor drive. */
svn_boolean_t added;
/* TRUE if the node exists only on the repository side (op_depth 0 or added) */
svn_boolean_t repos_only;
/* TRUE if the node is to be compared with an unrelated node*/
svn_boolean_t ignoring_ancestry;
/* Processor state */
void *pdb;
svn_boolean_t skip;
svn_boolean_t skip_children;
svn_diff_source_t *left_src;
svn_diff_source_t *right_src;
apr_hash_t *local_info;
/* A hash containing the basenames of the nodes reported deleted by the
repository (or NULL for no values). */
apr_hash_t *deletes;
/* Identifies those directory elements that get compared while running
the crawler. These elements should not be compared again when
recursively looking for local modifications.
This hash maps the basename of the node to an unimportant value.
If the directory's properties have been compared, an item with hash
key of "" will be present in the hash. */
apr_hash_t *compared;
/* The list of incoming BASE->repos propchanges. */
apr_array_header_t *propchanges;
/* Has a change on regular properties */
svn_boolean_t has_propchange;
/* The overall crawler editor baton. */
struct edit_baton_t *eb;
apr_pool_t *pool;
int users;
};
/* File level baton.
*/
struct file_baton_t
{
struct dir_baton_t *parent_baton;
/* The name and path of this file as if they would be/are in the
parent directory, diff session and local working copy. */
const char *name;
const char *relpath;
const char *local_abspath;
/* Processor state */
void *pfb;
svn_boolean_t skip;
/* TRUE if the file is added by the editor drive. */
svn_boolean_t added;
/* TRUE if the node exists only on the repository side (op_depth 0 or added) */
svn_boolean_t repos_only;
/* TRUE if the node is to be compared with an unrelated node*/
svn_boolean_t ignoring_ancestry;
const svn_diff_source_t *left_src;
const svn_diff_source_t *right_src;
/* The list of incoming BASE->repos propchanges. */
apr_array_header_t *propchanges;
/* Has a change on regular properties */
svn_boolean_t has_propchange;
/* The current BASE checksum and props */
const svn_checksum_t *base_checksum;
apr_hash_t *base_props;
/* The resulting from apply_textdelta */
const char *temp_file_path;
unsigned char result_digest[APR_MD5_DIGESTSIZE];
/* The overall crawler editor baton. */
struct edit_baton_t *eb;
apr_pool_t *pool;
};
/* Create a new edit baton. TARGET_PATH/ANCHOR are working copy paths
* that describe the root of the comparison. CALLBACKS/CALLBACK_BATON
* define the callbacks to compare files. DEPTH defines if and how to
* descend into subdirectories; see public doc string for exactly how.
* IGNORE_ANCESTRY defines whether to utilize node ancestry when
* calculating diffs. USE_TEXT_BASE defines whether to compare
* against working files or text-bases. REVERSE_ORDER defines which
* direction to perform the diff.
- *
- * CHANGELIST_FILTER is a list of const char * changelist names, used to
- * filter diff output responses to only those items in one of the
- * specified changelists, empty (or NULL altogether) if no changelist
- * filtering is requested.
*/
static svn_error_t *
make_edit_baton(struct edit_baton_t **edit_baton,
svn_wc__db_t *db,
const char *anchor_abspath,
const char *target,
- const svn_wc_diff_callbacks4_t *callbacks,
- void *callback_baton,
+ const svn_diff_tree_processor_t *processor,
svn_depth_t depth,
svn_boolean_t ignore_ancestry,
svn_boolean_t show_copies_as_adds,
svn_boolean_t use_text_base,
svn_boolean_t reverse_order,
- const apr_array_header_t *changelist_filter,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
- apr_hash_t *changelist_hash = NULL;
struct edit_baton_t *eb;
- const svn_diff_tree_processor_t *processor;
SVN_ERR_ASSERT(svn_dirent_is_absolute(anchor_abspath));
- if (changelist_filter && changelist_filter->nelts)
- SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelist_filter,
- pool));
-
- SVN_ERR(svn_wc__wrap_diff_callbacks(&processor,
- callbacks, callback_baton, TRUE,
- pool, pool));
-
if (reverse_order)
processor = svn_diff__tree_processor_reverse_create(processor, NULL, pool);
/* --show-copies-as-adds implies --notice-ancestry */
if (show_copies_as_adds)
ignore_ancestry = FALSE;
if (! show_copies_as_adds)
processor = svn_diff__tree_processor_copy_as_changed_create(processor,
pool);
eb = apr_pcalloc(pool, sizeof(*eb));
eb->db = db;
eb->anchor_abspath = apr_pstrdup(pool, anchor_abspath);
eb->target = apr_pstrdup(pool, target);
eb->processor = processor;
eb->depth = depth;
eb->ignore_ancestry = ignore_ancestry;
eb->local_before_remote = reverse_order;
eb->diff_pristine = use_text_base;
- eb->changelist_hash = changelist_hash;
eb->cancel_func = cancel_func;
eb->cancel_baton = cancel_baton;
eb->pool = pool;
*edit_baton = eb;
return SVN_NO_ERROR;
}
/* Create a new directory baton. PATH is the directory path,
* including anchor_path. ADDED is set if this directory is being
* added rather than replaced. PARENT_BATON is the baton of the
* parent directory, it will be null if this is the root of the
* comparison hierarchy. The directory and its parent may or may not
* exist in the working copy. EDIT_BATON is the overall crawler
* editor baton.
*/
static struct dir_baton_t *
make_dir_baton(const char *path,
struct dir_baton_t *parent_baton,
struct edit_baton_t *eb,
svn_boolean_t added,
svn_depth_t depth,
apr_pool_t *result_pool)
{
apr_pool_t *dir_pool = svn_pool_create(parent_baton ? parent_baton->pool
: eb->pool);
struct dir_baton_t *db = apr_pcalloc(dir_pool, sizeof(*db));
db->parent_baton = parent_baton;
/* Allocate 1 string for using as 3 strings */
db->local_abspath = svn_dirent_join(eb->anchor_abspath, path, dir_pool);
db->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, db->local_abspath);
db->name = svn_dirent_basename(db->relpath, NULL);
db->eb = eb;
db->added = added;
db->depth = depth;
db->pool = dir_pool;
db->propchanges = apr_array_make(dir_pool, 8, sizeof(svn_prop_t));
db->compared = apr_hash_make(dir_pool);
if (parent_baton != NULL)
{
parent_baton->users++;
}
db->users = 1;
return db;
}
/* Create a new file baton. PATH is the file path, including
* anchor_path. ADDED is set if this file is being added rather than
* replaced. PARENT_BATON is the baton of the parent directory.
* The directory and its parent may or may not exist in the working copy.
*/
static struct file_baton_t *
make_file_baton(const char *path,
svn_boolean_t added,
struct dir_baton_t *parent_baton,
apr_pool_t *result_pool)
{
apr_pool_t *file_pool = svn_pool_create(result_pool);
struct file_baton_t *fb = apr_pcalloc(file_pool, sizeof(*fb));
struct edit_baton_t *eb = parent_baton->eb;
fb->eb = eb;
fb->parent_baton = parent_baton;
fb->parent_baton->users++;
/* Allocate 1 string for using as 3 strings */
fb->local_abspath = svn_dirent_join(eb->anchor_abspath, path, file_pool);
fb->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, fb->local_abspath);
fb->name = svn_dirent_basename(fb->relpath, NULL);
fb->added = added;
fb->pool = file_pool;
fb->propchanges = apr_array_make(file_pool, 8, sizeof(svn_prop_t));
return fb;
}
/* Destroy DB when there are no more registered users */
static svn_error_t *
maybe_done(struct dir_baton_t *db)
{
db->users--;
if (!db->users)
{
struct dir_baton_t *pb = db->parent_baton;
svn_pool_clear(db->pool);
if (pb != NULL)
SVN_ERR(maybe_done(pb));
}
return SVN_NO_ERROR;
}
/* Standard check to see if a node is represented in the local working copy */
#define NOT_PRESENT(status) \
((status) == svn_wc__db_status_not_present \
|| (status) == svn_wc__db_status_excluded \
|| (status) == svn_wc__db_status_server_excluded)
svn_error_t *
svn_wc__diff_base_working_diff(svn_wc__db_t *db,
const char *local_abspath,
const char *relpath,
svn_revnum_t revision,
- apr_hash_t *changelist_hash,
const svn_diff_tree_processor_t *processor,
void *processor_dir_baton,
svn_boolean_t diff_pristine,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
void *file_baton = NULL;
svn_boolean_t skip = FALSE;
svn_wc__db_status_t status;
svn_revnum_t db_revision;
svn_boolean_t had_props;
svn_boolean_t props_mod;
svn_boolean_t files_same = FALSE;
svn_wc__db_status_t base_status;
const svn_checksum_t *working_checksum;
const svn_checksum_t *checksum;
svn_filesize_t recorded_size;
apr_time_t recorded_time;
const char *pristine_file;
const char *local_file;
svn_diff_source_t *left_src;
svn_diff_source_t *right_src;
apr_hash_t *base_props;
apr_hash_t *local_props;
apr_array_header_t *prop_changes;
- const char *changelist;
SVN_ERR(svn_wc__db_read_info(&status, NULL, &db_revision, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, &working_checksum, NULL,
NULL, NULL, NULL, NULL, NULL, &recorded_size,
- &recorded_time, &changelist, NULL, NULL,
+ &recorded_time, NULL, NULL, NULL,
&had_props, &props_mod, NULL, NULL, NULL,
db, local_abspath, scratch_pool, scratch_pool));
checksum = working_checksum;
assert(status == svn_wc__db_status_normal
|| status == svn_wc__db_status_added
|| (status == svn_wc__db_status_deleted && diff_pristine));
- /* If the item is not a member of a specified changelist (and there are
- some specified changelists), skip it. */
- if (changelist_hash && !svn_hash_gets(changelist_hash, changelist))
- return SVN_NO_ERROR;
-
-
if (status != svn_wc__db_status_normal)
{
SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db_revision,
NULL, NULL, NULL, NULL, NULL, NULL,
NULL, &checksum, NULL, NULL, &had_props,
NULL, NULL,
db, local_abspath,
scratch_pool, scratch_pool));
recorded_size = SVN_INVALID_FILESIZE;
recorded_time = 0;
props_mod = TRUE; /* Requires compare */
}
else if (diff_pristine)
files_same = TRUE;
else
{
const svn_io_dirent2_t *dirent;
/* Verify truename to mimic status for iota/IOTA difference on Windows */
SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath,
TRUE /* verify truename */,
TRUE /* ingore_enoent */,
scratch_pool, scratch_pool));
/* If a file does not exist on disk (missing/obstructed) then we
can't provide a text diff */
if (dirent->kind != svn_node_file
|| (dirent->kind == svn_node_file
&& dirent->filesize == recorded_size
&& dirent->mtime == recorded_time))
{
files_same = TRUE;
}
}
if (files_same && !props_mod)
return SVN_NO_ERROR; /* Cheap exit */
assert(checksum);
if (!SVN_IS_VALID_REVNUM(revision))
revision = db_revision;
left_src = svn_diff__source_create(revision, scratch_pool);
right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
SVN_ERR(processor->file_opened(&file_baton, &skip, relpath,
left_src,
right_src,
NULL /* copyfrom_src */,
processor_dir_baton,
processor,
scratch_pool, scratch_pool));
if (skip)
return SVN_NO_ERROR;
SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file,
db, local_abspath, checksum,
scratch_pool, scratch_pool));
if (diff_pristine)
SVN_ERR(svn_wc__db_pristine_get_path(&local_file,
db, local_abspath,
working_checksum,
scratch_pool, scratch_pool));
else if (! (had_props || props_mod))
local_file = local_abspath;
else if (files_same)
local_file = pristine_file;
else
SVN_ERR(svn_wc__internal_translated_file(
&local_file, local_abspath,
db, local_abspath,
SVN_WC_TRANSLATE_TO_NF
| SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
cancel_func, cancel_baton,
scratch_pool, scratch_pool));
if (! files_same)
SVN_ERR(svn_io_files_contents_same_p(&files_same, local_file,
pristine_file, scratch_pool));
if (had_props)
SVN_ERR(svn_wc__db_base_get_props(&base_props, db, local_abspath,
scratch_pool, scratch_pool));
else
base_props = apr_hash_make(scratch_pool);
if (status == svn_wc__db_status_normal && (diff_pristine || !props_mod))
local_props = base_props;
else if (diff_pristine)
SVN_ERR(svn_wc__db_read_pristine_props(&local_props, db, local_abspath,
scratch_pool, scratch_pool));
else
SVN_ERR(svn_wc__db_read_props(&local_props, db, local_abspath,
scratch_pool, scratch_pool));
SVN_ERR(svn_prop_diffs(&prop_changes, local_props, base_props, scratch_pool));
if (prop_changes->nelts || !files_same)
{
SVN_ERR(processor->file_changed(relpath,
left_src,
right_src,
pristine_file,
local_file,
base_props,
local_props,
! files_same,
prop_changes,
file_baton,
processor,
scratch_pool));
}
else
{
SVN_ERR(processor->file_closed(relpath,
left_src,
right_src,
file_baton,
processor,
scratch_pool));
}
return SVN_NO_ERROR;
}
static svn_error_t *
ensure_local_info(struct dir_baton_t *db,
apr_pool_t *scratch_pool)
{
apr_hash_t *conflicts;
if (db->local_info)
return SVN_NO_ERROR;
SVN_ERR(svn_wc__db_read_children_info(&db->local_info, &conflicts,
db->eb->db, db->local_abspath,
db->pool, scratch_pool));
return SVN_NO_ERROR;
}
/* Called when the directory is closed to compare any elements that have
* not yet been compared. This identifies local, working copy only
* changes. At this stage we are dealing with files/directories that do
* exist in the working copy.
*
* DIR_BATON is the baton for the directory.
*/
static svn_error_t *
walk_local_nodes_diff(struct edit_baton_t *eb,
const char *local_abspath,
const char *path,
svn_depth_t depth,
apr_hash_t *compared,
void *parent_baton,
apr_pool_t *scratch_pool)
{
svn_wc__db_t *db = eb->db;
svn_boolean_t in_anchor_not_target;
apr_pool_t *iterpool;
void *dir_baton = NULL;
svn_boolean_t skip = FALSE;
svn_boolean_t skip_children = FALSE;
svn_revnum_t revision;
svn_boolean_t props_mod;
svn_diff_source_t *left_src;
svn_diff_source_t *right_src;
/* Everything we do below is useless if we are comparing to BASE. */
if (eb->diff_pristine)
return SVN_NO_ERROR;
/* Determine if this is the anchor directory if the anchor is different
to the target. When the target is a file, the anchor is the parent
directory and if this is that directory the non-target entries must be
skipped. */
in_anchor_not_target = ((*path == '\0') && (*eb->target != '\0'));
iterpool = svn_pool_create(scratch_pool);
SVN_ERR(svn_wc__db_read_info(NULL, NULL, &revision, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, &props_mod, NULL, NULL, NULL,
db, local_abspath, scratch_pool, scratch_pool));
left_src = svn_diff__source_create(revision, scratch_pool);
right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
if (compared)
{
dir_baton = parent_baton;
skip = TRUE;
}
else if (!in_anchor_not_target)
SVN_ERR(eb->processor->dir_opened(&dir_baton, &skip, &skip_children,
path,
left_src,
right_src,
NULL /* copyfrom_src */,
parent_baton,
eb->processor,
scratch_pool, scratch_pool));
if (!skip_children && depth != svn_depth_empty)
{
apr_hash_t *nodes;
apr_hash_t *conflicts;
apr_array_header_t *children;
svn_depth_t depth_below_here = depth;
svn_boolean_t diff_files;
svn_boolean_t diff_dirs;
int i;
if (depth_below_here == svn_depth_immediates)
depth_below_here = svn_depth_empty;
diff_files = (depth == svn_depth_unknown
|| depth >= svn_depth_files);
diff_dirs = (depth == svn_depth_unknown
|| depth >= svn_depth_immediates);
SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts,
db, local_abspath,
scratch_pool, iterpool));
children = svn_sort__hash(nodes, svn_sort_compare_items_lexically,
scratch_pool);
for (i = 0; i < children->nelts; i++)
{
svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
svn_sort__item_t);
const char *name = item->key;
struct svn_wc__db_info_t *info = item->value;
const char *child_abspath;
const char *child_relpath;
svn_boolean_t repos_only;
svn_boolean_t local_only;
svn_node_kind_t base_kind;
if (eb->cancel_func)
SVN_ERR(eb->cancel_func(eb->cancel_baton));
/* In the anchor directory, if the anchor is not the target then all
entries other than the target should not be diff'd. Running diff
on one file in a directory should not diff other files in that
directory. */
if (in_anchor_not_target && strcmp(eb->target, name))
continue;
if (compared && svn_hash_gets(compared, name))
continue;
if (NOT_PRESENT(info->status))
continue;
assert(info->status == svn_wc__db_status_normal
|| info->status == svn_wc__db_status_added
|| info->status == svn_wc__db_status_deleted);
svn_pool_clear(iterpool);
child_abspath = svn_dirent_join(local_abspath, name, iterpool);
child_relpath = svn_relpath_join(path, name, iterpool);
repos_only = FALSE;
local_only = FALSE;
if (!info->have_base)
{
local_only = TRUE; /* Only report additions */
}
else if (info->status == svn_wc__db_status_normal)
{
/* Simple diff */
base_kind = info->kind;
}
else if (info->status == svn_wc__db_status_deleted
&& (!eb->diff_pristine || !info->have_more_work))
{
svn_wc__db_status_t base_status;
repos_only = TRUE;
SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL,
db, child_abspath,
iterpool, iterpool));
if (NOT_PRESENT(base_status))
continue;
}
else
{
/* working status is either added or deleted */
svn_wc__db_status_t base_status;
SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL,
db, child_abspath,
iterpool, iterpool));
if (NOT_PRESENT(base_status))
local_only = TRUE;
else if (base_kind != info->kind || !eb->ignore_ancestry)
{
repos_only = TRUE;
local_only = TRUE;
}
}
if (eb->local_before_remote && local_only)
{
if (info->kind == svn_node_file && diff_files)
SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
child_relpath,
eb->processor, dir_baton,
- eb->changelist_hash,
eb->diff_pristine,
eb->cancel_func,
eb->cancel_baton,
iterpool));
else if (info->kind == svn_node_dir && diff_dirs)
SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
child_relpath,
depth_below_here,
eb->processor, dir_baton,
- eb->changelist_hash,
eb->diff_pristine,
eb->cancel_func,
eb->cancel_baton,
iterpool));
}
if (repos_only)
{
/* Report repository form deleted */
if (base_kind == svn_node_file && diff_files)
SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath,
child_relpath, eb->revnum,
eb->processor, dir_baton,
iterpool));
else if (base_kind == svn_node_dir && diff_dirs)
SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath,
child_relpath, eb->revnum,
depth_below_here,
eb->processor, dir_baton,
eb->cancel_func,
eb->cancel_baton,
iterpool));
}
else if (!local_only) /* Not local only nor remote only */
{
/* Diff base against actual */
if (info->kind == svn_node_file && diff_files)
{
if (info->status != svn_wc__db_status_normal
|| !eb->diff_pristine)
{
SVN_ERR(svn_wc__diff_base_working_diff(
db, child_abspath,
child_relpath,
eb->revnum,
- eb->changelist_hash,
eb->processor, dir_baton,
eb->diff_pristine,
eb->cancel_func,
eb->cancel_baton,
scratch_pool));
}
}
else if (info->kind == svn_node_dir && diff_dirs)
SVN_ERR(walk_local_nodes_diff(eb, child_abspath,
child_relpath,
depth_below_here,
NULL /* compared */,
dir_baton,
scratch_pool));
}
if (!eb->local_before_remote && local_only)
{
if (info->kind == svn_node_file && diff_files)
SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
child_relpath,
eb->processor, dir_baton,
- eb->changelist_hash,
eb->diff_pristine,
eb->cancel_func,
eb->cancel_baton,
iterpool));
else if (info->kind == svn_node_dir && diff_dirs)
SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
child_relpath, depth_below_here,
eb->processor, dir_baton,
- eb->changelist_hash,
eb->diff_pristine,
eb->cancel_func,
eb->cancel_baton,
iterpool));
}
}
}
if (compared)
return SVN_NO_ERROR;
- /* Check for local property mods on this directory, if we haven't
- already reported them and we aren't changelist-filted.
- ### it should be noted that we do not currently allow directories
- ### to be part of changelists, so if a changelist is provided, the
- ### changelist check will always fail. */
+ /* Check for local property mods on this directory, if we haven't
+ already reported them. */
if (! skip
- && ! eb->changelist_hash
&& ! in_anchor_not_target
&& props_mod)
{
apr_array_header_t *propchanges;
apr_hash_t *left_props;
apr_hash_t *right_props;
SVN_ERR(svn_wc__internal_propdiff(&propchanges, &left_props,
db, local_abspath,
scratch_pool, scratch_pool));
right_props = svn_prop__patch(left_props, propchanges, scratch_pool);
SVN_ERR(eb->processor->dir_changed(path,
left_src,
right_src,
left_props,
right_props,
propchanges,
dir_baton,
eb->processor,
scratch_pool));
}
else if (! skip)
SVN_ERR(eb->processor->dir_closed(path,
left_src,
right_src,
dir_baton,
eb->processor,
scratch_pool));
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__diff_local_only_file(svn_wc__db_t *db,
const char *local_abspath,
const char *relpath,
const svn_diff_tree_processor_t *processor,
void *processor_parent_baton,
- apr_hash_t *changelist_hash,
svn_boolean_t diff_pristine,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
svn_diff_source_t *right_src;
svn_diff_source_t *copyfrom_src = NULL;
svn_wc__db_status_t status;
svn_node_kind_t kind;
const svn_checksum_t *checksum;
const char *original_repos_relpath;
svn_revnum_t original_revision;
- const char *changelist;
svn_boolean_t had_props;
svn_boolean_t props_mod;
apr_hash_t *pristine_props;
apr_hash_t *right_props = NULL;
const char *pristine_file;
const char *translated_file;
svn_revnum_t revision;
void *file_baton = NULL;
svn_boolean_t skip = FALSE;
svn_boolean_t file_mod = TRUE;
SVN_ERR(svn_wc__db_read_info(&status, &kind, &revision, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, &checksum, NULL,
&original_repos_relpath, NULL, NULL,
&original_revision, NULL, NULL, NULL,
- &changelist, NULL, NULL, &had_props,
+ NULL, NULL, NULL, &had_props,
&props_mod, NULL, NULL, NULL,
db, local_abspath,
scratch_pool, scratch_pool));
assert(kind == svn_node_file
&& (status == svn_wc__db_status_normal
|| status == svn_wc__db_status_added
|| (status == svn_wc__db_status_deleted && diff_pristine)));
- if (changelist && changelist_hash
- && !svn_hash_gets(changelist_hash, changelist))
- return SVN_NO_ERROR;
-
if (status == svn_wc__db_status_deleted)
{
assert(diff_pristine);
SVN_ERR(svn_wc__db_read_pristine_info(&status, &kind, NULL, NULL, NULL,
NULL, &checksum, NULL, &had_props,
&pristine_props,
db, local_abspath,
scratch_pool, scratch_pool));
props_mod = FALSE;
}
else if (!had_props)
pristine_props = apr_hash_make(scratch_pool);
else
SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props,
db, local_abspath,
scratch_pool, scratch_pool));
if (original_repos_relpath)
{
copyfrom_src = svn_diff__source_create(original_revision, scratch_pool);
copyfrom_src->repos_relpath = original_repos_relpath;
}
if (props_mod || !SVN_IS_VALID_REVNUM(revision))
right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
else
{
if (diff_pristine)
file_mod = FALSE;
else
SVN_ERR(svn_wc__internal_file_modified_p(&file_mod, db, local_abspath,
FALSE, scratch_pool));
if (!file_mod)
right_src = svn_diff__source_create(revision, scratch_pool);
else
right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
}
SVN_ERR(processor->file_opened(&file_baton, &skip,
relpath,
NULL /* left_source */,
right_src,
copyfrom_src,
processor_parent_baton,
processor,
scratch_pool, scratch_pool));
if (skip)
return SVN_NO_ERROR;
if (props_mod && !diff_pristine)
SVN_ERR(svn_wc__db_read_props(&right_props, db, local_abspath,
scratch_pool, scratch_pool));
else
right_props = svn_prop_hash_dup(pristine_props, scratch_pool);
if (checksum)
SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file, db, local_abspath,
checksum, scratch_pool, scratch_pool));
else
pristine_file = NULL;
if (diff_pristine)
{
translated_file = pristine_file; /* No translation needed */
}
else
{
SVN_ERR(svn_wc__internal_translated_file(
&translated_file, local_abspath, db, local_abspath,
SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
cancel_func, cancel_baton,
scratch_pool, scratch_pool));
}
SVN_ERR(processor->file_added(relpath,
copyfrom_src,
right_src,
copyfrom_src
? pristine_file
: NULL,
translated_file,
copyfrom_src
? pristine_props
: NULL,
right_props,
file_baton,
processor,
scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__diff_local_only_dir(svn_wc__db_t *db,
const char *local_abspath,
const char *relpath,
svn_depth_t depth,
const svn_diff_tree_processor_t *processor,
void *processor_parent_baton,
- apr_hash_t *changelist_hash,
svn_boolean_t diff_pristine,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
+ svn_wc__db_status_t status;
+ svn_node_kind_t kind;
+ svn_boolean_t had_props;
+ svn_boolean_t props_mod;
+ const char *original_repos_relpath;
+ svn_revnum_t original_revision;
+ svn_diff_source_t *copyfrom_src = NULL;
+ apr_hash_t *pristine_props;
const apr_array_header_t *children;
int i;
apr_pool_t *iterpool;
void *pdb = NULL;
svn_boolean_t skip = FALSE;
svn_boolean_t skip_children = FALSE;
svn_diff_source_t *right_src = svn_diff__source_create(SVN_INVALID_REVNUM,
scratch_pool);
svn_depth_t depth_below_here = depth;
apr_hash_t *nodes;
apr_hash_t *conflicts;
+ SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ &original_repos_relpath, NULL, NULL,
+ &original_revision, NULL, NULL, NULL,
+ NULL, NULL, NULL, &had_props,
+ &props_mod, NULL, NULL, NULL,
+ db, local_abspath,
+ scratch_pool, scratch_pool));
+ if (original_repos_relpath)
+ {
+ copyfrom_src = svn_diff__source_create(original_revision, scratch_pool);
+ copyfrom_src->repos_relpath = original_repos_relpath;
+ }
+
+ /* svn_wc__db_status_incomplete should never happen, as the result won't be
+ stable or guaranteed related to what is in the repository for this
+ revision, but without this it would be hard to diagnose that status... */
+ assert(kind == svn_node_dir
+ && (status == svn_wc__db_status_normal
+ || status == svn_wc__db_status_incomplete
+ || status == svn_wc__db_status_added
+ || (status == svn_wc__db_status_deleted && diff_pristine)));
+
+ if (status == svn_wc__db_status_deleted)
+ {
+ assert(diff_pristine);
+
+ SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, &had_props,
+ &pristine_props,
+ db, local_abspath,
+ scratch_pool, scratch_pool));
+ props_mod = FALSE;
+ }
+ else if (!had_props)
+ pristine_props = apr_hash_make(scratch_pool);
+ else
+ SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props,
+ db, local_abspath,
+ scratch_pool, scratch_pool));
+
/* Report the addition of the directory's contents. */
iterpool = svn_pool_create(scratch_pool);
SVN_ERR(processor->dir_opened(&pdb, &skip, &skip_children,
relpath,
NULL,
right_src,
- NULL /* copyfrom_src */,
+ copyfrom_src,
processor_parent_baton,
processor,
scratch_pool, iterpool));
+ /* ### skip_children is not used */
SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts, db, local_abspath,
scratch_pool, iterpool));
if (depth_below_here == svn_depth_immediates)
depth_below_here = svn_depth_empty;
children = svn_sort__hash(nodes, svn_sort_compare_items_lexically,
scratch_pool);
for (i = 0; i < children->nelts; i++)
{
svn_sort__item_t *item = &APR_ARRAY_IDX(children, i, svn_sort__item_t);
const char *name = item->key;
struct svn_wc__db_info_t *info = item->value;
const char *child_abspath;
const char *child_relpath;
svn_pool_clear(iterpool);
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
child_abspath = svn_dirent_join(local_abspath, name, iterpool);
if (NOT_PRESENT(info->status))
{
continue;
}
/* If comparing against WORKING, skip entries that are
schedule-deleted - they don't really exist. */
if (!diff_pristine && info->status == svn_wc__db_status_deleted)
continue;
child_relpath = svn_relpath_join(relpath, name, iterpool);
switch (info->kind)
{
case svn_node_file:
case svn_node_symlink:
SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
child_relpath,
processor, pdb,
- changelist_hash,
diff_pristine,
cancel_func, cancel_baton,
scratch_pool));
break;
case svn_node_dir:
if (depth > svn_depth_files || depth == svn_depth_unknown)
{
SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
child_relpath, depth_below_here,
processor, pdb,
- changelist_hash,
diff_pristine,
cancel_func, cancel_baton,
iterpool));
}
break;
default:
break;
}
}
if (!skip)
{
apr_hash_t *right_props;
- if (diff_pristine)
- SVN_ERR(svn_wc__db_read_pristine_props(&right_props, db, local_abspath,
- scratch_pool, scratch_pool));
+
+ if (props_mod && !diff_pristine)
+ SVN_ERR(svn_wc__db_read_props(&right_props, db, local_abspath,
+ scratch_pool, scratch_pool));
else
- SVN_ERR(svn_wc__get_actual_props(&right_props, db, local_abspath,
- scratch_pool, scratch_pool));
+ right_props = svn_prop_hash_dup(pristine_props, scratch_pool);
SVN_ERR(processor->dir_added(relpath,
- NULL /* copyfrom_src */,
+ copyfrom_src,
right_src,
- NULL,
+ copyfrom_src
+ ? pristine_props
+ : NULL,
right_props,
pdb,
processor,
iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Reports local changes. */
static svn_error_t *
handle_local_only(struct dir_baton_t *pb,
const char *name,
apr_pool_t *scratch_pool)
{
struct edit_baton_t *eb = pb->eb;
const struct svn_wc__db_info_t *info;
svn_boolean_t repos_delete = (pb->deletes
&& svn_hash_gets(pb->deletes, name));
assert(!strchr(name, '/'));
assert(!pb->added || eb->ignore_ancestry);
if (pb->skip_children)
return SVN_NO_ERROR;
SVN_ERR(ensure_local_info(pb, scratch_pool));
info = svn_hash_gets(pb->local_info, name);
if (info == NULL || NOT_PRESENT(info->status))
return SVN_NO_ERROR;
switch (info->status)
{
case svn_wc__db_status_incomplete:
return SVN_NO_ERROR; /* Not local only */
case svn_wc__db_status_normal:
if (!repos_delete)
return SVN_NO_ERROR; /* Local and remote */
svn_hash_sets(pb->deletes, name, NULL);
break;
case svn_wc__db_status_deleted:
if (!(eb->diff_pristine && repos_delete))
return SVN_NO_ERROR;
break;
case svn_wc__db_status_added:
default:
break;
}
if (info->kind == svn_node_dir)
{
svn_depth_t depth ;
if (pb->depth == svn_depth_infinity || pb->depth == svn_depth_unknown)
depth = pb->depth;
else
depth = svn_depth_empty;
SVN_ERR(svn_wc__diff_local_only_dir(
eb->db,
svn_dirent_join(pb->local_abspath, name, scratch_pool),
svn_relpath_join(pb->relpath, name, scratch_pool),
repos_delete ? svn_depth_infinity : depth,
eb->processor, pb->pdb,
- eb->changelist_hash,
eb->diff_pristine,
eb->cancel_func, eb->cancel_baton,
scratch_pool));
}
else
SVN_ERR(svn_wc__diff_local_only_file(
eb->db,
svn_dirent_join(pb->local_abspath, name, scratch_pool),
svn_relpath_join(pb->relpath, name, scratch_pool),
eb->processor, pb->pdb,
- eb->changelist_hash,
eb->diff_pristine,
eb->cancel_func, eb->cancel_baton,
scratch_pool));
return SVN_NO_ERROR;
}
/* Reports a file LOCAL_ABSPATH in BASE as deleted */
svn_error_t *
svn_wc__diff_base_only_file(svn_wc__db_t *db,
const char *local_abspath,
const char *relpath,
svn_revnum_t revision,
const svn_diff_tree_processor_t *processor,
void *processor_parent_baton,
apr_pool_t *scratch_pool)
{
svn_wc__db_status_t status;
svn_node_kind_t kind;
const svn_checksum_t *checksum;
apr_hash_t *props;
void *file_baton = NULL;
svn_boolean_t skip = FALSE;
svn_diff_source_t *left_src;
const char *pristine_file;
SVN_ERR(svn_wc__db_base_get_info(&status, &kind,
SVN_IS_VALID_REVNUM(revision)
? NULL : &revision,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
&checksum, NULL, NULL, NULL, &props, NULL,
db, local_abspath,
scratch_pool, scratch_pool));
SVN_ERR_ASSERT(status == svn_wc__db_status_normal
&& kind == svn_node_file
&& checksum);
left_src = svn_diff__source_create(revision, scratch_pool);
SVN_ERR(processor->file_opened(&file_baton, &skip,
relpath,
left_src,
NULL /* right_src */,
NULL /* copyfrom_source */,
processor_parent_baton,
processor,
scratch_pool, scratch_pool));
if (skip)
return SVN_NO_ERROR;
SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file,
db, local_abspath, checksum,
scratch_pool, scratch_pool));
SVN_ERR(processor->file_deleted(relpath,
left_src,
pristine_file,
props,
file_baton,
processor,
scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__diff_base_only_dir(svn_wc__db_t *db,
const char *local_abspath,
const char *relpath,
svn_revnum_t revision,
svn_depth_t depth,
const svn_diff_tree_processor_t *processor,
void *processor_parent_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
void *dir_baton = NULL;
svn_boolean_t skip = FALSE;
svn_boolean_t skip_children = FALSE;
svn_diff_source_t *left_src;
svn_revnum_t report_rev = revision;
if (!SVN_IS_VALID_REVNUM(report_rev))
SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &report_rev, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL,
db, local_abspath,
scratch_pool, scratch_pool));
left_src = svn_diff__source_create(report_rev, scratch_pool);
SVN_ERR(processor->dir_opened(&dir_baton, &skip, &skip_children,
relpath,
left_src,
NULL /* right_src */,
NULL /* copyfrom_src */,
processor_parent_baton,
processor,
scratch_pool, scratch_pool));
if (!skip_children && (depth == svn_depth_unknown || depth > svn_depth_empty))
{
apr_hash_t *nodes;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
apr_array_header_t *children;
int i;
SVN_ERR(svn_wc__db_base_get_children_info(&nodes, db, local_abspath,
scratch_pool, iterpool));
children = svn_sort__hash(nodes, svn_sort_compare_items_lexically,
scratch_pool);
for (i = 0; i < children->nelts; i++)
{
svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
svn_sort__item_t);
const char *name = item->key;
struct svn_wc__db_base_info_t *info = item->value;
const char *child_abspath;
const char *child_relpath;
if (info->status != svn_wc__db_status_normal)
continue;
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
svn_pool_clear(iterpool);
child_abspath = svn_dirent_join(local_abspath, name, iterpool);
child_relpath = svn_relpath_join(relpath, name, iterpool);
switch (info->kind)
{
case svn_node_file:
case svn_node_symlink:
SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath,
child_relpath,
revision,
processor, dir_baton,
iterpool));
break;
case svn_node_dir:
if (depth > svn_depth_files || depth == svn_depth_unknown)
{
svn_depth_t depth_below_here = depth;
if (depth_below_here == svn_depth_immediates)
depth_below_here = svn_depth_empty;
SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath,
child_relpath,
revision,
depth_below_here,
processor, dir_baton,
cancel_func,
cancel_baton,
iterpool));
}
break;
default:
break;
}
}
}
if (!skip)
{
apr_hash_t *props;
SVN_ERR(svn_wc__db_base_get_props(&props, db, local_abspath,
scratch_pool, scratch_pool));
SVN_ERR(processor->dir_deleted(relpath,
left_src,
props,
dir_baton,
processor,
scratch_pool));
}
return SVN_NO_ERROR;
}
/* An svn_delta_editor_t function. */
static svn_error_t *
set_target_revision(void *edit_baton,
svn_revnum_t target_revision,
apr_pool_t *pool)
{
struct edit_baton_t *eb = edit_baton;
eb->revnum = target_revision;
return SVN_NO_ERROR;
}
/* An svn_delta_editor_t function. The root of the comparison hierarchy */
static svn_error_t *
open_root(void *edit_baton,
svn_revnum_t base_revision,
apr_pool_t *dir_pool,
void **root_baton)
{
struct edit_baton_t *eb = edit_baton;
struct dir_baton_t *db;
eb->root_opened = TRUE;
db = make_dir_baton("", NULL, eb, FALSE, eb->depth, dir_pool);
*root_baton = db;
if (eb->target[0] == '\0')
{
db->left_src = svn_diff__source_create(eb->revnum, db->pool);
db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip,
&db->skip_children,
"",
db->left_src,
db->right_src,
NULL /* copyfrom_source */,
NULL /* parent_baton */,
eb->processor,
db->pool, db->pool));
}
else
db->skip = TRUE; /* Skip this, but not the children */
return SVN_NO_ERROR;
}
/* An svn_delta_editor_t function. */
static svn_error_t *
delete_entry(const char *path,
svn_revnum_t base_revision,
void *parent_baton,
apr_pool_t *pool)
{
struct dir_baton_t *pb = parent_baton;
const char *name = svn_dirent_basename(path, pb->pool);
if (!pb->deletes)
pb->deletes = apr_hash_make(pb->pool);
svn_hash_sets(pb->deletes, name, "");
return SVN_NO_ERROR;
}
/* An svn_delta_editor_t function. */
static svn_error_t *
add_directory(const char *path,
void *parent_baton,
const char *copyfrom_path,
svn_revnum_t copyfrom_revision,
apr_pool_t *dir_pool,
void **child_baton)
{
struct dir_baton_t *pb = parent_baton;
struct edit_baton_t *eb = pb->eb;
struct dir_baton_t *db;
svn_depth_t subdir_depth = (pb->depth == svn_depth_immediates)
? svn_depth_empty : pb->depth;
db = make_dir_baton(path, pb, pb->eb, TRUE, subdir_depth,
dir_pool);
*child_baton = db;
if (pb->repos_only || !eb->ignore_ancestry)
db->repos_only = TRUE;
else
{
struct svn_wc__db_info_t *info;
SVN_ERR(ensure_local_info(pb, dir_pool));
info = svn_hash_gets(pb->local_info, db->name);
if (!info || info->kind != svn_node_dir || NOT_PRESENT(info->status))
db->repos_only = TRUE;
if (!db->repos_only && info->status != svn_wc__db_status_added)
db->repos_only = TRUE;
if (!db->repos_only)
{
db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
db->ignoring_ancestry = TRUE;
svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, db->name), "");
}
}
db->left_src = svn_diff__source_create(eb->revnum, db->pool);
if (eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
SVN_ERR(handle_local_only(pb, db->name, dir_pool));
SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip, &db->skip_children,
db->relpath,
db->left_src,
db->right_src,
NULL /* copyfrom src */,
pb->pdb,
eb->processor,
db->pool, db->pool));
return SVN_NO_ERROR;
}
/* An svn_delta_editor_t function. */
static svn_error_t *
open_directory(const char *path,
void *parent_baton,
svn_revnum_t base_revision,
apr_pool_t *dir_pool,
void **child_baton)
{
struct dir_baton_t *pb = parent_baton;
struct edit_baton_t *eb = pb->eb;
struct dir_baton_t *db;
svn_depth_t subdir_depth = (pb->depth == svn_depth_immediates)
? svn_depth_empty : pb->depth;
/* Allocate path from the parent pool since the memory is used in the
parent's compared hash */
db = make_dir_baton(path, pb, pb->eb, FALSE, subdir_depth, dir_pool);
*child_baton = db;
if (pb->repos_only)
db->repos_only = TRUE;
else
{
struct svn_wc__db_info_t *info;
SVN_ERR(ensure_local_info(pb, dir_pool));
info = svn_hash_gets(pb->local_info, db->name);
if (!info || info->kind != svn_node_dir || NOT_PRESENT(info->status))
db->repos_only = TRUE;
if (!db->repos_only)
switch (info->status)
{
case svn_wc__db_status_normal:
break;
case svn_wc__db_status_deleted:
db->repos_only = TRUE;
if (!info->have_more_work)
svn_hash_sets(pb->compared,
apr_pstrdup(pb->pool, db->name), "");
break;
case svn_wc__db_status_added:
if (eb->ignore_ancestry)
db->ignoring_ancestry = TRUE;
else
db->repos_only = TRUE;
break;
default:
SVN_ERR_MALFUNCTION();
}
if (!db->repos_only)
{
db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, db->name), "");
}
}
db->left_src = svn_diff__source_create(eb->revnum, db->pool);
if (eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
SVN_ERR(handle_local_only(pb, db->name, dir_pool));
SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip, &db->skip_children,
db->relpath,
db->left_src,
db->right_src,
NULL /* copyfrom src */,
pb->pdb,
eb->processor,
db->pool, db->pool));
return SVN_NO_ERROR;
}
/* An svn_delta_editor_t function. When a directory is closed, all the
* directory elements that have been added or replaced will already have been
* diff'd. However there may be other elements in the working copy
* that have not yet been considered. */
static svn_error_t *
close_directory(void *dir_baton,
apr_pool_t *pool)
{
struct dir_baton_t *db = dir_baton;
struct dir_baton_t *pb = db->parent_baton;
struct edit_baton_t *eb = db->eb;
apr_pool_t *scratch_pool = db->pool;
svn_boolean_t reported_closed = FALSE;
if (!db->skip_children && db->deletes && apr_hash_count(db->deletes))
{
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
apr_array_header_t *children;
int i;
children = svn_sort__hash(db->deletes, svn_sort_compare_items_lexically,
scratch_pool);
for (i = 0; i < children->nelts; i++)
{
svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
svn_sort__item_t);
const char *name = item->key;
svn_pool_clear(iterpool);
SVN_ERR(handle_local_only(db, name, iterpool));
svn_hash_sets(db->compared, name, "");
}
svn_pool_destroy(iterpool);
}
/* Report local modifications for this directory. Skip added
directories since they can only contain added elements, all of
which have already been diff'd. */
if (!db->repos_only && !db->skip_children)
{
SVN_ERR(walk_local_nodes_diff(eb,
db->local_abspath,
db->relpath,
db->depth,
db->compared,
db->pdb,
scratch_pool));
}
/* Report the property changes on the directory itself, if necessary. */
if (db->skip)
{
/* Diff processor requested no directory details */
}
else if (db->propchanges->nelts > 0 || db->repos_only)
{
apr_hash_t *repos_props;
if (db->added)
{
repos_props = apr_hash_make(scratch_pool);
}
else
{
SVN_ERR(svn_wc__db_base_get_props(&repos_props,
eb->db, db->local_abspath,
scratch_pool, scratch_pool));
}
/* Add received property changes and entry props */
if (db->propchanges->nelts)
repos_props = svn_prop__patch(repos_props, db->propchanges,
scratch_pool);
if (db->repos_only)
{
SVN_ERR(eb->processor->dir_deleted(db->relpath,
db->left_src,
repos_props,
db->pdb,
eb->processor,
scratch_pool));
reported_closed = TRUE;
}
else
{
apr_hash_t *local_props;
apr_array_header_t *prop_changes;
if (eb->diff_pristine)
SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
&local_props,
eb->db, db->local_abspath,
scratch_pool, scratch_pool));
else
SVN_ERR(svn_wc__db_read_props(&local_props,
eb->db, db->local_abspath,
scratch_pool, scratch_pool));
SVN_ERR(svn_prop_diffs(&prop_changes, local_props, repos_props,
scratch_pool));
/* ### as a good diff processor we should now only report changes
if there are non-entry changes, but for now we stick to
compatibility */
if (prop_changes->nelts)
{
SVN_ERR(eb->processor->dir_changed(db->relpath,
db->left_src,
db->right_src,
repos_props,
local_props,
prop_changes,
db->pdb,
eb->processor,
scratch_pool));
reported_closed = TRUE;
}
}
}
/* Mark this directory as compared in the parent directory's baton,
unless this is the root of the comparison. */
if (!reported_closed && !db->skip)
SVN_ERR(eb->processor->dir_closed(db->relpath,
db->left_src,
db->right_src,
db->pdb,
eb->processor,
scratch_pool));
if (pb && !eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
SVN_ERR(handle_local_only(pb, db->name, scratch_pool));
SVN_ERR(maybe_done(db)); /* destroys scratch_pool */
return SVN_NO_ERROR;
}
/* An svn_delta_editor_t function. */
static svn_error_t *
add_file(const char *path,
void *parent_baton,
const char *copyfrom_path,
svn_revnum_t copyfrom_revision,
apr_pool_t *file_pool,
void **file_baton)
{
struct dir_baton_t *pb = parent_baton;
struct edit_baton_t *eb = pb->eb;
struct file_baton_t *fb;
fb = make_file_baton(path, TRUE, pb, file_pool);
*file_baton = fb;
if (pb->skip_children)
{
fb->skip = TRUE;
return SVN_NO_ERROR;
}
else if (pb->repos_only || !eb->ignore_ancestry)
fb->repos_only = TRUE;
else
{
struct svn_wc__db_info_t *info;
SVN_ERR(ensure_local_info(pb, file_pool));
info = svn_hash_gets(pb->local_info, fb->name);
if (!info || info->kind != svn_node_file || NOT_PRESENT(info->status))
fb->repos_only = TRUE;
if (!fb->repos_only && info->status != svn_wc__db_status_added)
fb->repos_only = TRUE;
if (!fb->repos_only)
{
/* Add this path to the parent directory's list of elements that
have been compared. */
fb->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, fb->pool);
fb->ignoring_ancestry = TRUE;
svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, fb->name), "");
}
}
fb->left_src = svn_diff__source_create(eb->revnum, fb->pool);
SVN_ERR(eb->processor->file_opened(&fb->pfb, &fb->skip,
fb->relpath,
fb->left_src,
fb->right_src,
NULL /* copyfrom src */,
pb->pdb,
eb->processor,
fb->pool, fb->pool));
return SVN_NO_ERROR;
}
/* An svn_delta_editor_t function. */
static svn_error_t *
open_file(const char *path,
void *parent_baton,
svn_revnum_t base_revision,
apr_pool_t *file_pool,
void **file_baton)
{
struct dir_baton_t *pb = parent_baton;
struct edit_baton_t *eb = pb->eb;
struct file_baton_t *fb;
fb = make_file_baton(path, FALSE, pb, file_pool);
*file_baton = fb;
if (pb->skip_children)
fb->skip = TRUE;
else if (pb->repos_only)
fb->repos_only = TRUE;
else
{
struct svn_wc__db_info_t *info;
SVN_ERR(ensure_local_info(pb, file_pool));
info = svn_hash_gets(pb->local_info, fb->name);
if (!info || info->kind != svn_node_file || NOT_PRESENT(info->status))
fb->repos_only = TRUE;
if (!fb->repos_only)
switch (info->status)
{
case svn_wc__db_status_normal:
break;
case svn_wc__db_status_deleted:
fb->repos_only = TRUE;
if (!info->have_more_work)
svn_hash_sets(pb->compared,
apr_pstrdup(pb->pool, fb->name), "");
break;
case svn_wc__db_status_added:
if (eb->ignore_ancestry)
fb->ignoring_ancestry = TRUE;
else
fb->repos_only = TRUE;
break;
default:
SVN_ERR_MALFUNCTION();
}
if (!fb->repos_only)
{
/* Add this path to the parent directory's list of elements that
have been compared. */
fb->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, fb->pool);
svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, fb->name), "");
}
}
fb->left_src = svn_diff__source_create(eb->revnum, fb->pool);
SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, &fb->base_checksum, NULL,
NULL, NULL, &fb->base_props, NULL,
eb->db, fb->local_abspath,
fb->pool, fb->pool));
SVN_ERR(eb->processor->file_opened(&fb->pfb, &fb->skip,
fb->relpath,
fb->left_src,
fb->right_src,
NULL /* copyfrom src */,
pb->pdb,
eb->processor,
fb->pool, fb->pool));
return SVN_NO_ERROR;
}
/* An svn_delta_editor_t function. */
static svn_error_t *
apply_textdelta(void *file_baton,
const char *base_checksum_hex,
apr_pool_t *pool,
svn_txdelta_window_handler_t *handler,
void **handler_baton)
{
struct file_baton_t *fb = file_baton;
struct edit_baton_t *eb = fb->eb;
svn_stream_t *source;
svn_stream_t *temp_stream;
svn_checksum_t *repos_checksum = NULL;
if (fb->skip)
{
*handler = svn_delta_noop_window_handler;
*handler_baton = NULL;
return SVN_NO_ERROR;
}
if (base_checksum_hex && fb->base_checksum)
{
const svn_checksum_t *base_md5;
SVN_ERR(svn_checksum_parse_hex(&repos_checksum, svn_checksum_md5,
base_checksum_hex, pool));
SVN_ERR(svn_wc__db_pristine_get_md5(&base_md5,
eb->db, eb->anchor_abspath,
fb->base_checksum,
pool, pool));
if (! svn_checksum_match(repos_checksum, base_md5))
{
/* ### I expect that there are some bad drivers out there
### that used to give bad results. We could look in
### working to see if the expected checksum matches and
### then return the pristine of that... But that only moves
### the problem */
/* If needed: compare checksum obtained via md5 of working.
And if they match set fb->base_checksum and fb->base_props */
return svn_checksum_mismatch_err(
base_md5,
repos_checksum,
pool,
_("Checksum mismatch for '%s'"),
svn_dirent_local_style(fb->local_abspath,
pool));
}
SVN_ERR(svn_wc__db_pristine_read(&source, NULL,
eb->db, fb->local_abspath,
fb->base_checksum,
pool, pool));
}
else if (fb->base_checksum)
{
SVN_ERR(svn_wc__db_pristine_read(&source, NULL,
eb->db, fb->local_abspath,
fb->base_checksum,
pool, pool));
}
else
source = svn_stream_empty(pool);
/* This is the file that will contain the pristine repository version. */
SVN_ERR(svn_stream_open_unique(&temp_stream, &fb->temp_file_path, NULL,
svn_io_file_del_on_pool_cleanup,
fb->pool, fb->pool));
svn_txdelta_apply(source, temp_stream,
fb->result_digest,
fb->local_abspath /* error_info */,
fb->pool,
handler, handler_baton);
return SVN_NO_ERROR;
}
/* An svn_delta_editor_t function. When the file is closed we have a temporary
* file containing a pristine version of the repository file. This can
* be compared against the working copy.
*
* Ignore TEXT_CHECKSUM.
*/
static svn_error_t *
close_file(void *file_baton,
const char *expected_md5_digest,
apr_pool_t *pool)
{
struct file_baton_t *fb = file_baton;
struct dir_baton_t *pb = fb->parent_baton;
struct edit_baton_t *eb = fb->eb;
apr_pool_t *scratch_pool = fb->pool;
/* The repository information; constructed from BASE + Changes */
const char *repos_file;
apr_hash_t *repos_props;
- if (!fb->skip && expected_md5_digest != NULL)
+ if (fb->skip)
{
+ svn_pool_destroy(fb->pool); /* destroys scratch_pool and fb */
+ SVN_ERR(maybe_done(pb));
+ return SVN_NO_ERROR;
+ }
+
+ if (expected_md5_digest != NULL)
+ {
svn_checksum_t *expected_checksum;
const svn_checksum_t *result_checksum;
if (fb->temp_file_path)
result_checksum = svn_checksum__from_digest_md5(fb->result_digest,
scratch_pool);
else
result_checksum = fb->base_checksum;
SVN_ERR(svn_checksum_parse_hex(&expected_checksum, svn_checksum_md5,
expected_md5_digest, scratch_pool));
if (result_checksum->kind != svn_checksum_md5)
SVN_ERR(svn_wc__db_pristine_get_md5(&result_checksum,
eb->db, fb->local_abspath,
result_checksum,
scratch_pool, scratch_pool));
if (!svn_checksum_match(expected_checksum, result_checksum))
return svn_checksum_mismatch_err(
expected_checksum,
result_checksum,
pool,
_("Checksum mismatch for '%s'"),
svn_dirent_local_style(fb->local_abspath,
scratch_pool));
}
if (eb->local_before_remote && !fb->repos_only && !fb->ignoring_ancestry)
SVN_ERR(handle_local_only(pb, fb->name, scratch_pool));
{
apr_hash_t *prop_base;
if (fb->added)
prop_base = apr_hash_make(scratch_pool);
else
prop_base = fb->base_props;
/* includes entry props */
repos_props = svn_prop__patch(prop_base, fb->propchanges, scratch_pool);
repos_file = fb->temp_file_path;
if (! repos_file)
{
assert(fb->base_checksum);
SVN_ERR(svn_wc__db_pristine_get_path(&repos_file,
eb->db, eb->anchor_abspath,
fb->base_checksum,
scratch_pool, scratch_pool));
}
}
- if (fb->skip)
+ if (fb->repos_only)
{
- /* Diff processor requested skipping information */
- }
- else if (fb->repos_only)
- {
SVN_ERR(eb->processor->file_deleted(fb->relpath,
fb->left_src,
fb->temp_file_path,
repos_props,
fb->pfb,
eb->processor,
scratch_pool));
}
else
{
/* Produce a diff of actual or pristine against repos */
apr_hash_t *local_props;
apr_array_header_t *prop_changes;
const char *localfile;
/* pb->local_info contains some information that might allow optimizing
this a bit */
if (eb->diff_pristine)
{
const svn_checksum_t *checksum;
SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL,
NULL, &checksum, NULL, NULL,
&local_props,
eb->db, fb->local_abspath,
scratch_pool, scratch_pool));
assert(checksum);
SVN_ERR(svn_wc__db_pristine_get_path(&localfile,
eb->db, eb->anchor_abspath,
checksum,
scratch_pool, scratch_pool));
}
else
{
SVN_ERR(svn_wc__db_read_props(&local_props,
eb->db, fb->local_abspath,
scratch_pool, scratch_pool));
/* a detranslated version of the working file */
SVN_ERR(svn_wc__internal_translated_file(
&localfile, fb->local_abspath, eb->db, fb->local_abspath,
SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
eb->cancel_func, eb->cancel_baton,
scratch_pool, scratch_pool));
}
SVN_ERR(svn_prop_diffs(&prop_changes, local_props, repos_props,
scratch_pool));
/* ### as a good diff processor we should now only report changes, and
report file_closed() in other cases */
SVN_ERR(eb->processor->file_changed(fb->relpath,
fb->left_src,
fb->right_src,
repos_file /* left file */,
localfile /* right file */,
repos_props /* left_props */,
local_props /* right props */,
TRUE /* ### file_modified */,
prop_changes,
fb->pfb,
eb->processor,
scratch_pool));
}
if (!eb->local_before_remote && !fb->repos_only && !fb->ignoring_ancestry)
SVN_ERR(handle_local_only(pb, fb->name, scratch_pool));
svn_pool_destroy(fb->pool); /* destroys scratch_pool and fb */
SVN_ERR(maybe_done(pb));
return SVN_NO_ERROR;
}
/* An svn_delta_editor_t function. */
static svn_error_t *
change_file_prop(void *file_baton,
const char *name,
const svn_string_t *value,
apr_pool_t *pool)
{
struct file_baton_t *fb = file_baton;
svn_prop_t *propchange;
svn_prop_kind_t propkind;
propkind = svn_property_kind2(name);
if (propkind == svn_prop_wc_kind)
return SVN_NO_ERROR;
else if (propkind == svn_prop_regular_kind)
fb->has_propchange = TRUE;
propchange = apr_array_push(fb->propchanges);
propchange->name = apr_pstrdup(fb->pool, name);
propchange->value = value ? svn_string_dup(value, fb->pool) : NULL;
return SVN_NO_ERROR;
}
/* An svn_delta_editor_t function. */
static svn_error_t *
change_dir_prop(void *dir_baton,
const char *name,
const svn_string_t *value,
apr_pool_t *pool)
{
struct dir_baton_t *db = dir_baton;
svn_prop_t *propchange;
svn_prop_kind_t propkind;
propkind = svn_property_kind2(name);
if (propkind == svn_prop_wc_kind)
return SVN_NO_ERROR;
else if (propkind == svn_prop_regular_kind)
db->has_propchange = TRUE;
propchange = apr_array_push(db->propchanges);
propchange->name = apr_pstrdup(db->pool, name);
propchange->value = value ? svn_string_dup(value, db->pool) : NULL;
return SVN_NO_ERROR;
}
/* An svn_delta_editor_t function. */
static svn_error_t *
close_edit(void *edit_baton,
apr_pool_t *pool)
{
struct edit_baton_t *eb = edit_baton;
if (!eb->root_opened)
{
SVN_ERR(walk_local_nodes_diff(eb,
eb->anchor_abspath,
"",
eb->depth,
NULL /* compared */,
NULL /* No parent_baton */,
eb->pool));
}
return SVN_NO_ERROR;
}
/* Public Interface */
/* Create a diff editor and baton. */
svn_error_t *
svn_wc__get_diff_editor(const svn_delta_editor_t **editor,
void **edit_baton,
svn_wc_context_t *wc_ctx,
const char *anchor_abspath,
const char *target,
svn_depth_t depth,
svn_boolean_t ignore_ancestry,
svn_boolean_t show_copies_as_adds,
svn_boolean_t use_git_diff_format,
svn_boolean_t use_text_base,
svn_boolean_t reverse_order,
svn_boolean_t server_performs_filtering,
const apr_array_header_t *changelist_filter,
const svn_wc_diff_callbacks4_t *callbacks,
void *callback_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
struct edit_baton_t *eb;
void *inner_baton;
svn_delta_editor_t *tree_editor;
const svn_delta_editor_t *inner_editor;
struct svn_wc__shim_fetch_baton_t *sfb;
svn_delta_shim_callbacks_t *shim_callbacks =
svn_delta_shim_callbacks_default(result_pool);
+ const svn_diff_tree_processor_t *diff_processor;
SVN_ERR_ASSERT(svn_dirent_is_absolute(anchor_abspath));
/* --git implies --show-copies-as-adds */
if (use_git_diff_format)
show_copies_as_adds = TRUE;
+ SVN_ERR(svn_wc__wrap_diff_callbacks(&diff_processor,
+ callbacks, callback_baton, TRUE,
+ result_pool, scratch_pool));
+
+ /* Apply changelist filtering to the output */
+ if (changelist_filter && changelist_filter->nelts)
+ {
+ apr_hash_t *changelist_hash;
+
+ SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelist_filter,
+ result_pool));
+ diff_processor = svn_wc__changelist_filter_tree_processor_create(
+ diff_processor, wc_ctx, anchor_abspath,
+ changelist_hash, result_pool);
+ }
+
SVN_ERR(make_edit_baton(&eb,
wc_ctx->db,
anchor_abspath, target,
- callbacks, callback_baton,
+ diff_processor,
depth, ignore_ancestry, show_copies_as_adds,
- use_text_base, reverse_order, changelist_filter,
+ use_text_base, reverse_order,
cancel_func, cancel_baton,
result_pool));
tree_editor = svn_delta_default_editor(eb->pool);
tree_editor->set_target_revision = set_target_revision;
tree_editor->open_root = open_root;
tree_editor->delete_entry = delete_entry;
tree_editor->add_directory = add_directory;
tree_editor->open_directory = open_directory;
tree_editor->close_directory = close_directory;
tree_editor->add_file = add_file;
tree_editor->open_file = open_file;
tree_editor->apply_textdelta = apply_textdelta;
tree_editor->change_file_prop = change_file_prop;
tree_editor->change_dir_prop = change_dir_prop;
tree_editor->close_file = close_file;
tree_editor->close_edit = close_edit;
inner_editor = tree_editor;
inner_baton = eb;
if (!server_performs_filtering
&& depth == svn_depth_unknown)
SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor,
&inner_baton,
wc_ctx->db,
anchor_abspath,
target,
inner_editor,
inner_baton,
result_pool));
SVN_ERR(svn_delta_get_cancellation_editor(cancel_func,
cancel_baton,
inner_editor,
inner_baton,
editor,
edit_baton,
result_pool));
sfb = apr_palloc(result_pool, sizeof(*sfb));
sfb->db = wc_ctx->db;
sfb->base_abspath = eb->anchor_abspath;
sfb->fetch_base = TRUE;
shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func;
shim_callbacks->fetch_props_func = svn_wc__fetch_props_func;
shim_callbacks->fetch_base_func = svn_wc__fetch_base_func;
shim_callbacks->fetch_baton = sfb;
SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
NULL, NULL, shim_callbacks,
result_pool, scratch_pool));
return SVN_NO_ERROR;
}
/* Wrapping svn_wc_diff_callbacks4_t as svn_diff_tree_processor_t */
/* baton for the svn_diff_tree_processor_t wrapper */
typedef struct wc_diff_wrap_baton_t
{
const svn_wc_diff_callbacks4_t *callbacks;
void *callback_baton;
svn_boolean_t walk_deleted_dirs;
apr_pool_t *result_pool;
const char *empty_file;
} wc_diff_wrap_baton_t;
static svn_error_t *
wrap_ensure_empty_file(wc_diff_wrap_baton_t *wb,
apr_pool_t *scratch_pool)
{
if (wb->empty_file)
return SVN_NO_ERROR;
/* Create a unique file in the tempdir */
SVN_ERR(svn_io_open_unique_file3(NULL, &wb->empty_file, NULL,
svn_io_file_del_on_pool_cleanup,
wb->result_pool, scratch_pool));
return SVN_NO_ERROR;
}
/* svn_diff_tree_processor_t function */
static svn_error_t *
wrap_dir_opened(void **new_dir_baton,
svn_boolean_t *skip,
svn_boolean_t *skip_children,
const char *relpath,
const svn_diff_source_t *left_source,
const svn_diff_source_t *right_source,
const svn_diff_source_t *copyfrom_source,
void *parent_dir_baton,
const svn_diff_tree_processor_t *processor,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
wc_diff_wrap_baton_t *wb = processor->baton;
svn_boolean_t tree_conflicted = FALSE;
- assert(left_source || right_source);
- assert(!copyfrom_source || !right_source);
+ assert(left_source || right_source); /* Must exist at one point. */
+ assert(!left_source || !copyfrom_source); /* Either existed or added. */
/* Maybe store state and tree_conflicted in baton? */
if (left_source != NULL)
{
/* Open for change or delete */
SVN_ERR(wb->callbacks->dir_opened(&tree_conflicted, skip, skip_children,
relpath,
right_source
? right_source->revision
: (left_source
? left_source->revision
: SVN_INVALID_REVNUM),
wb->callback_baton,
scratch_pool));
if (! right_source && !wb->walk_deleted_dirs)
*skip_children = TRUE;
}
else /* left_source == NULL -> Add */
{
svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
SVN_ERR(wb->callbacks->dir_added(&state, &tree_conflicted,
skip, skip_children,
relpath,
right_source->revision,
copyfrom_source
? copyfrom_source->repos_relpath
: NULL,
copyfrom_source
? copyfrom_source->revision
: SVN_INVALID_REVNUM,
wb->callback_baton,
scratch_pool));
}
*new_dir_baton = NULL;
return SVN_NO_ERROR;
}
/* svn_diff_tree_processor_t function */
static svn_error_t *
wrap_dir_added(const char *relpath,
const svn_diff_source_t *right_source,
const svn_diff_source_t *copyfrom_source,
/*const*/ apr_hash_t *copyfrom_props,
/*const*/ apr_hash_t *right_props,
void *dir_baton,
const svn_diff_tree_processor_t *processor,
apr_pool_t *scratch_pool)
{
wc_diff_wrap_baton_t *wb = processor->baton;
svn_boolean_t tree_conflicted = FALSE;
svn_wc_notify_state_t state = svn_wc_notify_state_unknown;
svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
apr_hash_t *pristine_props = copyfrom_props;
apr_array_header_t *prop_changes = NULL;
if (right_props && apr_hash_count(right_props))
{
if (!pristine_props)
pristine_props = apr_hash_make(scratch_pool);
SVN_ERR(svn_prop_diffs(&prop_changes, right_props, pristine_props,
scratch_pool));
SVN_ERR(wb->callbacks->dir_props_changed(&prop_state,
&tree_conflicted,
relpath,
TRUE /* dir_was_added */,
prop_changes, pristine_props,
wb->callback_baton,
scratch_pool));
}
SVN_ERR(wb->callbacks->dir_closed(&state, &prop_state,
&tree_conflicted,
relpath,
TRUE /* dir_was_added */,
wb->callback_baton,
scratch_pool));
return SVN_NO_ERROR;
}
/* svn_diff_tree_processor_t function */
static svn_error_t *
wrap_dir_deleted(const char *relpath,
const svn_diff_source_t *left_source,
/*const*/ apr_hash_t *left_props,
void *dir_baton,
const svn_diff_tree_processor_t *processor,
apr_pool_t *scratch_pool)
{
wc_diff_wrap_baton_t *wb = processor->baton;
svn_boolean_t tree_conflicted = FALSE;
svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
SVN_ERR(wb->callbacks->dir_deleted(&state, &tree_conflicted,
relpath,
wb->callback_baton,
scratch_pool));
return SVN_NO_ERROR;
}
/* svn_diff_tree_processor_t function */
static svn_error_t *
wrap_dir_closed(const char *relpath,
const svn_diff_source_t *left_source,
const svn_diff_source_t *right_source,
void *dir_baton,
const svn_diff_tree_processor_t *processor,
apr_pool_t *scratch_pool)
{
wc_diff_wrap_baton_t *wb = processor->baton;
/* No previous implementations provided these arguments, so we
are not providing them either */
SVN_ERR(wb->callbacks->dir_closed(NULL, NULL, NULL,
relpath,
FALSE /* added */,
wb->callback_baton,
scratch_pool));
return SVN_NO_ERROR;
}
/* svn_diff_tree_processor_t function */
static svn_error_t *
wrap_dir_changed(const char *relpath,
const svn_diff_source_t *left_source,
const svn_diff_source_t *right_source,
/*const*/ apr_hash_t *left_props,
/*const*/ apr_hash_t *right_props,
const apr_array_header_t *prop_changes,
void *dir_baton,
const struct svn_diff_tree_processor_t *processor,
apr_pool_t *scratch_pool)
{
wc_diff_wrap_baton_t *wb = processor->baton;
svn_boolean_t tree_conflicted = FALSE;
svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable;
assert(left_source && right_source);
SVN_ERR(wb->callbacks->dir_props_changed(&prop_state, &tree_conflicted,
relpath,
FALSE /* dir_was_added */,
prop_changes,
left_props,
wb->callback_baton,
scratch_pool));
/* And call dir_closed, etc */
SVN_ERR(wrap_dir_closed(relpath, left_source, right_source,
dir_baton, processor,
scratch_pool));
return SVN_NO_ERROR;
}
/* svn_diff_tree_processor_t function */
static svn_error_t *
wrap_file_opened(void **new_file_baton,
svn_boolean_t *skip,
const char *relpath,
const svn_diff_source_t *left_source,
const svn_diff_source_t *right_source,
const svn_diff_source_t *copyfrom_source,
void *dir_baton,
const svn_diff_tree_processor_t *processor,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
wc_diff_wrap_baton_t *wb = processor->baton;
svn_boolean_t tree_conflicted = FALSE;
if (left_source) /* If ! added */
SVN_ERR(wb->callbacks->file_opened(&tree_conflicted, skip, relpath,
right_source
? right_source->revision
: (left_source
? left_source->revision
: SVN_INVALID_REVNUM),
wb->callback_baton, scratch_pool));
/* No old implementation used the output arguments for notify */
*new_file_baton = NULL;
return SVN_NO_ERROR;
}
/* svn_diff_tree_processor_t function */
static svn_error_t *
wrap_file_added(const char *relpath,
const svn_diff_source_t *copyfrom_source,
const svn_diff_source_t *right_source,
const char *copyfrom_file,
const char *right_file,
/*const*/ apr_hash_t *copyfrom_props,
/*const*/ apr_hash_t *right_props,
void *file_baton,
const svn_diff_tree_processor_t *processor,
apr_pool_t *scratch_pool)
{
wc_diff_wrap_baton_t *wb = processor->baton;
svn_boolean_t tree_conflicted = FALSE;
svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable;
apr_array_header_t *prop_changes;
if (! copyfrom_props)
copyfrom_props = apr_hash_make(scratch_pool);
SVN_ERR(svn_prop_diffs(&prop_changes, right_props, copyfrom_props,
scratch_pool));
if (! copyfrom_source)
SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool));
SVN_ERR(wb->callbacks->file_added(&state, &prop_state, &tree_conflicted,
relpath,
copyfrom_source
? copyfrom_file
: wb->empty_file,
right_file,
0,
right_source->revision,
copyfrom_props
? svn_prop_get_value(copyfrom_props,
SVN_PROP_MIME_TYPE)
: NULL,
right_props
? svn_prop_get_value(right_props,
SVN_PROP_MIME_TYPE)
: NULL,
copyfrom_source
? copyfrom_source->repos_relpath
: NULL,
copyfrom_source
? copyfrom_source->revision
: SVN_INVALID_REVNUM,
prop_changes, copyfrom_props,
wb->callback_baton,
scratch_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
wrap_file_deleted(const char *relpath,
const svn_diff_source_t *left_source,
const char *left_file,
apr_hash_t *left_props,
void *file_baton,
const svn_diff_tree_processor_t *processor,
apr_pool_t *scratch_pool)
{
wc_diff_wrap_baton_t *wb = processor->baton;
svn_boolean_t tree_conflicted = FALSE;
svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool));
SVN_ERR(wb->callbacks->file_deleted(&state, &tree_conflicted,
relpath,
left_file, wb->empty_file,
left_props
? svn_prop_get_value(left_props,
SVN_PROP_MIME_TYPE)
: NULL,
NULL,
left_props,
wb->callback_baton,
scratch_pool));
return SVN_NO_ERROR;
}
/* svn_diff_tree_processor_t function */
static svn_error_t *
wrap_file_changed(const char *relpath,
const svn_diff_source_t *left_source,
const svn_diff_source_t *right_source,
const char *left_file,
const char *right_file,
/*const*/ apr_hash_t *left_props,
/*const*/ apr_hash_t *right_props,
svn_boolean_t file_modified,
const apr_array_header_t *prop_changes,
void *file_baton,
const svn_diff_tree_processor_t *processor,
apr_pool_t *scratch_pool)
{
wc_diff_wrap_baton_t *wb = processor->baton;
svn_boolean_t tree_conflicted = FALSE;
svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
svn_wc_notify_state_t prop_state = svn_wc_notify_state_inapplicable;
SVN_ERR(wrap_ensure_empty_file(wb, scratch_pool));
assert(left_source && right_source);
SVN_ERR(wb->callbacks->file_changed(&state, &prop_state, &tree_conflicted,
relpath,
file_modified ? left_file : NULL,
file_modified ? right_file : NULL,
left_source->revision,
right_source->revision,
left_props
? svn_prop_get_value(left_props,
SVN_PROP_MIME_TYPE)
: NULL,
right_props
? svn_prop_get_value(right_props,
SVN_PROP_MIME_TYPE)
: NULL,
prop_changes,
left_props,
wb->callback_baton,
scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__wrap_diff_callbacks(const svn_diff_tree_processor_t **diff_processor,
const svn_wc_diff_callbacks4_t *callbacks,
void *callback_baton,
svn_boolean_t walk_deleted_dirs,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
wc_diff_wrap_baton_t *wrap_baton;
svn_diff_tree_processor_t *processor;
wrap_baton = apr_pcalloc(result_pool, sizeof(*wrap_baton));
wrap_baton->result_pool = result_pool;
wrap_baton->callbacks = callbacks;
wrap_baton->callback_baton = callback_baton;
wrap_baton->empty_file = NULL;
wrap_baton->walk_deleted_dirs = walk_deleted_dirs;
processor = svn_diff__tree_processor_create(wrap_baton, result_pool);
processor->dir_opened = wrap_dir_opened;
processor->dir_added = wrap_dir_added;
processor->dir_deleted = wrap_dir_deleted;
processor->dir_changed = wrap_dir_changed;
processor->dir_closed = wrap_dir_closed;
processor->file_opened = wrap_file_opened;
processor->file_added = wrap_file_added;
processor->file_deleted = wrap_file_deleted;
processor->file_changed = wrap_file_changed;
/*processor->file_closed = wrap_file_closed*/; /* Not needed */
*diff_processor = processor;
return SVN_NO_ERROR;
}
+
+/* =====================================================================
+ * A tree processor filter that filters by changelist membership
+ * =====================================================================
+ *
+ * The current implementation queries the WC for the changelist of each
+ * file as it comes through, and sets the 'skip' flag for a non-matching
+ * file.
+ *
+ * (It doesn't set the 'skip' flag for a directory, as we need to receive
+ * the changed/added/deleted/closed call to know when it is closed, in
+ * order to preserve the strict open-close semantics for the wrapped tree
+ * processor.)
+ *
+ * It passes on the opening and closing of every directory, even if there
+ * are no file changes to be passed on inside that directory.
+ */
+
+typedef struct filter_tree_baton_t
+{
+ const svn_diff_tree_processor_t *processor;
+ svn_wc_context_t *wc_ctx;
+ /* WC path of the root of the diff (where relpath = "") */
+ const char *root_local_abspath;
+ /* Hash whose keys are const char * changelist names. */
+ apr_hash_t *changelist_hash;
+} filter_tree_baton_t;
+
+static svn_error_t *
+filter_dir_opened(void **new_dir_baton,
+ svn_boolean_t *skip,
+ svn_boolean_t *skip_children,
+ const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ const svn_diff_source_t *copyfrom_source,
+ void *parent_dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct filter_tree_baton_t *fb = processor->baton;
+
+ SVN_ERR(fb->processor->dir_opened(new_dir_baton, skip, skip_children,
+ relpath,
+ left_source, right_source,
+ copyfrom_source,
+ parent_dir_baton,
+ fb->processor,
+ result_pool, scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_dir_added(const char *relpath,
+ const svn_diff_source_t *copyfrom_source,
+ const svn_diff_source_t *right_source,
+ /*const*/ apr_hash_t *copyfrom_props,
+ /*const*/ apr_hash_t *right_props,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct filter_tree_baton_t *fb = processor->baton;
+
+ SVN_ERR(fb->processor->dir_closed(relpath,
+ NULL,
+ right_source,
+ dir_baton,
+ fb->processor,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_dir_deleted(const char *relpath,
+ const svn_diff_source_t *left_source,
+ /*const*/ apr_hash_t *left_props,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct filter_tree_baton_t *fb = processor->baton;
+
+ SVN_ERR(fb->processor->dir_closed(relpath,
+ left_source,
+ NULL,
+ dir_baton,
+ fb->processor,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_dir_changed(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ /*const*/ apr_hash_t *left_props,
+ /*const*/ apr_hash_t *right_props,
+ const apr_array_header_t *prop_changes,
+ void *dir_baton,
+ const struct svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct filter_tree_baton_t *fb = processor->baton;
+
+ SVN_ERR(fb->processor->dir_closed(relpath,
+ left_source,
+ right_source,
+ dir_baton,
+ fb->processor,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_dir_closed(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct filter_tree_baton_t *fb = processor->baton;
+
+ SVN_ERR(fb->processor->dir_closed(relpath,
+ left_source,
+ right_source,
+ dir_baton,
+ fb->processor,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_file_opened(void **new_file_baton,
+ svn_boolean_t *skip,
+ const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ const svn_diff_source_t *copyfrom_source,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct filter_tree_baton_t *fb = processor->baton;
+ const char *local_abspath
+ = svn_dirent_join(fb->root_local_abspath, relpath, scratch_pool);
+
+ /* Skip if not a member of a given changelist */
+ if (! svn_wc__changelist_match(fb->wc_ctx, local_abspath,
+ fb->changelist_hash, scratch_pool))
+ {
+ *skip = TRUE;
+ return SVN_NO_ERROR;
+ }
+
+ SVN_ERR(fb->processor->file_opened(new_file_baton,
+ skip,
+ relpath,
+ left_source,
+ right_source,
+ copyfrom_source,
+ dir_baton,
+ fb->processor,
+ result_pool,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_file_added(const char *relpath,
+ const svn_diff_source_t *copyfrom_source,
+ const svn_diff_source_t *right_source,
+ const char *copyfrom_file,
+ const char *right_file,
+ /*const*/ apr_hash_t *copyfrom_props,
+ /*const*/ apr_hash_t *right_props,
+ void *file_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct filter_tree_baton_t *fb = processor->baton;
+
+ SVN_ERR(fb->processor->file_added(relpath,
+ copyfrom_source,
+ right_source,
+ copyfrom_file,
+ right_file,
+ copyfrom_props,
+ right_props,
+ file_baton,
+ fb->processor,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_file_deleted(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const char *left_file,
+ /*const*/ apr_hash_t *left_props,
+ void *file_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct filter_tree_baton_t *fb = processor->baton;
+
+ SVN_ERR(fb->processor->file_deleted(relpath,
+ left_source,
+ left_file,
+ left_props,
+ file_baton,
+ fb->processor,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_file_changed(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ const char *left_file,
+ const char *right_file,
+ /*const*/ apr_hash_t *left_props,
+ /*const*/ apr_hash_t *right_props,
+ svn_boolean_t file_modified,
+ const apr_array_header_t *prop_changes,
+ void *file_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct filter_tree_baton_t *fb = processor->baton;
+
+ SVN_ERR(fb->processor->file_changed(relpath,
+ left_source,
+ right_source,
+ left_file,
+ right_file,
+ left_props,
+ right_props,
+ file_modified,
+ prop_changes,
+ file_baton,
+ fb->processor,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_file_closed(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ void *file_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct filter_tree_baton_t *fb = processor->baton;
+
+ SVN_ERR(fb->processor->file_closed(relpath,
+ left_source,
+ right_source,
+ file_baton,
+ fb->processor,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_node_absent(const char *relpath,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct filter_tree_baton_t *fb = processor->baton;
+
+ SVN_ERR(fb->processor->node_absent(relpath,
+ dir_baton,
+ fb->processor,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+const svn_diff_tree_processor_t *
+svn_wc__changelist_filter_tree_processor_create(
+ const svn_diff_tree_processor_t *processor,
+ svn_wc_context_t *wc_ctx,
+ const char *root_local_abspath,
+ apr_hash_t *changelist_hash,
+ apr_pool_t *result_pool)
+{
+ struct filter_tree_baton_t *fb;
+ svn_diff_tree_processor_t *filter;
+
+ if (! changelist_hash)
+ return processor;
+
+ fb = apr_pcalloc(result_pool, sizeof(*fb));
+ fb->processor = processor;
+ fb->wc_ctx = wc_ctx;
+ fb->root_local_abspath = root_local_abspath;
+ fb->changelist_hash = changelist_hash;
+
+ filter = svn_diff__tree_processor_create(fb, result_pool);
+ filter->dir_opened = filter_dir_opened;
+ filter->dir_added = filter_dir_added;
+ filter->dir_deleted = filter_dir_deleted;
+ filter->dir_changed = filter_dir_changed;
+ filter->dir_closed = filter_dir_closed;
+
+ filter->file_opened = filter_file_opened;
+ filter->file_added = filter_file_added;
+ filter->file_deleted = filter_file_deleted;
+ filter->file_changed = filter_file_changed;
+ filter->file_closed = filter_file_closed;
+
+ filter->node_absent = filter_node_absent;
+
+ return filter;
+}
+
Index: vendor/subversion/dist/subversion/libsvn_wc/diff_local.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_wc/diff_local.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_wc/diff_local.c (revision 286501)
@@ -1,540 +1,537 @@
/*
* diff_pristine.c -- A simple diff walker which compares local files against
* their pristine versions.
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This is the simple working copy diff algorithm which is used when you
* just use 'svn diff PATH'. It shows what is modified in your working copy
* since a node was checked out or copied but doesn't show most kinds of
* restructuring operations.
*
* You can look at this as another form of the status walker.
*/
#include <apr_hash.h>
#include "svn_error.h"
#include "svn_pools.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_hash.h"
#include "private/svn_wc_private.h"
#include "private/svn_diff_tree.h"
#include "wc.h"
#include "props.h"
#include "translate.h"
#include "diff.h"
#include "svn_private_config.h"
/*-------------------------------------------------------------------------*/
/* Baton containing the state of a directory
reported open via a diff processor */
struct node_state_t
{
struct node_state_t *parent;
apr_pool_t *pool;
const char *local_abspath;
const char *relpath;
void *baton;
svn_diff_source_t *left_src;
svn_diff_source_t *right_src;
svn_diff_source_t *copy_src;
svn_boolean_t skip;
svn_boolean_t skip_children;
apr_hash_t *left_props;
apr_hash_t *right_props;
const apr_array_header_t *propchanges;
};
/* The diff baton */
struct diff_baton
{
/* A wc db. */
svn_wc__db_t *db;
/* Report editor paths relative from this directory */
const char *anchor_abspath;
struct node_state_t *cur;
const svn_diff_tree_processor_t *processor;
/* Should this diff ignore node ancestry? */
svn_boolean_t ignore_ancestry;
/* Should this diff not compare copied files with their source? */
svn_boolean_t show_copies_as_adds;
- /* Hash whose keys are const char * changelist names. */
- apr_hash_t *changelist_hash;
-
/* Cancel function/baton */
svn_cancel_func_t cancel_func;
void *cancel_baton;
apr_pool_t *pool;
};
/* Recursively opens directories on the stack in EB, until LOCAL_ABSPATH
is reached. If RECURSIVE_SKIP is TRUE, don't open LOCAL_ABSPATH itself,
but create it marked with skip+skip_children.
*/
static svn_error_t *
ensure_state(struct diff_baton *eb,
const char *local_abspath,
svn_boolean_t recursive_skip,
apr_pool_t *scratch_pool)
{
struct node_state_t *ns;
apr_pool_t *ns_pool;
if (!eb->cur)
{
const char *relpath;
relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, local_abspath);
if (! relpath)
return SVN_NO_ERROR;
/* Don't recurse on the anchor, as that might loop infinately because
svn_dirent_dirname("/",...) -> "/"
svn_dirent_dirname("C:/",...) -> "C:/" (Windows) */
if (*relpath)
SVN_ERR(ensure_state(eb,
svn_dirent_dirname(local_abspath,scratch_pool),
FALSE,
scratch_pool));
}
else if (svn_dirent_is_child(eb->cur->local_abspath, local_abspath, NULL))
SVN_ERR(ensure_state(eb, svn_dirent_dirname(local_abspath,scratch_pool),
FALSE,
scratch_pool));
else
return SVN_NO_ERROR;
if (eb->cur && eb->cur->skip_children)
return SVN_NO_ERROR;
ns_pool = svn_pool_create(eb->cur ? eb->cur->pool : eb->pool);
ns = apr_pcalloc(ns_pool, sizeof(*ns));
ns->pool = ns_pool;
ns->local_abspath = apr_pstrdup(ns_pool, local_abspath);
ns->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, ns->local_abspath);
ns->parent = eb->cur;
eb->cur = ns;
if (recursive_skip)
{
ns->skip = TRUE;
ns->skip_children = TRUE;
return SVN_NO_ERROR;
}
{
svn_revnum_t revision;
svn_error_t *err;
err = svn_wc__db_base_get_info(NULL, NULL, &revision, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL,
eb->db, local_abspath,
scratch_pool, scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
svn_error_clear(err);
revision = 0; /* Use original revision? */
}
ns->left_src = svn_diff__source_create(revision, ns->pool);
ns->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, ns->pool);
SVN_ERR(eb->processor->dir_opened(&ns->baton, &ns->skip,
&ns->skip_children,
ns->relpath,
ns->left_src,
ns->right_src,
NULL /* copyfrom_source */,
ns->parent ? ns->parent->baton : NULL,
eb->processor,
ns->pool, scratch_pool));
}
return SVN_NO_ERROR;
}
/* Implements svn_wc_status_func3_t */
static svn_error_t *
diff_status_callback(void *baton,
const char *local_abspath,
const svn_wc_status3_t *status,
apr_pool_t *scratch_pool)
{
struct diff_baton *eb = baton;
svn_wc__db_t *db = eb->db;
if (! status->versioned)
return SVN_NO_ERROR; /* unversioned (includes dir externals) */
if (status->node_status == svn_wc_status_conflicted
&& status->text_status == svn_wc_status_none
&& status->prop_status == svn_wc_status_none)
{
/* Node is an actual only node describing a tree conflict */
return SVN_NO_ERROR;
}
/* Not text/prop modified, not copied. Easy out */
if (status->node_status == svn_wc_status_normal && !status->copied)
return SVN_NO_ERROR;
/* Mark all directories where we are no longer inside as closed */
while (eb->cur
&& !svn_dirent_is_ancestor(eb->cur->local_abspath, local_abspath))
{
struct node_state_t *ns = eb->cur;
if (!ns->skip)
{
if (ns->propchanges)
SVN_ERR(eb->processor->dir_changed(ns->relpath,
ns->left_src,
ns->right_src,
ns->left_props,
ns->right_props,
ns->propchanges,
ns->baton,
eb->processor,
ns->pool));
else
SVN_ERR(eb->processor->dir_closed(ns->relpath,
ns->left_src,
ns->right_src,
ns->baton,
eb->processor,
ns->pool));
}
eb->cur = ns->parent;
svn_pool_clear(ns->pool);
}
SVN_ERR(ensure_state(eb, svn_dirent_dirname(local_abspath, scratch_pool),
FALSE, scratch_pool));
if (eb->cur && eb->cur->skip_children)
return SVN_NO_ERROR;
- if (eb->changelist_hash != NULL
- && (!status->changelist
- || ! svn_hash_gets(eb->changelist_hash, status->changelist)))
- return SVN_NO_ERROR; /* Filtered via changelist */
-
/* This code does about the same thing as the inner body of
walk_local_nodes_diff() in diff_editor.c, except that
it is already filtered by the status walker, doesn't have to
account for remote changes (and many tiny other details) */
{
svn_boolean_t repos_only;
svn_boolean_t local_only;
svn_wc__db_status_t db_status;
svn_boolean_t have_base;
svn_node_kind_t base_kind;
svn_node_kind_t db_kind = status->kind;
svn_depth_t depth_below_here = svn_depth_unknown;
const char *child_abspath = local_abspath;
const char *child_relpath = svn_dirent_skip_ancestor(eb->anchor_abspath,
local_abspath);
repos_only = FALSE;
local_only = FALSE;
/* ### optimize away this call using status info. Should
be possible in almost every case (except conflict, missing, obst.)*/
SVN_ERR(svn_wc__db_read_info(&db_status, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
&have_base, NULL, NULL,
eb->db, local_abspath,
scratch_pool, scratch_pool));
if (!have_base)
{
local_only = TRUE; /* Only report additions */
}
else if (db_status == svn_wc__db_status_normal)
{
/* Simple diff */
base_kind = db_kind;
}
else if (db_status == svn_wc__db_status_deleted)
{
svn_wc__db_status_t base_status;
repos_only = TRUE;
SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL,
eb->db, local_abspath,
scratch_pool, scratch_pool));
if (base_status != svn_wc__db_status_normal)
return SVN_NO_ERROR;
}
else
{
/* working status is either added or deleted */
svn_wc__db_status_t base_status;
SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL,
eb->db, local_abspath,
scratch_pool, scratch_pool));
if (base_status != svn_wc__db_status_normal)
local_only = TRUE;
else if (base_kind != db_kind || !eb->ignore_ancestry)
{
repos_only = TRUE;
local_only = TRUE;
}
}
if (repos_only)
{
/* Report repository form deleted */
if (base_kind == svn_node_file)
SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath,
child_relpath,
SVN_INVALID_REVNUM,
eb->processor,
eb->cur ? eb->cur->baton : NULL,
scratch_pool));
else if (base_kind == svn_node_dir)
SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath,
child_relpath,
SVN_INVALID_REVNUM,
depth_below_here,
eb->processor,
eb->cur ? eb->cur->baton : NULL,
eb->cancel_func,
eb->cancel_baton,
scratch_pool));
}
else if (!local_only)
{
/* Diff base against actual */
if (db_kind == svn_node_file)
{
SVN_ERR(svn_wc__diff_base_working_diff(db, child_abspath,
child_relpath,
SVN_INVALID_REVNUM,
- eb->changelist_hash,
eb->processor,
eb->cur
? eb->cur->baton
: NULL,
FALSE,
eb->cancel_func,
eb->cancel_baton,
scratch_pool));
}
else if (db_kind == svn_node_dir)
{
SVN_ERR(ensure_state(eb, local_abspath, FALSE, scratch_pool));
if (status->prop_status != svn_wc_status_none
&& status->prop_status != svn_wc_status_normal)
{
apr_array_header_t *propchanges;
SVN_ERR(svn_wc__db_base_get_props(&eb->cur->left_props,
eb->db, local_abspath,
eb->cur->pool,
scratch_pool));
SVN_ERR(svn_wc__db_read_props(&eb->cur->right_props,
eb->db, local_abspath,
eb->cur->pool,
scratch_pool));
SVN_ERR(svn_prop_diffs(&propchanges,
eb->cur->right_props,
eb->cur->left_props,
eb->cur->pool));
eb->cur->propchanges = propchanges;
}
}
}
if (local_only && (db_status != svn_wc__db_status_deleted))
{
if (db_kind == svn_node_file)
SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
child_relpath,
eb->processor,
eb->cur ? eb->cur->baton : NULL,
- eb->changelist_hash,
FALSE,
eb->cancel_func,
eb->cancel_baton,
scratch_pool));
else if (db_kind == svn_node_dir)
SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
child_relpath, depth_below_here,
eb->processor,
eb->cur ? eb->cur->baton : NULL,
- eb->changelist_hash,
FALSE,
eb->cancel_func,
eb->cancel_baton,
scratch_pool));
}
if (db_kind == svn_node_dir && (local_only || repos_only))
SVN_ERR(ensure_state(eb, local_abspath, TRUE /* skip */, scratch_pool));
}
return SVN_NO_ERROR;
}
/* Public Interface */
svn_error_t *
svn_wc_diff6(svn_wc_context_t *wc_ctx,
const char *local_abspath,
const svn_wc_diff_callbacks4_t *callbacks,
void *callback_baton,
svn_depth_t depth,
svn_boolean_t ignore_ancestry,
svn_boolean_t show_copies_as_adds,
svn_boolean_t use_git_diff_format,
const apr_array_header_t *changelist_filter,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
struct diff_baton eb = { 0 };
svn_node_kind_t kind;
svn_boolean_t get_all;
const svn_diff_tree_processor_t *processor;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_read_kind(&kind, wc_ctx->db, local_abspath,
FALSE /* allow_missing */,
TRUE /* show_deleted */,
FALSE /* show_hidden */,
scratch_pool));
if (kind == svn_node_dir)
eb.anchor_abspath = local_abspath;
else
eb.anchor_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
SVN_ERR(svn_wc__wrap_diff_callbacks(&processor,
callbacks, callback_baton, TRUE,
scratch_pool, scratch_pool));
if (use_git_diff_format)
show_copies_as_adds = TRUE;
if (show_copies_as_adds)
ignore_ancestry = FALSE;
/*
if (reverse_order)
processor = svn_diff__tree_processor_reverse_create(processor, NULL, pool);
*/
if (! show_copies_as_adds && !use_git_diff_format)
processor = svn_diff__tree_processor_copy_as_changed_create(processor,
scratch_pool);
+ /* Apply changelist filtering to the output */
+ if (changelist_filter && changelist_filter->nelts)
+ {
+ apr_hash_t *changelist_hash;
+
+ SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelist_filter,
+ scratch_pool));
+ processor = svn_wc__changelist_filter_tree_processor_create(
+ processor, wc_ctx, local_abspath,
+ changelist_hash, scratch_pool);
+ }
+
eb.db = wc_ctx->db;
eb.processor = processor;
eb.ignore_ancestry = ignore_ancestry;
eb.show_copies_as_adds = show_copies_as_adds;
eb.pool = scratch_pool;
-
- if (changelist_filter && changelist_filter->nelts)
- SVN_ERR(svn_hash_from_cstring_keys(&eb.changelist_hash, changelist_filter,
- scratch_pool));
if (show_copies_as_adds || use_git_diff_format || !ignore_ancestry)
get_all = TRUE; /* We need unmodified descendants of copies */
else
get_all = FALSE;
/* Walk status handles files and directories */
SVN_ERR(svn_wc__internal_walk_status(wc_ctx->db, local_abspath, depth,
get_all,
TRUE /* no_ignore */,
FALSE /* ignore_text_mods */,
NULL /* ignore_patterns */,
diff_status_callback, &eb,
cancel_func, cancel_baton,
scratch_pool));
/* Close the remaining open directories */
while (eb.cur)
{
struct node_state_t *ns = eb.cur;
if (!ns->skip)
{
if (ns->propchanges)
SVN_ERR(processor->dir_changed(ns->relpath,
ns->left_src,
ns->right_src,
ns->left_props,
ns->right_props,
ns->propchanges,
ns->baton,
processor,
ns->pool));
else
SVN_ERR(processor->dir_closed(ns->relpath,
ns->left_src,
ns->right_src,
ns->baton,
processor,
ns->pool));
}
eb.cur = ns->parent;
svn_pool_clear(ns->pool);
}
return SVN_NO_ERROR;
}
Index: vendor/subversion/dist/subversion/libsvn_wc/entries.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_wc/entries.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_wc/entries.c (revision 286501)
@@ -1,2738 +1,2739 @@
/*
* entries.c : manipulating the administrative `entries' file.
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include <string.h>
#include <assert.h>
#include <apr_strings.h>
#include "svn_error.h"
#include "svn_types.h"
#include "svn_time.h"
#include "svn_pools.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_ctype.h"
#include "svn_string.h"
#include "svn_hash.h"
#include "wc.h"
#include "adm_files.h"
#include "conflicts.h"
#include "entries.h"
#include "lock.h"
#include "tree_conflicts.h"
#include "wc_db.h"
#include "wc-queries.h" /* for STMT_* */
#include "svn_private_config.h"
#include "private/svn_wc_private.h"
#include "private/svn_sqlite.h"
#define MAYBE_ALLOC(x,p) ((x) ? (x) : apr_pcalloc((p), sizeof(*(x))))
/* Temporary structures which mirror the tables in wc-metadata.sql.
For detailed descriptions of each field, see that file. */
typedef struct db_node_t {
apr_int64_t wc_id;
const char *local_relpath;
int op_depth;
apr_int64_t repos_id;
const char *repos_relpath;
const char *parent_relpath;
svn_wc__db_status_t presence;
svn_revnum_t revision;
svn_node_kind_t kind;
svn_checksum_t *checksum;
svn_filesize_t recorded_size;
svn_revnum_t changed_rev;
apr_time_t changed_date;
const char *changed_author;
svn_depth_t depth;
apr_time_t recorded_time;
apr_hash_t *properties;
svn_boolean_t file_external;
apr_array_header_t *inherited_props;
} db_node_t;
typedef struct db_actual_node_t {
apr_int64_t wc_id;
const char *local_relpath;
const char *parent_relpath;
apr_hash_t *properties;
const char *conflict_old;
const char *conflict_new;
const char *conflict_working;
const char *prop_reject;
const char *changelist;
/* ### enum for text_mod */
const char *tree_conflict_data;
} db_actual_node_t;
/*** reading and writing the entries file ***/
/* */
static svn_wc_entry_t *
alloc_entry(apr_pool_t *pool)
{
svn_wc_entry_t *entry = apr_pcalloc(pool, sizeof(*entry));
entry->revision = SVN_INVALID_REVNUM;
entry->copyfrom_rev = SVN_INVALID_REVNUM;
entry->cmt_rev = SVN_INVALID_REVNUM;
entry->kind = svn_node_none;
entry->working_size = SVN_WC_ENTRY_WORKING_SIZE_UNKNOWN;
entry->depth = svn_depth_infinity;
entry->file_external_peg_rev.kind = svn_opt_revision_unspecified;
entry->file_external_rev.kind = svn_opt_revision_unspecified;
return entry;
}
/* Is the entry in a 'hidden' state in the sense of the 'show_hidden'
* switches on svn_wc_entries_read(), svn_wc_walk_entries*(), etc.? */
svn_error_t *
svn_wc__entry_is_hidden(svn_boolean_t *hidden, const svn_wc_entry_t *entry)
{
/* In English, the condition is: "the entry is not present, and I haven't
scheduled something over the top of it." */
if (entry->deleted
|| entry->absent
|| entry->depth == svn_depth_exclude)
{
/* These kinds of nodes cannot be marked for deletion (which also
means no "replace" either). */
SVN_ERR_ASSERT(entry->schedule == svn_wc_schedule_add
|| entry->schedule == svn_wc_schedule_normal);
/* Hidden if something hasn't been added over it.
### is this even possible with absent or excluded nodes? */
*hidden = entry->schedule != svn_wc_schedule_add;
}
else
*hidden = FALSE;
return SVN_NO_ERROR;
}
/* Hit the database to check the file external information for the given
entry. The entry will be modified in place. */
static svn_error_t *
check_file_external(svn_wc_entry_t *entry,
svn_wc__db_t *db,
const char *local_abspath,
const char *wri_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_status_t status;
svn_node_kind_t kind;
const char *repos_relpath;
svn_revnum_t peg_revision;
svn_revnum_t revision;
svn_error_t *err;
err = svn_wc__db_external_read(&status, &kind, NULL, NULL, NULL,
&repos_relpath, &peg_revision, &revision,
db, local_abspath, wri_abspath,
result_pool, scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
svn_error_clear(err);
return SVN_NO_ERROR;
}
if (status == svn_wc__db_status_normal
&& kind == svn_node_file)
{
entry->file_external_path = repos_relpath;
if (SVN_IS_VALID_REVNUM(peg_revision))
{
entry->file_external_peg_rev.kind = svn_opt_revision_number;
entry->file_external_peg_rev.value.number = peg_revision;
entry->file_external_rev = entry->file_external_peg_rev;
}
if (SVN_IS_VALID_REVNUM(revision))
{
entry->file_external_rev.kind = svn_opt_revision_number;
entry->file_external_rev.value.number = revision;
}
}
return SVN_NO_ERROR;
}
/* Fill in the following fields of ENTRY:
REVISION
REPOS
UUID
CMT_REV
CMT_DATE
CMT_AUTHOR
DEPTH
DELETED
Return: KIND, REPOS_RELPATH, CHECKSUM
*/
static svn_error_t *
get_info_for_deleted(svn_wc_entry_t *entry,
svn_node_kind_t *kind,
const char **repos_relpath,
const svn_checksum_t **checksum,
svn_wc__db_lock_t **lock,
svn_wc__db_t *db,
const char *entry_abspath,
const svn_wc_entry_t *parent_entry,
svn_boolean_t have_base,
svn_boolean_t have_more_work,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
if (have_base && !have_more_work)
{
/* This is the delete of a BASE node */
SVN_ERR(svn_wc__db_base_get_info(NULL, kind,
&entry->revision,
repos_relpath,
&entry->repos,
&entry->uuid,
&entry->cmt_rev,
&entry->cmt_date,
&entry->cmt_author,
&entry->depth,
checksum,
NULL,
lock,
&entry->has_props, NULL,
NULL,
db,
entry_abspath,
result_pool,
scratch_pool));
}
else
{
const char *work_del_abspath;
const char *parent_repos_relpath;
const char *parent_abspath;
/* This is a deleted child of a copy/move-here,
so we need to scan up the WORKING tree to find the root of
the deletion. Then examine its parent to discover its
future location in the repository. */
SVN_ERR(svn_wc__db_read_pristine_info(NULL, kind,
&entry->cmt_rev,
&entry->cmt_date,
&entry->cmt_author,
&entry->depth,
checksum,
NULL,
&entry->has_props, NULL,
db,
entry_abspath,
result_pool,
scratch_pool));
/* working_size and text_time unavailable */
SVN_ERR(svn_wc__db_scan_deletion(NULL,
NULL,
&work_del_abspath, NULL,
db, entry_abspath,
scratch_pool, scratch_pool));
SVN_ERR_ASSERT(work_del_abspath != NULL);
parent_abspath = svn_dirent_dirname(work_del_abspath, scratch_pool);
/* The parent directory of the delete root must be added, so we
can find the required information there */
SVN_ERR(svn_wc__db_scan_addition(NULL, NULL,
&parent_repos_relpath,
&entry->repos,
&entry->uuid,
NULL, NULL, NULL, NULL,
db, parent_abspath,
result_pool, scratch_pool));
/* Now glue it all together */
*repos_relpath = svn_relpath_join(parent_repos_relpath,
svn_dirent_is_child(parent_abspath,
entry_abspath,
NULL),
result_pool);
/* Even though this is the delete of a WORKING node, there might still
be a BASE node somewhere below with an interesting revision */
if (have_base)
{
svn_wc__db_status_t status;
SVN_ERR(svn_wc__db_base_get_info(&status, NULL, &entry->revision,
NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, lock, NULL, NULL,
NULL,
db, entry_abspath,
result_pool, scratch_pool));
if (status == svn_wc__db_status_not_present)
entry->deleted = TRUE;
}
}
/* Do some extra work for the child nodes. */
if (!SVN_IS_VALID_REVNUM(entry->revision) && parent_entry != NULL)
{
/* For child nodes without a revision, pick up the parent's
revision. */
entry->revision = parent_entry->revision;
}
return SVN_NO_ERROR;
}
/*
* Encode tree conflict descriptions into a single string.
*
* Set *CONFLICT_DATA to a string, allocated in POOL, that encodes the tree
* conflicts in CONFLICTS in a form suitable for storage in a single string
* field in a WC entry. CONFLICTS is a hash of zero or more pointers to
* svn_wc_conflict_description2_t objects, index by their basenames. All of the
* conflict victim paths must be siblings.
*
* Do all allocations in POOL.
*
* @see svn_wc__read_tree_conflicts()
*/
static svn_error_t *
write_tree_conflicts(const char **conflict_data,
apr_hash_t *conflicts,
apr_pool_t *pool)
{
svn_skel_t *skel = svn_skel__make_empty_list(pool);
apr_hash_index_t *hi;
for (hi = apr_hash_first(pool, conflicts); hi; hi = apr_hash_next(hi))
{
svn_skel_t *c_skel;
SVN_ERR(svn_wc__serialize_conflict(&c_skel, svn__apr_hash_index_val(hi),
pool, pool));
svn_skel__prepend(c_skel, skel);
}
*conflict_data = svn_skel__unparse(skel, pool)->data;
return SVN_NO_ERROR;
}
/* Read one entry from wc_db. It will be allocated in RESULT_POOL and
returned in *NEW_ENTRY.
DIR_ABSPATH is the name of the directory to read this entry from, and
it will be named NAME (use "" for "this dir").
DB specifies the wc_db database, and WC_ID specifies which working copy
this information is being read from.
If this node is "this dir", then PARENT_ENTRY should be NULL. Otherwise,
it should refer to the entry for the child's parent directory.
Temporary allocations are made in SCRATCH_POOL. */
static svn_error_t *
read_one_entry(const svn_wc_entry_t **new_entry,
svn_wc__db_t *db,
apr_int64_t wc_id,
const char *dir_abspath,
const char *name,
const svn_wc_entry_t *parent_entry,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_node_kind_t kind;
svn_wc__db_status_t status;
svn_wc__db_lock_t *lock;
const char *repos_relpath;
const svn_checksum_t *checksum;
svn_filesize_t translated_size;
svn_wc_entry_t *entry = alloc_entry(result_pool);
const char *entry_abspath;
const char *original_repos_relpath;
const char *original_root_url;
svn_boolean_t conflicted;
svn_boolean_t have_base;
svn_boolean_t have_more_work;
entry->name = name;
entry_abspath = svn_dirent_join(dir_abspath, entry->name, scratch_pool);
SVN_ERR(svn_wc__db_read_info(
&status,
&kind,
&entry->revision,
&repos_relpath,
&entry->repos,
&entry->uuid,
&entry->cmt_rev,
&entry->cmt_date,
&entry->cmt_author,
&entry->depth,
&checksum,
NULL,
&original_repos_relpath,
&original_root_url,
NULL,
&entry->copyfrom_rev,
&lock,
&translated_size,
&entry->text_time,
&entry->changelist,
&conflicted,
NULL /* op_root */,
&entry->has_props /* have_props */,
&entry->has_prop_mods /* props_mod */,
&have_base,
&have_more_work,
NULL /* have_work */,
db,
entry_abspath,
result_pool,
scratch_pool));
if (entry->has_prop_mods)
entry->has_props = TRUE;
if (strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR) == 0)
{
/* get the tree conflict data. */
apr_hash_t *tree_conflicts = NULL;
const apr_array_header_t *conflict_victims;
int k;
SVN_ERR(svn_wc__db_read_conflict_victims(&conflict_victims, db,
dir_abspath,
scratch_pool,
scratch_pool));
for (k = 0; k < conflict_victims->nelts; k++)
{
int j;
const apr_array_header_t *child_conflicts;
const char *child_name;
const char *child_abspath;
child_name = APR_ARRAY_IDX(conflict_victims, k, const char *);
child_abspath = svn_dirent_join(dir_abspath, child_name,
scratch_pool);
SVN_ERR(svn_wc__read_conflicts(&child_conflicts,
db, child_abspath,
FALSE /* create tempfiles */,
scratch_pool, scratch_pool));
for (j = 0; j < child_conflicts->nelts; j++)
{
const svn_wc_conflict_description2_t *conflict =
APR_ARRAY_IDX(child_conflicts, j,
svn_wc_conflict_description2_t *);
if (conflict->kind == svn_wc_conflict_kind_tree)
{
if (!tree_conflicts)
tree_conflicts = apr_hash_make(scratch_pool);
svn_hash_sets(tree_conflicts, child_name, conflict);
}
}
}
if (tree_conflicts)
{
SVN_ERR(write_tree_conflicts(&entry->tree_conflict_data,
tree_conflicts, result_pool));
}
}
if (status == svn_wc__db_status_normal
|| status == svn_wc__db_status_incomplete)
{
/* Plain old BASE node. */
entry->schedule = svn_wc_schedule_normal;
/* Grab inherited repository information, if necessary. */
if (repos_relpath == NULL)
{
SVN_ERR(svn_wc__db_scan_base_repos(&repos_relpath,
&entry->repos,
&entry->uuid,
db,
entry_abspath,
result_pool,
scratch_pool));
}
entry->incomplete = (status == svn_wc__db_status_incomplete);
}
else if (status == svn_wc__db_status_deleted)
{
svn_node_kind_t path_kind;
/* ### we don't have to worry about moves, so this is a delete. */
entry->schedule = svn_wc_schedule_delete;
/* If there are multiple working layers or no BASE layer, then
this is a WORKING delete or WORKING not-present. */
if (have_more_work || !have_base)
entry->copied = TRUE;
else if (have_base && !have_more_work)
entry->copied = FALSE;
else
{
const char *work_del_abspath;
SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL,
&work_del_abspath, NULL,
db, entry_abspath,
scratch_pool, scratch_pool));
if (work_del_abspath)
entry->copied = TRUE;
}
/* If there is still a directory on-disk we keep it, if not it is
already deleted. Simple, isn't it?
Before single-db we had to keep the administative area alive until
after the commit really deletes it. Setting keep alive stopped the
commit processing from deleting the directory. We don't delete it
any more, so all we have to do is provide some 'sane' value.
*/
SVN_ERR(svn_io_check_path(entry_abspath, &path_kind, scratch_pool));
entry->keep_local = (path_kind == svn_node_dir);
}
else if (status == svn_wc__db_status_added)
{
svn_wc__db_status_t work_status;
const char *op_root_abspath;
const char *scanned_original_relpath;
svn_revnum_t original_revision;
/* For child nodes, pick up the parent's revision. */
if (*entry->name != '\0')
{
assert(parent_entry != NULL);
assert(entry->revision == SVN_INVALID_REVNUM);
entry->revision = parent_entry->revision;
}
if (have_base)
{
svn_wc__db_status_t base_status;
/* ENTRY->REVISION is overloaded. When a node is schedule-add
or -replace, then REVISION refers to the BASE node's revision
that is being overwritten. We need to fetch it now. */
SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL,
&entry->revision,
NULL, NULL, NULL,
NULL, NULL, NULL,
NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
db, entry_abspath,
scratch_pool,
scratch_pool));
if (base_status == svn_wc__db_status_not_present)
{
/* The underlying node is DELETED in this revision. */
entry->deleted = TRUE;
/* This is an add since there isn't a node to replace. */
entry->schedule = svn_wc_schedule_add;
}
else
entry->schedule = svn_wc_schedule_replace;
}
else
{
/* There is NO 'not-present' BASE_NODE for this node.
Therefore, we are looking at some kind of add/copy
rather than a replace. */
/* ### if this looks like a plain old add, then rev=0. */
if (!SVN_IS_VALID_REVNUM(entry->copyfrom_rev)
&& !SVN_IS_VALID_REVNUM(entry->cmt_rev))
entry->revision = 0;
entry->schedule = svn_wc_schedule_add;
}
/* If we don't have "real" data from the entry (obstruction),
then we cannot begin a scan for data. The original node may
have important data. Set up stuff to kill that idea off,
and finish up this entry. */
{
SVN_ERR(svn_wc__db_scan_addition(&work_status,
&op_root_abspath,
&repos_relpath,
&entry->repos,
&entry->uuid,
&scanned_original_relpath,
NULL, NULL, /* original_root|uuid */
&original_revision,
db,
entry_abspath,
result_pool, scratch_pool));
/* In wc.db we want to keep the valid revision of the not-present
BASE_REV, but when we used entries we set the revision to 0
when adding a new node over a not present base node. */
if (work_status == svn_wc__db_status_added
&& entry->deleted)
entry->revision = 0;
}
if (!SVN_IS_VALID_REVNUM(entry->cmt_rev)
&& scanned_original_relpath == NULL)
{
/* There is NOT a last-changed revision (last-changed date and
author may be unknown, but we can always check the rev).
The absence of a revision implies this node was added WITHOUT
any history. Avoid the COPIED checks in the else block. */
/* ### scan_addition may need to be updated to avoid returning
### status_copied in this case. */
}
/* For backwards-compatiblity purposes we treat moves just like
* regular copies. */
else if (work_status == svn_wc__db_status_copied ||
work_status == svn_wc__db_status_moved_here)
{
entry->copied = TRUE;
/* If this is a child of a copied subtree, then it should be
schedule_normal. */
if (original_repos_relpath == NULL)
{
/* ### what if there is a BASE node under there? */
entry->schedule = svn_wc_schedule_normal;
}
/* Copied nodes need to mirror their copyfrom_rev, if they
don't have a revision of their own already. */
if (!SVN_IS_VALID_REVNUM(entry->revision)
|| entry->revision == 0 /* added */)
entry->revision = original_revision;
}
/* Does this node have copyfrom_* information? */
if (scanned_original_relpath != NULL)
{
svn_boolean_t is_copied_child;
svn_boolean_t is_mixed_rev = FALSE;
SVN_ERR_ASSERT(work_status == svn_wc__db_status_copied ||
work_status == svn_wc__db_status_moved_here);
/* If this node inherits copyfrom information from an
ancestor node, then it must be a copied child. */
is_copied_child = (original_repos_relpath == NULL);
/* If this node has copyfrom information on it, then it may
be an actual copy-root, or it could be participating in
a mixed-revision copied tree. So if we don't already know
this is a copied child, then we need to look for this
mixed-revision situation. */
if (!is_copied_child)
{
const char *parent_abspath;
svn_error_t *err;
const char *parent_repos_relpath;
const char *parent_root_url;
/* When we insert entries into the database, we will
construct additional copyfrom records for mixed-revision
copies. The old entries would simply record the different
revision in the entry->revision field. That is not
available within wc-ng, so additional copies are made
(see the logic inside write_entry()). However, when
reading these back *out* of the database, the additional
copies look like new "Added" nodes rather than a simple
mixed-rev working copy.
That would be a behavior change if we did not compensate.
If there is copyfrom information for this node, then the
code below looks at the parent to detect if it *also* has
copyfrom information, and if the copyfrom_url would align
properly. If it *does*, then we omit storing copyfrom_url
and copyfrom_rev (ie. inherit the copyfrom info like a
normal child), and update entry->revision with the
copyfrom_rev in order to (re)create the mixed-rev copied
subtree that was originally presented for storage. */
/* Get the copyfrom information from our parent.
Note that the parent could be added/copied/moved-here.
There is no way for it to be deleted/moved-away and
have *this* node appear as copied. */
parent_abspath = svn_dirent_dirname(entry_abspath,
scratch_pool);
err = svn_wc__db_scan_addition(NULL,
&op_root_abspath,
NULL, NULL, NULL,
&parent_repos_relpath,
&parent_root_url,
NULL, NULL,
db, parent_abspath,
scratch_pool,
scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
svn_error_clear(err);
}
else if (parent_root_url != NULL
&& strcmp(original_root_url, parent_root_url) == 0)
{
const char *relpath_to_entry = svn_dirent_is_child(
op_root_abspath, entry_abspath, NULL);
const char *entry_repos_relpath = svn_relpath_join(
parent_repos_relpath, relpath_to_entry, scratch_pool);
/* The copyfrom repos roots matched.
Now we look to see if the copyfrom path of the parent
would align with our own path. If so, then it means
this copyfrom was spontaneously created and inserted
for mixed-rev purposes and can be eliminated without
changing the semantics of a mixed-rev copied subtree.
See notes/api-errata/wc003.txt for some additional
detail, and potential issues. */
if (strcmp(entry_repos_relpath,
original_repos_relpath) == 0)
{
is_copied_child = TRUE;
is_mixed_rev = TRUE;
}
}
}
if (is_copied_child)
{
/* We won't be settig the copyfrom_url, yet need to
clear out the copyfrom_rev. Thus, this node becomes a
child of a copied subtree (rather than its own root). */
entry->copyfrom_rev = SVN_INVALID_REVNUM;
/* Children in a copied subtree are schedule normal
since we don't plan to actually *do* anything with
them. Their operation is implied by ancestors. */
entry->schedule = svn_wc_schedule_normal;
/* And *finally* we turn this entry into the mixed
revision node that it was intended to be. This
node's revision is taken from the copyfrom record
that we spontaneously constructed. */
if (is_mixed_rev)
entry->revision = original_revision;
}
else if (original_repos_relpath != NULL)
{
entry->copyfrom_url =
svn_path_url_add_component2(original_root_url,
original_repos_relpath,
result_pool);
}
else
{
/* NOTE: if original_repos_relpath == NULL, then the
second call to scan_addition() will not have occurred.
Thus, this use of OP_ROOT_ABSPATH still contains the
original value where we fetched a value for
SCANNED_REPOS_RELPATH. */
const char *relpath_to_entry = svn_dirent_is_child(
op_root_abspath, entry_abspath, NULL);
const char *entry_repos_relpath = svn_relpath_join(
scanned_original_relpath, relpath_to_entry, scratch_pool);
entry->copyfrom_url =
svn_path_url_add_component2(original_root_url,
entry_repos_relpath,
result_pool);
}
}
}
else if (status == svn_wc__db_status_not_present)
{
/* ### buh. 'deleted' nodes are actually supposed to be
### schedule "normal" since we aren't going to actually *do*
### anything to this node at commit time. */
entry->schedule = svn_wc_schedule_normal;
entry->deleted = TRUE;
}
else if (status == svn_wc__db_status_server_excluded)
{
entry->absent = TRUE;
}
else if (status == svn_wc__db_status_excluded)
{
entry->schedule = svn_wc_schedule_normal;
entry->depth = svn_depth_exclude;
}
else
{
/* ### we should have handled all possible status values. */
SVN_ERR_MALFUNCTION();
}
/* ### higher levels want repos information about deleted nodes, even
### tho they are not "part of" a repository any more. */
if (entry->schedule == svn_wc_schedule_delete)
{
SVN_ERR(get_info_for_deleted(entry,
&kind,
&repos_relpath,
&checksum,
&lock,
db, entry_abspath,
parent_entry,
have_base, have_more_work,
result_pool, scratch_pool));
}
/* ### default to the infinite depth if we don't know it. */
if (entry->depth == svn_depth_unknown)
entry->depth = svn_depth_infinity;
if (kind == svn_node_dir)
entry->kind = svn_node_dir;
else if (kind == svn_node_file)
entry->kind = svn_node_file;
else if (kind == svn_node_symlink)
entry->kind = svn_node_file; /* ### no symlink kind */
else
entry->kind = svn_node_unknown;
/* We should always have a REPOS_RELPATH, except for:
- deleted nodes
- certain obstructed nodes
- not-present nodes
- absent nodes
- excluded nodes
### the last three should probably have an "implied" REPOS_RELPATH
*/
SVN_ERR_ASSERT(repos_relpath != NULL
|| entry->schedule == svn_wc_schedule_delete
|| status == svn_wc__db_status_not_present
|| status == svn_wc__db_status_server_excluded
|| status == svn_wc__db_status_excluded);
if (repos_relpath)
entry->url = svn_path_url_add_component2(entry->repos,
repos_relpath,
result_pool);
if (checksum)
{
/* We got a SHA-1, get the corresponding MD-5. */
if (checksum->kind != svn_checksum_md5)
SVN_ERR(svn_wc__db_pristine_get_md5(&checksum, db,
entry_abspath, checksum,
scratch_pool, scratch_pool));
SVN_ERR_ASSERT(checksum->kind == svn_checksum_md5);
entry->checksum = svn_checksum_to_cstring(checksum, result_pool);
}
if (conflicted)
{
svn_skel_t *conflict;
svn_boolean_t text_conflicted;
svn_boolean_t prop_conflicted;
SVN_ERR(svn_wc__db_read_conflict(&conflict, db, entry_abspath,
scratch_pool, scratch_pool));
SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, &text_conflicted,
&prop_conflicted, NULL,
db, dir_abspath, conflict,
scratch_pool, scratch_pool));
if (text_conflicted)
{
const char *my_abspath;
const char *their_old_abspath;
const char *their_abspath;
SVN_ERR(svn_wc__conflict_read_text_conflict(&my_abspath,
&their_old_abspath,
&their_abspath,
db, dir_abspath,
conflict, scratch_pool,
scratch_pool));
if (my_abspath)
entry->conflict_wrk = svn_dirent_basename(my_abspath, result_pool);
if (their_old_abspath)
entry->conflict_old = svn_dirent_basename(their_old_abspath,
result_pool);
if (their_abspath)
entry->conflict_new = svn_dirent_basename(their_abspath,
result_pool);
}
if (prop_conflicted)
{
const char *prej_abspath;
SVN_ERR(svn_wc__conflict_read_prop_conflict(&prej_abspath, NULL,
NULL, NULL, NULL,
db, dir_abspath,
conflict, scratch_pool,
scratch_pool));
if (prej_abspath)
entry->prejfile = svn_dirent_basename(prej_abspath, result_pool);
}
}
if (lock)
{
entry->lock_token = lock->token;
entry->lock_owner = lock->owner;
entry->lock_comment = lock->comment;
entry->lock_creation_date = lock->date;
}
/* Let's check for a file external. ugh. */
if (status == svn_wc__db_status_normal
&& kind == svn_node_file)
SVN_ERR(check_file_external(entry, db, entry_abspath, dir_abspath,
result_pool, scratch_pool));
entry->working_size = translated_size;
*new_entry = entry;
return SVN_NO_ERROR;
}
/* Read entries for PATH/LOCAL_ABSPATH from DB. The entries
will be allocated in RESULT_POOL, with temporary allocations in
SCRATCH_POOL. The entries are returned in RESULT_ENTRIES. */
static svn_error_t *
read_entries_new(apr_hash_t **result_entries,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_hash_t *entries;
const apr_array_header_t *children;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
int i;
const svn_wc_entry_t *parent_entry;
apr_int64_t wc_id = 1; /* ### hacky. should remove. */
entries = apr_hash_make(result_pool);
SVN_ERR(read_one_entry(&parent_entry, db, wc_id, local_abspath,
"" /* name */,
NULL /* parent_entry */,
result_pool, iterpool));
svn_hash_sets(entries, "", parent_entry);
/* Use result_pool so that the child names (used by reference, rather
than copied) appear in result_pool. */
SVN_ERR(svn_wc__db_read_children(&children, db,
local_abspath,
result_pool, iterpool));
for (i = children->nelts; i--; )
{
const char *name = APR_ARRAY_IDX(children, i, const char *);
const svn_wc_entry_t *entry;
svn_pool_clear(iterpool);
SVN_ERR(read_one_entry(&entry,
db, wc_id, local_abspath, name, parent_entry,
result_pool, iterpool));
svn_hash_sets(entries, entry->name, entry);
}
svn_pool_destroy(iterpool);
*result_entries = entries;
return SVN_NO_ERROR;
}
/* Read a pair of entries from wc_db in the directory DIR_ABSPATH. Return
the directory's entry in *PARENT_ENTRY and NAME's entry in *ENTRY. The
two returned pointers will be the same if NAME=="" ("this dir").
The parent entry must exist.
The requested entry MAY exist. If it does not, then NULL will be returned.
The resulting entries are allocated in RESULT_POOL, and all temporary
allocations are made in SCRATCH_POOL. */
static svn_error_t *
read_entry_pair(const svn_wc_entry_t **parent_entry,
const svn_wc_entry_t **entry,
svn_wc__db_t *db,
const char *dir_abspath,
const char *name,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_int64_t wc_id = 1; /* ### hacky. should remove. */
SVN_ERR(read_one_entry(parent_entry, db, wc_id, dir_abspath,
"" /* name */,
NULL /* parent_entry */,
result_pool, scratch_pool));
/* If we need the entry for "this dir", then return the parent_entry
in both outputs. Otherwise, read the child node. */
if (*name == '\0')
{
/* If the retrieved node is a FILE, then we have a problem. We asked
for a directory. This implies there is an obstructing, unversioned
directory where a FILE should be. We navigated from the obstructing
subdir up to the parent dir, then returned the FILE found there.
Let's return WC_MISSING cuz the caller thought we had a dir, but
that (versioned subdir) isn't there. */
if ((*parent_entry)->kind == svn_node_file)
{
*parent_entry = NULL;
return svn_error_createf(SVN_ERR_WC_MISSING, NULL,
_("'%s' is not a versioned working copy"),
svn_dirent_local_style(dir_abspath,
scratch_pool));
}
*entry = *parent_entry;
}
else
{
const apr_array_header_t *children;
int i;
/* Default to not finding the child. */
*entry = NULL;
/* Determine whether the parent KNOWS about this child. If it does
not, then we should not attempt to look for it.
For example: the parent doesn't "know" about the child, but the
versioned directory *does* exist on disk. We don't want to look
into that subdir. */
SVN_ERR(svn_wc__db_read_children(&children, db, dir_abspath,
scratch_pool, scratch_pool));
for (i = children->nelts; i--; )
{
const char *child = APR_ARRAY_IDX(children, i, const char *);
if (strcmp(child, name) == 0)
{
svn_error_t *err;
err = read_one_entry(entry,
db, wc_id, dir_abspath, name, *parent_entry,
result_pool, scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
/* No problem. Clear the error and leave the default value
of "missing". */
svn_error_clear(err);
}
/* Found it. No need to keep searching. */
break;
}
}
/* if the loop ends without finding a child, then we have the default
ENTRY value of NULL. */
}
return SVN_NO_ERROR;
}
/* */
static svn_error_t *
read_entries(apr_hash_t **entries,
svn_wc__db_t *db,
const char *wcroot_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
int wc_format;
SVN_ERR(svn_wc__db_temp_get_format(&wc_format, db, wcroot_abspath,
scratch_pool));
if (wc_format < SVN_WC__WC_NG_VERSION)
return svn_error_trace(svn_wc__read_entries_old(entries,
wcroot_abspath,
result_pool,
scratch_pool));
return svn_error_trace(read_entries_new(entries, db, wcroot_abspath,
result_pool, scratch_pool));
}
/* For a given LOCAL_ABSPATH, using DB, set *ADM_ABSPATH to the directory in
which the entry information is located, and *ENTRY_NAME to the entry name
to access that entry.
KIND is as in svn_wc__get_entry().
Return the results in RESULT_POOL and use SCRATCH_POOL for temporary
allocations. */
static svn_error_t *
get_entry_access_info(const char **adm_abspath,
const char **entry_name,
svn_wc__db_t *db,
const char *local_abspath,
svn_node_kind_t kind,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc_adm_access_t *adm_access;
svn_boolean_t read_from_subdir = FALSE;
/* If the caller didn't know the node kind, then stat the path. Maybe
it is really there, and we can speed up the steps below. */
if (kind == svn_node_unknown)
{
svn_node_kind_t on_disk;
/* Do we already have an access baton for LOCAL_ABSPATH? */
adm_access = svn_wc__adm_retrieve_internal2(db, local_abspath,
scratch_pool);
if (adm_access)
{
/* Sweet. The node is a directory. */
on_disk = svn_node_dir;
}
else
{
svn_boolean_t special;
/* What's on disk? */
SVN_ERR(svn_io_check_special_path(local_abspath, &on_disk, &special,
scratch_pool));
}
if (on_disk != svn_node_dir)
{
/* If this is *anything* besides a directory (FILE, NONE, or
UNKNOWN), then we cannot treat it as a versioned directory
containing entries to read. Leave READ_FROM_SUBDIR as FALSE,
so that the parent will be examined.
For NONE and UNKNOWN, it may be that metadata exists for the
node, even though on-disk is unhelpful.
If NEED_PARENT_STUB is TRUE, and the entry is not a DIRECTORY,
then we'll error.
If NEED_PARENT_STUB if FALSE, and we successfully read a stub,
then this on-disk node is obstructing the read. */
}
else
{
/* We found a directory for this UNKNOWN node. Determine whether
we need to read inside it. */
read_from_subdir = TRUE;
}
}
else if (kind == svn_node_dir)
{
read_from_subdir = TRUE;
}
if (read_from_subdir)
{
/* KIND must be a DIR or UNKNOWN (and we found a subdir). We want
the "real" data, so treat LOCAL_ABSPATH as a versioned directory. */
*adm_abspath = apr_pstrdup(result_pool, local_abspath);
*entry_name = "";
}
else
{
/* FILE node needs to read the parent directory. Or a DIR node
needs to read from the parent to get at the stub entry. Or this
is an UNKNOWN node, and we need to examine the parent. */
svn_dirent_split(adm_abspath, entry_name, local_abspath, result_pool);
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__get_entry(const svn_wc_entry_t **entry,
svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t allow_unversioned,
svn_node_kind_t kind,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *dir_abspath;
const char *entry_name;
SVN_ERR(get_entry_access_info(&dir_abspath, &entry_name, db, local_abspath,
kind, scratch_pool, scratch_pool));
{
const svn_wc_entry_t *parent_entry;
svn_error_t *err;
/* NOTE: if KIND is UNKNOWN and we decided to examine the *parent*
directory, then it is possible we moved out of the working copy.
If the on-disk node is a DIR, and we asked for a stub, then we
obviously can't provide that (parent has no info). If the on-disk
node is a FILE/NONE/UNKNOWN, then it is obstructing the real
LOCAL_ABSPATH (or it was never a versioned item). In all these
cases, the read_entries() will (properly) throw an error.
NOTE: if KIND is a DIR and we asked for the real data, but it is
obstructed on-disk by some other node kind (NONE, FILE, UNKNOWN),
then this will throw an error. */
err = read_entry_pair(&parent_entry, entry,
db, dir_abspath, entry_name,
result_pool, scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_MISSING || kind != svn_node_unknown
|| *entry_name != '\0')
return svn_error_trace(err);
svn_error_clear(err);
/* The caller didn't know the node type, we saw a directory there,
we attempted to read IN that directory, and then wc_db reports
that it is NOT a working copy directory. It is possible that
one of two things has happened:
1) a directory is obstructing a file in the parent
2) the (versioned) directory's contents have been removed
Let's assume situation (1); if that is true, then we can just
return the newly-found data.
If we assumed (2), then a valid result still won't help us
since the caller asked for the actual contents, not the stub
(which is why we read *into* the directory). However, if we
assume (1) and get back a stub, then we have verified a
missing, versioned directory, and can return an error
describing that.
Redo the fetch, but "insist" we are trying to find a file.
This will read from the parent directory of the "file". */
err = svn_wc__get_entry(entry, db, local_abspath, allow_unversioned,
svn_node_file, result_pool, scratch_pool);
if (err == SVN_NO_ERROR)
return SVN_NO_ERROR;
if (err->apr_err != SVN_ERR_NODE_UNEXPECTED_KIND)
return svn_error_trace(err);
svn_error_clear(err);
/* We asked for a FILE, but the node found is a DIR. Thus, we
are looking at a stub. Originally, we tried to read into the
subdir because NEED_PARENT_STUB is FALSE. The stub we just
read is not going to work for the caller, so inform them of
the missing subdirectory. */
SVN_ERR_ASSERT(*entry != NULL && (*entry)->kind == svn_node_dir);
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("Admin area of '%s' is missing"),
svn_dirent_local_style(local_abspath,
scratch_pool));
}
}
if (*entry == NULL)
{
if (allow_unversioned)
return SVN_NO_ERROR;
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("'%s' is not under version control"),
svn_dirent_local_style(local_abspath,
scratch_pool));
}
/* The caller had the wrong information. */
if ((kind == svn_node_file && (*entry)->kind != svn_node_file)
|| (kind == svn_node_dir && (*entry)->kind != svn_node_dir))
return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
_("'%s' is not of the right kind"),
svn_dirent_local_style(local_abspath,
scratch_pool));
return SVN_NO_ERROR;
}
/* TODO ### Rewrite doc string to mention ENTRIES_ALL; not ADM_ACCESS.
Prune the deleted entries from the cached entries in ADM_ACCESS, and
return that collection in *ENTRIES_PRUNED. SCRATCH_POOL is used for local,
short term, memory allocation, RESULT_POOL for permanent stuff. */
static svn_error_t *
prune_deleted(apr_hash_t **entries_pruned,
apr_hash_t *entries_all,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_hash_index_t *hi;
if (!entries_all)
{
*entries_pruned = NULL;
return SVN_NO_ERROR;
}
/* I think it will be common for there to be no deleted entries, so
it is worth checking for that case as we can optimise it. */
for (hi = apr_hash_first(scratch_pool, entries_all);
hi;
hi = apr_hash_next(hi))
{
svn_boolean_t hidden;
SVN_ERR(svn_wc__entry_is_hidden(&hidden,
svn__apr_hash_index_val(hi)));
if (hidden)
break;
}
if (! hi)
{
/* There are no deleted entries, so we can use the full hash */
*entries_pruned = entries_all;
return SVN_NO_ERROR;
}
/* Construct pruned hash without deleted entries */
*entries_pruned = apr_hash_make(result_pool);
for (hi = apr_hash_first(scratch_pool, entries_all);
hi;
hi = apr_hash_next(hi))
{
const void *key = svn__apr_hash_index_key(hi);
const svn_wc_entry_t *entry = svn__apr_hash_index_val(hi);
svn_boolean_t hidden;
SVN_ERR(svn_wc__entry_is_hidden(&hidden, entry));
if (!hidden)
svn_hash_sets(*entries_pruned, key, entry);
}
return SVN_NO_ERROR;
}
struct entries_read_baton_t
{
apr_hash_t **new_entries;
svn_wc__db_t *db;
const char *local_abspath;
apr_pool_t *result_pool;
};
static svn_error_t *
entries_read_txn(void *baton, svn_sqlite__db_t *db, apr_pool_t *scratch_pool)
{
struct entries_read_baton_t *erb = baton;
SVN_ERR(read_entries(erb->new_entries, erb->db, erb->local_abspath,
erb->result_pool, scratch_pool));
return NULL;
}
svn_error_t *
svn_wc__entries_read_internal(apr_hash_t **entries,
svn_wc_adm_access_t *adm_access,
svn_boolean_t show_hidden,
apr_pool_t *pool)
{
apr_hash_t *new_entries;
new_entries = svn_wc__adm_access_entries(adm_access);
if (! new_entries)
{
svn_wc__db_t *db = svn_wc__adm_get_db(adm_access);
const char *local_abspath = svn_wc__adm_access_abspath(adm_access);
apr_pool_t *result_pool = svn_wc__adm_access_pool_internal(adm_access);
svn_sqlite__db_t *sdb;
struct entries_read_baton_t erb;
/* ### Use the borrow DB api to handle all calls in a single read
### transaction. This api is used extensively in our test suite
### via the entries-read application. */
SVN_ERR(svn_wc__db_temp_borrow_sdb(&sdb, db, local_abspath, pool));
erb.db = db;
erb.local_abspath = local_abspath;
erb.new_entries = &new_entries;
erb.result_pool = result_pool;
SVN_ERR(svn_sqlite__with_lock(sdb, entries_read_txn, &erb, pool));
svn_wc__adm_access_set_entries(adm_access, new_entries);
}
if (show_hidden)
*entries = new_entries;
else
SVN_ERR(prune_deleted(entries, new_entries,
svn_wc__adm_access_pool_internal(adm_access),
pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_entries_read(apr_hash_t **entries,
svn_wc_adm_access_t *adm_access,
svn_boolean_t show_hidden,
apr_pool_t *pool)
{
return svn_error_trace(svn_wc__entries_read_internal(entries, adm_access,
show_hidden, pool));
}
/* No transaction required: called from write_entry which is itself
transaction-wrapped. */
static svn_error_t *
insert_node(svn_sqlite__db_t *sdb,
const db_node_t *node,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
SVN_ERR_ASSERT(node->op_depth > 0 || node->repos_relpath);
SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnnnsnrisnnni",
node->wc_id,
node->local_relpath,
node->op_depth,
node->parent_relpath,
/* Setting depth for files? */
svn_depth_to_word(node->depth),
node->changed_rev,
node->changed_date,
node->changed_author,
node->recorded_time));
if (node->repos_relpath)
{
SVN_ERR(svn_sqlite__bind_int64(stmt, 5,
node->repos_id));
SVN_ERR(svn_sqlite__bind_text(stmt, 6,
node->repos_relpath));
SVN_ERR(svn_sqlite__bind_revnum(stmt, 7, node->revision));
}
if (node->presence == svn_wc__db_status_normal)
SVN_ERR(svn_sqlite__bind_text(stmt, 8, "normal"));
else if (node->presence == svn_wc__db_status_not_present)
SVN_ERR(svn_sqlite__bind_text(stmt, 8, "not-present"));
else if (node->presence == svn_wc__db_status_base_deleted)
SVN_ERR(svn_sqlite__bind_text(stmt, 8, "base-deleted"));
else if (node->presence == svn_wc__db_status_incomplete)
SVN_ERR(svn_sqlite__bind_text(stmt, 8, "incomplete"));
else if (node->presence == svn_wc__db_status_excluded)
SVN_ERR(svn_sqlite__bind_text(stmt, 8, "excluded"));
else if (node->presence == svn_wc__db_status_server_excluded)
SVN_ERR(svn_sqlite__bind_text(stmt, 8, "server-excluded"));
if (node->kind == svn_node_none)
SVN_ERR(svn_sqlite__bind_text(stmt, 10, "unknown"));
else
SVN_ERR(svn_sqlite__bind_text(stmt, 10,
svn_node_kind_to_word(node->kind)));
if (node->kind == svn_node_file)
{
if (!node->checksum
&& node->op_depth == 0
&& node->presence != svn_wc__db_status_not_present
&& node->presence != svn_wc__db_status_excluded
&& node->presence != svn_wc__db_status_server_excluded)
return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
_("The file '%s' has no checksum"),
svn_dirent_local_style(node->local_relpath,
scratch_pool));
SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, node->checksum,
scratch_pool));
}
if (node->properties) /* ### Never set, props done later */
SVN_ERR(svn_sqlite__bind_properties(stmt, 15, node->properties,
scratch_pool));
if (node->recorded_size != SVN_INVALID_FILESIZE)
SVN_ERR(svn_sqlite__bind_int64(stmt, 16, node->recorded_size));
if (node->file_external)
SVN_ERR(svn_sqlite__bind_int(stmt, 20, 1));
if (node->inherited_props)
SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, node->inherited_props,
scratch_pool));
SVN_ERR(svn_sqlite__insert(NULL, stmt));
return SVN_NO_ERROR;
}
/* */
static svn_error_t *
insert_actual_node(svn_sqlite__db_t *sdb,
svn_wc__db_t *db,
const char *wri_abspath,
const db_actual_node_t *actual_node,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_skel_t *conflict_data = NULL;
SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_ACTUAL_NODE));
SVN_ERR(svn_sqlite__bind_int64(stmt, 1, actual_node->wc_id));
SVN_ERR(svn_sqlite__bind_text(stmt, 2, actual_node->local_relpath));
SVN_ERR(svn_sqlite__bind_text(stmt, 3, actual_node->parent_relpath));
if (actual_node->properties)
SVN_ERR(svn_sqlite__bind_properties(stmt, 4, actual_node->properties,
scratch_pool));
if (actual_node->changelist)
SVN_ERR(svn_sqlite__bind_text(stmt, 5, actual_node->changelist));
SVN_ERR(svn_wc__upgrade_conflict_skel_from_raw(
&conflict_data,
db, wri_abspath,
actual_node->local_relpath,
actual_node->conflict_old,
actual_node->conflict_working,
actual_node->conflict_new,
actual_node->prop_reject,
actual_node->tree_conflict_data,
actual_node->tree_conflict_data
? strlen(actual_node->tree_conflict_data)
: 0,
scratch_pool, scratch_pool));
if (conflict_data)
{
svn_stringbuf_t *data = svn_skel__unparse(conflict_data, scratch_pool);
SVN_ERR(svn_sqlite__bind_blob(stmt, 6, data->data, data->len));
}
/* Execute and reset the insert clause. */
return svn_error_trace(svn_sqlite__insert(NULL, stmt));
}
static svn_boolean_t
is_switched(db_node_t *parent,
db_node_t *child,
apr_pool_t *scratch_pool)
{
if (parent && child)
{
if (parent->repos_id != child->repos_id)
return TRUE;
if (parent->repos_relpath && child->repos_relpath)
{
const char *unswitched
= svn_relpath_join(parent->repos_relpath,
svn_relpath_basename(child->local_relpath,
scratch_pool),
scratch_pool);
if (strcmp(unswitched, child->repos_relpath))
return TRUE;
}
}
return FALSE;
}
struct write_baton {
db_node_t *base;
db_node_t *work;
db_node_t *below_work;
apr_hash_t *tree_conflicts;
};
#define WRITE_ENTRY_ASSERT(expr) \
if (!(expr)) \
return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL, \
_("Unable to upgrade '%s' at line %d"), \
svn_dirent_local_style( \
svn_dirent_join(root_abspath, \
local_relpath, \
scratch_pool), \
scratch_pool), __LINE__)
/* Write the information for ENTRY to WC_DB. The WC_ID, REPOS_ID and
REPOS_ROOT will all be used for writing ENTRY.
### transitioning from straight sql to using the wc_db APIs. For the
### time being, we'll need both parameters. */
static svn_error_t *
write_entry(struct write_baton **entry_node,
const struct write_baton *parent_node,
svn_wc__db_t *db,
svn_sqlite__db_t *sdb,
apr_int64_t wc_id,
apr_int64_t repos_id,
const svn_wc_entry_t *entry,
const svn_wc__text_base_info_t *text_base_info,
const char *local_relpath,
const char *tmp_entry_abspath,
const char *root_abspath,
const svn_wc_entry_t *this_dir,
svn_boolean_t create_locks,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
db_node_t *base_node = NULL;
db_node_t *working_node = NULL, *below_working_node = NULL;
db_actual_node_t *actual_node = NULL;
const char *parent_relpath;
apr_hash_t *tree_conflicts;
if (*local_relpath == '\0')
parent_relpath = NULL;
else
parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
/* This is how it should work, it doesn't work like this yet because
we need proper op_depth to layer the working nodes.
Using "svn add", "svn rm", "svn cp" only files can be replaced
pre-wcng; directories can only be normal, deleted or added.
Files cannot be replaced within a deleted directory, so replaced
files can only exist in a normal directory, or a directory that
is added+copied. In a normal directory a replaced file needs a
base node and a working node, in an added+copied directory a
replaced file needs two working nodes at different op-depths.
With just the above operations the conversion for files and
directories is straightforward:
pre-wcng wcng
parent child parent child
normal normal base base
add+copied normal+copied work work
normal+copied normal+copied work work
normal delete base base+work
delete delete base+work base+work
add+copied delete work work
normal add base work
add add work work
add+copied add work work
normal add+copied base work
add add+copied work work
add+copied add+copied work work
normal replace base base+work
add+copied replace work work+work
normal replace+copied base base+work
add+copied replace+copied work work+work
However "svn merge" make this more complicated. The pre-wcng
"svn merge" is capable of replacing a directory, that is it can
mark the whole tree deleted, and then copy another tree on top.
The entries then represent the replacing tree overlayed on the
deleted tree.
original replace schedule in
tree tree combined tree
A A replace+copied
A/f delete+copied
A/g A/g replace+copied
A/h add+copied
A/B A/B replace+copied
A/B/f delete+copied
A/B/g A/B/g replace+copied
A/B/h add+copied
A/C delete+copied
A/C/f delete+copied
A/D add+copied
A/D/f add+copied
The original tree could be normal tree, or an add+copied tree.
Committing such a merge generally worked, but making further tree
modifications before commit sometimes failed.
The root of the replace is handled like the file replace:
pre-wcng wcng
parent child parent child
normal replace+copied base base+work
add+copied replace+copied work work+work
although obviously the node is a directory rather then a file.
There are then more conversion states where the parent is
replaced.
pre-wcng wcng
parent child parent child
replace+copied add [base|work]+work work
replace+copied add+copied [base|work]+work work
replace+copied delete+copied [base|work]+work [base|work]+work
delete+copied delete+copied [base|work]+work [base|work]+work
replace+copied replace+copied [base|work]+work [base|work]+work
*/
WRITE_ENTRY_ASSERT(parent_node || entry->schedule == svn_wc_schedule_normal);
WRITE_ENTRY_ASSERT(!parent_node || parent_node->base
|| parent_node->below_work || parent_node->work);
switch (entry->schedule)
{
case svn_wc_schedule_normal:
if (entry->copied ||
(entry->depth == svn_depth_exclude
&& parent_node && !parent_node->base && parent_node->work))
working_node = MAYBE_ALLOC(working_node, result_pool);
else
base_node = MAYBE_ALLOC(base_node, result_pool);
break;
case svn_wc_schedule_add:
working_node = MAYBE_ALLOC(working_node, result_pool);
if (entry->deleted)
{
if (parent_node->base)
base_node = MAYBE_ALLOC(base_node, result_pool);
else
below_working_node = MAYBE_ALLOC(below_working_node, result_pool);
}
break;
case svn_wc_schedule_delete:
working_node = MAYBE_ALLOC(working_node, result_pool);
if (parent_node->base)
base_node = MAYBE_ALLOC(base_node, result_pool);
if (parent_node->work)
below_working_node = MAYBE_ALLOC(below_working_node, result_pool);
break;
case svn_wc_schedule_replace:
working_node = MAYBE_ALLOC(working_node, result_pool);
if (parent_node->base)
base_node = MAYBE_ALLOC(base_node, result_pool);
else
below_working_node = MAYBE_ALLOC(below_working_node, result_pool);
break;
}
/* Something deleted in this revision means there should always be a
BASE node to indicate the not-present node. */
if (entry->deleted)
{
WRITE_ENTRY_ASSERT(base_node || below_working_node);
WRITE_ENTRY_ASSERT(!entry->incomplete);
if (base_node)
base_node->presence = svn_wc__db_status_not_present;
else
below_working_node->presence = svn_wc__db_status_not_present;
}
else if (entry->absent)
{
WRITE_ENTRY_ASSERT(base_node && !working_node && !below_working_node);
WRITE_ENTRY_ASSERT(!entry->incomplete);
base_node->presence = svn_wc__db_status_server_excluded;
}
if (entry->copied)
{
if (entry->copyfrom_url)
{
working_node->repos_id = repos_id;
working_node->repos_relpath = svn_uri_skip_ancestor(
this_dir->repos, entry->copyfrom_url,
result_pool);
working_node->revision = entry->copyfrom_rev;
working_node->op_depth
= svn_wc__db_op_depth_for_upgrade(local_relpath);
}
else if (parent_node->work && parent_node->work->repos_relpath)
{
working_node->repos_id = repos_id;
working_node->repos_relpath
= svn_relpath_join(parent_node->work->repos_relpath,
svn_relpath_basename(local_relpath, NULL),
result_pool);
working_node->revision = parent_node->work->revision;
working_node->op_depth = parent_node->work->op_depth;
}
else if (parent_node->below_work
&& parent_node->below_work->repos_relpath)
{
working_node->repos_id = repos_id;
working_node->repos_relpath
= svn_relpath_join(parent_node->below_work->repos_relpath,
svn_relpath_basename(local_relpath, NULL),
result_pool);
working_node->revision = parent_node->below_work->revision;
working_node->op_depth = parent_node->below_work->op_depth;
}
else
return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
_("No copyfrom URL for '%s'"),
svn_dirent_local_style(local_relpath,
scratch_pool));
}
if (entry->conflict_old)
{
actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
if (parent_relpath && entry->conflict_old)
actual_node->conflict_old = svn_relpath_join(parent_relpath,
entry->conflict_old,
scratch_pool);
else
actual_node->conflict_old = entry->conflict_old;
if (parent_relpath && entry->conflict_new)
actual_node->conflict_new = svn_relpath_join(parent_relpath,
entry->conflict_new,
scratch_pool);
else
actual_node->conflict_new = entry->conflict_new;
if (parent_relpath && entry->conflict_wrk)
actual_node->conflict_working = svn_relpath_join(parent_relpath,
entry->conflict_wrk,
scratch_pool);
else
actual_node->conflict_working = entry->conflict_wrk;
}
if (entry->prejfile)
{
actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
actual_node->prop_reject = svn_relpath_join((entry->kind == svn_node_dir
? local_relpath
: parent_relpath),
entry->prejfile,
scratch_pool);
}
if (entry->changelist)
{
actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
actual_node->changelist = entry->changelist;
}
/* ### set the text_mod value? */
if (entry_node && entry->tree_conflict_data)
{
/* Issues #3840/#3916: 1.6 stores multiple tree conflicts on the
parent node, 1.7 stores them directly on the conflited nodes.
So "((skel1) (skel2))" becomes "(skel1)" and "(skel2)" */
svn_skel_t *skel;
skel = svn_skel__parse(entry->tree_conflict_data,
strlen(entry->tree_conflict_data),
scratch_pool);
tree_conflicts = apr_hash_make(result_pool);
skel = skel->children;
while(skel)
{
svn_wc_conflict_description2_t *conflict;
svn_skel_t *new_skel;
const char *key;
/* *CONFLICT is allocated so it is safe to use a non-const pointer */
SVN_ERR(svn_wc__deserialize_conflict(
(const svn_wc_conflict_description2_t**)&conflict,
skel,
svn_dirent_join(root_abspath,
local_relpath,
scratch_pool),
scratch_pool, scratch_pool));
WRITE_ENTRY_ASSERT(conflict->kind == svn_wc_conflict_kind_tree);
/* Fix dubious data stored by old clients, local adds don't have
a repository URL. */
if (conflict->reason == svn_wc_conflict_reason_added)
conflict->src_left_version = NULL;
SVN_ERR(svn_wc__serialize_conflict(&new_skel, conflict,
scratch_pool, scratch_pool));
/* Store in hash to be retrieved when writing the child
row. */
key = svn_dirent_skip_ancestor(root_abspath, conflict->local_abspath);
svn_hash_sets(tree_conflicts, apr_pstrdup(result_pool, key),
svn_skel__unparse(new_skel, result_pool)->data);
skel = skel->next;
}
}
else
tree_conflicts = NULL;
if (parent_node && parent_node->tree_conflicts)
{
const char *tree_conflict_data =
svn_hash_gets(parent_node->tree_conflicts, local_relpath);
if (tree_conflict_data)
{
actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
actual_node->tree_conflict_data = tree_conflict_data;
}
/* Reset hash so that we don't write the row again when writing
actual-only nodes */
svn_hash_sets(parent_node->tree_conflicts, local_relpath, NULL);
}
if (entry->file_external_path != NULL)
{
base_node = MAYBE_ALLOC(base_node, result_pool);
}
/* Insert the base node. */
if (base_node)
{
base_node->wc_id = wc_id;
base_node->local_relpath = local_relpath;
base_node->op_depth = 0;
base_node->parent_relpath = parent_relpath;
base_node->revision = entry->revision;
base_node->recorded_time = entry->text_time;
base_node->recorded_size = entry->working_size;
if (entry->depth != svn_depth_exclude)
base_node->depth = entry->depth;
else
{
base_node->presence = svn_wc__db_status_excluded;
base_node->depth = svn_depth_infinity;
}
if (entry->deleted)
{
WRITE_ENTRY_ASSERT(base_node->presence
== svn_wc__db_status_not_present);
/* ### should be svn_node_unknown, but let's store what we have. */
base_node->kind = entry->kind;
}
else if (entry->absent)
{
WRITE_ENTRY_ASSERT(base_node->presence
== svn_wc__db_status_server_excluded);
/* ### should be svn_node_unknown, but let's store what we have. */
base_node->kind = entry->kind;
/* Store the most likely revision in the node to avoid
base nodes without a valid revision. Of course
we remember that the data is still incomplete. */
if (!SVN_IS_VALID_REVNUM(base_node->revision) && parent_node->base)
base_node->revision = parent_node->base->revision;
}
else
{
base_node->kind = entry->kind;
if (base_node->presence != svn_wc__db_status_excluded)
{
/* All subdirs are initially incomplete, they stop being
incomplete when the entries file in the subdir is
upgraded and remain incomplete if that doesn't happen. */
if (entry->kind == svn_node_dir
&& strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR))
{
base_node->presence = svn_wc__db_status_incomplete;
/* Store the most likely revision in the node to avoid
base nodes without a valid revision. Of course
we remember that the data is still incomplete. */
if (parent_node->base)
base_node->revision = parent_node->base->revision;
}
else if (entry->incomplete)
{
/* ### nobody should have set the presence. */
WRITE_ENTRY_ASSERT(base_node->presence
== svn_wc__db_status_normal);
base_node->presence = svn_wc__db_status_incomplete;
}
}
}
if (entry->kind == svn_node_dir)
base_node->checksum = NULL;
else
{
if (text_base_info && text_base_info->revert_base.sha1_checksum)
base_node->checksum = text_base_info->revert_base.sha1_checksum;
else if (text_base_info && text_base_info->normal_base.sha1_checksum)
base_node->checksum = text_base_info->normal_base.sha1_checksum;
else
base_node->checksum = NULL;
/* The base MD5 checksum is available in the entry, unless there
* is a copied WORKING node. If possible, verify that the entry
* checksum matches the base file that we found. */
if (! (working_node && entry->copied))
{
svn_checksum_t *entry_md5_checksum, *found_md5_checksum;
SVN_ERR(svn_checksum_parse_hex(&entry_md5_checksum,
svn_checksum_md5,
entry->checksum, scratch_pool));
if (text_base_info && text_base_info->revert_base.md5_checksum)
found_md5_checksum = text_base_info->revert_base.md5_checksum;
else if (text_base_info
&& text_base_info->normal_base.md5_checksum)
found_md5_checksum = text_base_info->normal_base.md5_checksum;
else
found_md5_checksum = NULL;
if (entry_md5_checksum && found_md5_checksum &&
!svn_checksum_match(entry_md5_checksum, found_md5_checksum))
return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
_("Bad base MD5 checksum for '%s'; "
"expected: '%s'; found '%s'; "),
svn_dirent_local_style(
svn_dirent_join(root_abspath,
local_relpath,
scratch_pool),
scratch_pool),
svn_checksum_to_cstring_display(
entry_md5_checksum, scratch_pool),
svn_checksum_to_cstring_display(
found_md5_checksum, scratch_pool));
else
{
/* ### Not sure what conditions this should cover. */
/* SVN_ERR_ASSERT(entry->deleted || ...); */
}
}
}
if (this_dir->repos)
{
base_node->repos_id = repos_id;
if (entry->url != NULL)
{
base_node->repos_relpath = svn_uri_skip_ancestor(
this_dir->repos, entry->url,
result_pool);
}
else
{
const char *relpath = svn_uri_skip_ancestor(this_dir->repos,
this_dir->url,
scratch_pool);
if (relpath == NULL || *relpath == '\0')
base_node->repos_relpath = entry->name;
else
base_node->repos_relpath =
svn_dirent_join(relpath, entry->name, result_pool);
}
}
/* TODO: These values should always be present, if they are missing
during an upgrade, set a flag, and then ask the user to talk to the
server.
Note: cmt_rev is the distinguishing value. The others may be 0 or
NULL if the corresponding revprop has been deleted. */
base_node->changed_rev = entry->cmt_rev;
base_node->changed_date = entry->cmt_date;
base_node->changed_author = entry->cmt_author;
if (entry->file_external_path)
base_node->file_external = TRUE;
/* Switched nodes get an empty iprops cache. */
if (parent_node
&& is_switched(parent_node->base, base_node, scratch_pool))
base_node->inherited_props
= apr_array_make(scratch_pool, 0, sizeof(svn_prop_inherited_item_t*));
SVN_ERR(insert_node(sdb, base_node, scratch_pool));
/* We have to insert the lock after the base node, because the node
must exist to lookup various bits of repos related information for
the abs path. */
if (entry->lock_token && create_locks)
{
svn_wc__db_lock_t lock;
lock.token = entry->lock_token;
lock.owner = entry->lock_owner;
lock.comment = entry->lock_comment;
lock.date = entry->lock_creation_date;
SVN_ERR(svn_wc__db_lock_add(db, tmp_entry_abspath, &lock,
scratch_pool));
}
}
if (below_working_node)
{
db_node_t *work
= parent_node->below_work ? parent_node->below_work : parent_node->work;
below_working_node->wc_id = wc_id;
below_working_node->local_relpath = local_relpath;
below_working_node->op_depth = work->op_depth;
below_working_node->parent_relpath = parent_relpath;
below_working_node->presence = svn_wc__db_status_normal;
below_working_node->kind = entry->kind;
below_working_node->repos_id = work->repos_id;
+ below_working_node->revision = work->revision;
/* This is just guessing. If the node below would have been switched
or if it was updated to a different version, the guess would
fail. But we don't have better information pre wc-ng :( */
if (work->repos_relpath)
below_working_node->repos_relpath
- = svn_relpath_join(work->repos_relpath, entry->name,
+ = svn_relpath_join(work->repos_relpath,
+ svn_relpath_basename(local_relpath, NULL),
result_pool);
else
below_working_node->repos_relpath = NULL;
- below_working_node->revision = parent_node->work->revision;
/* The revert_base checksum isn't available in the entry structure,
so the caller provides it. */
/* text_base_info is NULL for files scheduled to be added. */
below_working_node->checksum = NULL;
if (text_base_info)
{
if (entry->schedule == svn_wc_schedule_delete)
below_working_node->checksum =
text_base_info->normal_base.sha1_checksum;
else
below_working_node->checksum =
text_base_info->revert_base.sha1_checksum;
}
below_working_node->recorded_size = 0;
below_working_node->changed_rev = SVN_INVALID_REVNUM;
below_working_node->changed_date = 0;
below_working_node->changed_author = NULL;
below_working_node->depth = svn_depth_infinity;
below_working_node->recorded_time = 0;
below_working_node->properties = NULL;
if (working_node
&& entry->schedule == svn_wc_schedule_delete
&& working_node->repos_relpath)
{
/* We are lucky, our guesses above are not necessary. The known
correct information is in working. But our op_depth design
expects more information here */
below_working_node->repos_relpath = working_node->repos_relpath;
below_working_node->repos_id = working_node->repos_id;
below_working_node->revision = working_node->revision;
/* Nice for 'svn status' */
below_working_node->changed_rev = entry->cmt_rev;
below_working_node->changed_date = entry->cmt_date;
below_working_node->changed_author = entry->cmt_author;
/* And now remove it from WORKING, because in wc-ng code
should read it from the lower layer */
working_node->repos_relpath = NULL;
working_node->repos_id = 0;
working_node->revision = SVN_INVALID_REVNUM;
}
SVN_ERR(insert_node(sdb, below_working_node, scratch_pool));
}
/* Insert the working node. */
if (working_node)
{
working_node->wc_id = wc_id;
working_node->local_relpath = local_relpath;
working_node->parent_relpath = parent_relpath;
working_node->changed_rev = SVN_INVALID_REVNUM;
working_node->recorded_time = entry->text_time;
working_node->recorded_size = entry->working_size;
if (entry->depth != svn_depth_exclude)
working_node->depth = entry->depth;
else
{
working_node->presence = svn_wc__db_status_excluded;
working_node->depth = svn_depth_infinity;
}
if (entry->kind == svn_node_dir)
working_node->checksum = NULL;
else
{
working_node->checksum = NULL;
/* text_base_info is NULL for files scheduled to be added. */
if (text_base_info)
working_node->checksum = text_base_info->normal_base.sha1_checksum;
/* If an MD5 checksum is present in the entry, we can verify that
* it matches the MD5 of the base file we found earlier. */
#ifdef SVN_DEBUG
if (entry->checksum && text_base_info)
{
svn_checksum_t *md5_checksum;
SVN_ERR(svn_checksum_parse_hex(&md5_checksum, svn_checksum_md5,
entry->checksum, result_pool));
SVN_ERR_ASSERT(
md5_checksum && text_base_info->normal_base.md5_checksum);
SVN_ERR_ASSERT(svn_checksum_match(
md5_checksum, text_base_info->normal_base.md5_checksum));
}
#endif
}
working_node->kind = entry->kind;
if (working_node->presence != svn_wc__db_status_excluded)
{
/* All subdirs start of incomplete, and stop being incomplete
when the entries file in the subdir is upgraded. */
if (entry->kind == svn_node_dir
&& strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR))
{
working_node->presence = svn_wc__db_status_incomplete;
working_node->kind = svn_node_dir;
}
else if (entry->schedule == svn_wc_schedule_delete)
{
working_node->presence = svn_wc__db_status_base_deleted;
working_node->kind = entry->kind;
}
else
{
/* presence == normal */
working_node->kind = entry->kind;
if (entry->incomplete)
{
/* We shouldn't be overwriting another status. */
WRITE_ENTRY_ASSERT(working_node->presence
== svn_wc__db_status_normal);
working_node->presence = svn_wc__db_status_incomplete;
}
}
}
/* These should generally be unset for added and deleted files,
and contain whatever information we have for copied files. Let's
just store whatever we have.
Note: cmt_rev is the distinguishing value. The others may be 0 or
NULL if the corresponding revprop has been deleted. */
if (working_node->presence != svn_wc__db_status_base_deleted)
{
working_node->changed_rev = entry->cmt_rev;
working_node->changed_date = entry->cmt_date;
working_node->changed_author = entry->cmt_author;
}
if (entry->schedule == svn_wc_schedule_delete
&& parent_node->work
&& parent_node->work->presence == svn_wc__db_status_base_deleted)
{
working_node->op_depth = parent_node->work->op_depth;
}
else if (!entry->copied)
{
working_node->op_depth
= svn_wc__db_op_depth_for_upgrade(local_relpath);
}
SVN_ERR(insert_node(sdb, working_node, scratch_pool));
}
/* Insert the actual node. */
if (actual_node)
{
actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
actual_node->wc_id = wc_id;
actual_node->local_relpath = local_relpath;
actual_node->parent_relpath = parent_relpath;
SVN_ERR(insert_actual_node(sdb, db, tmp_entry_abspath,
actual_node, scratch_pool));
}
if (entry_node)
{
*entry_node = apr_palloc(result_pool, sizeof(**entry_node));
(*entry_node)->base = base_node;
(*entry_node)->work = working_node;
(*entry_node)->below_work = below_working_node;
(*entry_node)->tree_conflicts = tree_conflicts;
}
if (entry->file_external_path)
{
/* TODO: Maybe add a file external registration inside EXTERNALS here,
to allow removing file externals that aren't referenced from
svn:externals.
The svn:externals values are processed anyway after everything is
upgraded */
}
return SVN_NO_ERROR;
}
static svn_error_t *
write_actual_only_entries(apr_hash_t *tree_conflicts,
svn_sqlite__db_t *sdb,
svn_wc__db_t *db,
const char *wri_abspath,
apr_int64_t wc_id,
const char *parent_relpath,
apr_pool_t *scratch_pool)
{
apr_hash_index_t *hi;
for (hi = apr_hash_first(scratch_pool, tree_conflicts);
hi;
hi = apr_hash_next(hi))
{
db_actual_node_t *actual_node = NULL;
actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
actual_node->wc_id = wc_id;
actual_node->local_relpath = svn__apr_hash_index_key(hi);
actual_node->parent_relpath = parent_relpath;
actual_node->tree_conflict_data = svn__apr_hash_index_val(hi);
SVN_ERR(insert_actual_node(sdb, db, wri_abspath, actual_node,
scratch_pool));
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__write_upgraded_entries(void **dir_baton,
void *parent_baton,
svn_wc__db_t *db,
svn_sqlite__db_t *sdb,
apr_int64_t repos_id,
apr_int64_t wc_id,
const char *dir_abspath,
const char *new_root_abspath,
apr_hash_t *entries,
apr_hash_t *text_bases_info,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const svn_wc_entry_t *this_dir;
apr_hash_index_t *hi;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
const char *old_root_abspath, *dir_relpath;
struct write_baton *parent_node = parent_baton;
struct write_baton *dir_node;
/* Get a copy of the "this dir" entry for comparison purposes. */
this_dir = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR);
/* If there is no "this dir" entry, something is wrong. */
if (! this_dir)
return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
_("No default entry in directory '%s'"),
svn_dirent_local_style(dir_abspath,
iterpool));
old_root_abspath = svn_dirent_get_longest_ancestor(dir_abspath,
new_root_abspath,
scratch_pool);
SVN_ERR_ASSERT(old_root_abspath[0]);
dir_relpath = svn_dirent_skip_ancestor(old_root_abspath, dir_abspath);
/* Write out "this dir" */
SVN_ERR(write_entry(&dir_node, parent_node, db, sdb,
wc_id, repos_id, this_dir, NULL, dir_relpath,
svn_dirent_join(new_root_abspath, dir_relpath,
iterpool),
old_root_abspath,
this_dir, FALSE, result_pool, iterpool));
for (hi = apr_hash_first(scratch_pool, entries); hi;
hi = apr_hash_next(hi))
{
const char *name = svn__apr_hash_index_key(hi);
const svn_wc_entry_t *this_entry = svn__apr_hash_index_val(hi);
const char *child_abspath, *child_relpath;
svn_wc__text_base_info_t *text_base_info
= svn_hash_gets(text_bases_info, name);
svn_pool_clear(iterpool);
/* Don't rewrite the "this dir" entry! */
if (strcmp(name, SVN_WC_ENTRY_THIS_DIR) == 0)
continue;
/* Write the entry. Pass TRUE for create locks, because we still
use this function for upgrading old working copies. */
child_abspath = svn_dirent_join(dir_abspath, name, iterpool);
child_relpath = svn_dirent_skip_ancestor(old_root_abspath, child_abspath);
SVN_ERR(write_entry(NULL, dir_node, db, sdb,
wc_id, repos_id,
this_entry, text_base_info, child_relpath,
svn_dirent_join(new_root_abspath, child_relpath,
iterpool),
old_root_abspath,
this_dir, TRUE, iterpool, iterpool));
}
if (dir_node->tree_conflicts)
SVN_ERR(write_actual_only_entries(dir_node->tree_conflicts, sdb, db,
new_root_abspath, wc_id, dir_relpath,
iterpool));
*dir_baton = dir_node;
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
svn_wc_entry_t *
svn_wc_entry_dup(const svn_wc_entry_t *entry, apr_pool_t *pool)
{
svn_wc_entry_t *dupentry = apr_palloc(pool, sizeof(*dupentry));
/* Perform a trivial copy ... */
*dupentry = *entry;
/* ...and then re-copy stuff that needs to be duped into our pool. */
if (entry->name)
dupentry->name = apr_pstrdup(pool, entry->name);
if (entry->url)
dupentry->url = apr_pstrdup(pool, entry->url);
if (entry->repos)
dupentry->repos = apr_pstrdup(pool, entry->repos);
if (entry->uuid)
dupentry->uuid = apr_pstrdup(pool, entry->uuid);
if (entry->copyfrom_url)
dupentry->copyfrom_url = apr_pstrdup(pool, entry->copyfrom_url);
if (entry->conflict_old)
dupentry->conflict_old = apr_pstrdup(pool, entry->conflict_old);
if (entry->conflict_new)
dupentry->conflict_new = apr_pstrdup(pool, entry->conflict_new);
if (entry->conflict_wrk)
dupentry->conflict_wrk = apr_pstrdup(pool, entry->conflict_wrk);
if (entry->prejfile)
dupentry->prejfile = apr_pstrdup(pool, entry->prejfile);
if (entry->checksum)
dupentry->checksum = apr_pstrdup(pool, entry->checksum);
if (entry->cmt_author)
dupentry->cmt_author = apr_pstrdup(pool, entry->cmt_author);
if (entry->lock_token)
dupentry->lock_token = apr_pstrdup(pool, entry->lock_token);
if (entry->lock_owner)
dupentry->lock_owner = apr_pstrdup(pool, entry->lock_owner);
if (entry->lock_comment)
dupentry->lock_comment = apr_pstrdup(pool, entry->lock_comment);
if (entry->changelist)
dupentry->changelist = apr_pstrdup(pool, entry->changelist);
/* NOTE: we do not dup cachable_props or present_props since they
are deprecated. Use "" to indicate "nothing cachable or cached". */
dupentry->cachable_props = "";
dupentry->present_props = "";
if (entry->tree_conflict_data)
dupentry->tree_conflict_data = apr_pstrdup(pool,
entry->tree_conflict_data);
if (entry->file_external_path)
dupentry->file_external_path = apr_pstrdup(pool,
entry->file_external_path);
return dupentry;
}
/*** Generic Entry Walker */
/* A recursive entry-walker, helper for svn_wc_walk_entries3().
*
* For this directory (DIRPATH, ADM_ACCESS), call the "found_entry" callback
* in WALK_CALLBACKS, passing WALK_BATON to it. Then, for each versioned
* entry in this directory, call the "found entry" callback and then recurse
* (if it is a directory and if DEPTH allows).
*
* If SHOW_HIDDEN is true, include entries that are in a 'deleted' or
* 'absent' state (and not scheduled for re-addition), else skip them.
*
* Call CANCEL_FUNC with CANCEL_BATON to allow cancellation.
*/
static svn_error_t *
walker_helper(const char *dirpath,
svn_wc_adm_access_t *adm_access,
const svn_wc_entry_callbacks2_t *walk_callbacks,
void *walk_baton,
svn_depth_t depth,
svn_boolean_t show_hidden,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
apr_pool_t *subpool = svn_pool_create(pool);
apr_hash_t *entries;
apr_hash_index_t *hi;
svn_wc_entry_t *dot_entry;
svn_error_t *err;
svn_wc__db_t *db = svn_wc__adm_get_db(adm_access);
err = svn_wc__entries_read_internal(&entries, adm_access, show_hidden,
pool);
if (err)
SVN_ERR(walk_callbacks->handle_error(dirpath, err, walk_baton, pool));
/* As promised, always return the '.' entry first. */
dot_entry = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR);
if (! dot_entry)
return walk_callbacks->handle_error
(dirpath, svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
_("Directory '%s' has no THIS_DIR entry"),
svn_dirent_local_style(dirpath, pool)),
walk_baton, pool);
/* Call the "found entry" callback for this directory as a "this dir"
* entry. Note that if this directory has been reached by recursion, this
* is the second visit as it will already have been visited once as a
* child entry of its parent. */
err = walk_callbacks->found_entry(dirpath, dot_entry, walk_baton, subpool);
if(err)
SVN_ERR(walk_callbacks->handle_error(dirpath, err, walk_baton, pool));
if (depth == svn_depth_empty)
return SVN_NO_ERROR;
/* Loop over each of the other entries. */
for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
{
const char *name = svn__apr_hash_index_key(hi);
const svn_wc_entry_t *current_entry = svn__apr_hash_index_val(hi);
const char *entrypath;
const char *entry_abspath;
svn_boolean_t hidden;
svn_pool_clear(subpool);
/* See if someone wants to cancel this operation. */
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
/* Skip the "this dir" entry. */
if (strcmp(current_entry->name, SVN_WC_ENTRY_THIS_DIR) == 0)
continue;
entrypath = svn_dirent_join(dirpath, name, subpool);
SVN_ERR(svn_wc__entry_is_hidden(&hidden, current_entry));
SVN_ERR(svn_dirent_get_absolute(&entry_abspath, entrypath, subpool));
/* Call the "found entry" callback for this entry. (For a directory,
* this is the first visit: as a child.) */
if (current_entry->kind == svn_node_file
|| depth >= svn_depth_immediates)
{
err = walk_callbacks->found_entry(entrypath, current_entry,
walk_baton, subpool);
if (err)
SVN_ERR(walk_callbacks->handle_error(entrypath, err,
walk_baton, pool));
}
/* Recurse into this entry if appropriate. */
if (current_entry->kind == svn_node_dir
&& !hidden
&& depth >= svn_depth_immediates)
{
svn_wc_adm_access_t *entry_access;
svn_depth_t depth_below_here = depth;
if (depth == svn_depth_immediates)
depth_below_here = svn_depth_empty;
entry_access = svn_wc__adm_retrieve_internal2(db, entry_abspath,
subpool);
if (entry_access)
SVN_ERR(walker_helper(entrypath, entry_access,
walk_callbacks, walk_baton,
depth_below_here, show_hidden,
cancel_func, cancel_baton,
subpool));
}
}
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__walker_default_error_handler(const char *path,
svn_error_t *err,
void *walk_baton,
apr_pool_t *pool)
{
/* Note: don't trace this. We don't want to insert a false "stack frame"
onto an error generated elsewhere. */
return svn_error_trace(err);
}
/* The public API. */
svn_error_t *
svn_wc_walk_entries3(const char *path,
svn_wc_adm_access_t *adm_access,
const svn_wc_entry_callbacks2_t *walk_callbacks,
void *walk_baton,
svn_depth_t walk_depth,
svn_boolean_t show_hidden,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
const char *local_abspath;
svn_wc__db_t *db = svn_wc__adm_get_db(adm_access);
svn_error_t *err;
svn_node_kind_t kind;
svn_depth_t depth;
SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
err = svn_wc__db_read_info(NULL, &kind, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, &depth, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL,
db, local_abspath,
pool, pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
/* Remap into SVN_ERR_UNVERSIONED_RESOURCE. */
svn_error_clear(err);
return walk_callbacks->handle_error(
path, svn_error_createf(SVN_ERR_UNVERSIONED_RESOURCE, NULL,
_("'%s' is not under version control"),
svn_dirent_local_style(local_abspath, pool)),
walk_baton, pool);
}
if (kind == svn_node_file || depth == svn_depth_exclude)
{
const svn_wc_entry_t *entry;
/* ### we should stop passing out entry structures.
###
### we should not call handle_error for an error the *callback*
### gave us. let it deal with the problem before returning. */
if (!show_hidden)
{
svn_boolean_t hidden;
SVN_ERR(svn_wc__db_node_hidden(&hidden, db, local_abspath, pool));
if (hidden)
{
/* The fool asked to walk a "hidden" node. Report the node as
unversioned.
### this is incorrect behavior. see depth_test 36. the walk
### API will be revamped to avoid entry structures. we should
### be able to solve the problem with the new API. (since we
### shouldn't return a hidden entry here) */
return walk_callbacks->handle_error(
path, svn_error_createf(
SVN_ERR_UNVERSIONED_RESOURCE, NULL,
_("'%s' is not under version control"),
svn_dirent_local_style(local_abspath, pool)),
walk_baton, pool);
}
}
SVN_ERR(svn_wc__get_entry(&entry, db, local_abspath, FALSE,
svn_node_file, pool, pool));
err = walk_callbacks->found_entry(path, entry, walk_baton, pool);
if (err)
return walk_callbacks->handle_error(path, err, walk_baton, pool);
return SVN_NO_ERROR;
}
if (kind == svn_node_dir)
return walker_helper(path, adm_access, walk_callbacks, walk_baton,
walk_depth, show_hidden, cancel_func, cancel_baton,
pool);
return walk_callbacks->handle_error(
path, svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL,
_("'%s' has an unrecognized node kind"),
svn_dirent_local_style(local_abspath, pool)),
walk_baton, pool);
}
Index: vendor/subversion/dist/subversion/libsvn_wc/externals.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_wc/externals.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_wc/externals.c (revision 286501)
@@ -1,1687 +1,1697 @@
/*
* externals.c : routines dealing with (file) externals in the working copy
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include <stdlib.h>
#include <string.h>
#include <apr_pools.h>
#include <apr_hash.h>
#include <apr_tables.h>
#include <apr_general.h>
#include <apr_uri.h>
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_error.h"
#include "svn_hash.h"
#include "svn_io.h"
#include "svn_pools.h"
#include "svn_props.h"
#include "svn_string.h"
#include "svn_time.h"
#include "svn_types.h"
#include "svn_wc.h"
#include "private/svn_skel.h"
#include "private/svn_subr_private.h"
#include "wc.h"
#include "adm_files.h"
#include "props.h"
#include "translate.h"
#include "workqueue.h"
#include "conflicts.h"
#include "svn_private_config.h"
/** Externals **/
/*
* Look for either
*
* -r N
* -rN
*
* in the LINE_PARTS array and update the revision field in ITEM with
* the revision if the revision is found. Set REV_IDX to the index in
* LINE_PARTS where the revision specification starts. Remove from
* LINE_PARTS the element(s) that specify the revision.
* PARENT_DIRECTORY_DISPLAY and LINE are given to return a nice error
* string.
*
* If this function returns successfully, then LINE_PARTS will have
* only two elements in it.
*/
static svn_error_t *
find_and_remove_externals_revision(int *rev_idx,
const char **line_parts,
int num_line_parts,
svn_wc_external_item2_t *item,
const char *parent_directory_display,
const char *line,
apr_pool_t *pool)
{
int i;
for (i = 0; i < 2; ++i)
{
const char *token = line_parts[i];
if (token[0] == '-' && token[1] == 'r')
{
svn_opt_revision_t end_revision = { svn_opt_revision_unspecified };
const char *digits_ptr;
int shift_count;
int j;
*rev_idx = i;
if (token[2] == '\0')
{
/* There must be a total of four elements in the line if
-r N is used. */
if (num_line_parts != 4)
goto parse_error;
shift_count = 2;
digits_ptr = line_parts[i+1];
}
else
{
/* There must be a total of three elements in the line
if -rN is used. */
if (num_line_parts != 3)
goto parse_error;
shift_count = 1;
digits_ptr = token+2;
}
if (svn_opt_parse_revision(&item->revision,
&end_revision,
digits_ptr, pool) != 0)
goto parse_error;
/* We want a single revision, not a range. */
if (end_revision.kind != svn_opt_revision_unspecified)
goto parse_error;
/* Allow only numbers and dates, not keywords. */
if (item->revision.kind != svn_opt_revision_number
&& item->revision.kind != svn_opt_revision_date)
goto parse_error;
/* Shift any line elements past the revision specification
down over the revision specification. */
for (j = i; j < num_line_parts-shift_count; ++j)
line_parts[j] = line_parts[j+shift_count];
line_parts[num_line_parts-shift_count] = NULL;
/* Found the revision, so leave the function immediately, do
* not continue looking for additional revisions. */
return SVN_NO_ERROR;
}
}
/* No revision was found, so there must be exactly two items in the
line array. */
if (num_line_parts == 2)
return SVN_NO_ERROR;
parse_error:
return svn_error_createf
(SVN_ERR_CLIENT_INVALID_EXTERNALS_DESCRIPTION, NULL,
_("Error parsing %s property on '%s': '%s'"),
SVN_PROP_EXTERNALS,
parent_directory_display,
line);
}
svn_error_t *
svn_wc_parse_externals_description3(apr_array_header_t **externals_p,
const char *parent_directory,
const char *desc,
svn_boolean_t canonicalize_url,
apr_pool_t *pool)
{
int i;
apr_array_header_t *externals = NULL;
apr_array_header_t *lines = svn_cstring_split(desc, "\n\r", TRUE, pool);
const char *parent_directory_display = svn_path_is_url(parent_directory) ?
parent_directory : svn_dirent_local_style(parent_directory, pool);
/* If an error occurs halfway through parsing, *externals_p should stay
* untouched. So, store the list in a local var first. */
if (externals_p)
externals = apr_array_make(pool, 1, sizeof(svn_wc_external_item2_t *));
for (i = 0; i < lines->nelts; i++)
{
const char *line = APR_ARRAY_IDX(lines, i, const char *);
apr_status_t status;
char **line_parts;
int num_line_parts;
svn_wc_external_item2_t *item;
const char *token0;
const char *token1;
svn_boolean_t token0_is_url;
svn_boolean_t token1_is_url;
/* Index into line_parts where the revision specification
started. */
int rev_idx = -1;
if ((! line) || (line[0] == '#'))
continue;
/* else proceed */
status = apr_tokenize_to_argv(line, &line_parts, pool);
if (status)
return svn_error_wrap_apr(status,
_("Can't split line into components: '%s'"),
line);
/* Count the number of tokens. */
for (num_line_parts = 0; line_parts[num_line_parts]; num_line_parts++)
;
SVN_ERR(svn_wc_external_item2_create(&item, pool));
item->revision.kind = svn_opt_revision_unspecified;
item->peg_revision.kind = svn_opt_revision_unspecified;
/*
* There are six different formats of externals:
*
* 1) DIR URL
* 2) DIR -r N URL
* 3) DIR -rN URL
* 4) URL DIR
* 5) -r N URL DIR
* 6) -rN URL DIR
*
* The last three allow peg revisions in the URL.
*
* With relative URLs and no '-rN' or '-r N', there is no way to
* distinguish between 'DIR URL' and 'URL DIR' when URL is a
* relative URL like /svn/repos/trunk, so this case is taken as
* case 4).
*/
if (num_line_parts < 2 || num_line_parts > 4)
return svn_error_createf
(SVN_ERR_CLIENT_INVALID_EXTERNALS_DESCRIPTION, NULL,
_("Error parsing %s property on '%s': '%s'"),
SVN_PROP_EXTERNALS,
parent_directory_display,
line);
/* To make it easy to check for the forms, find and remove -r N
or -rN from the line item array. If it is found, rev_idx
contains the index into line_parts where '-r' was found and
set item->revision to the parsed revision. */
/* ### ugh. stupid cast. */
SVN_ERR(find_and_remove_externals_revision(&rev_idx,
(const char **)line_parts,
num_line_parts, item,
parent_directory_display,
line, pool));
token0 = line_parts[0];
token1 = line_parts[1];
token0_is_url = svn_path_is_url(token0);
token1_is_url = svn_path_is_url(token1);
if (token0_is_url && token1_is_url)
return svn_error_createf
(SVN_ERR_CLIENT_INVALID_EXTERNALS_DESCRIPTION, NULL,
_("Invalid %s property on '%s': "
"cannot use two absolute URLs ('%s' and '%s') in an external; "
"one must be a path where an absolute or relative URL is "
"checked out to"),
SVN_PROP_EXTERNALS, parent_directory_display, token0, token1);
if (0 == rev_idx && token1_is_url)
return svn_error_createf
(SVN_ERR_CLIENT_INVALID_EXTERNALS_DESCRIPTION, NULL,
_("Invalid %s property on '%s': "
"cannot use a URL '%s' as the target directory for an external "
"definition"),
SVN_PROP_EXTERNALS, parent_directory_display, token1);
if (1 == rev_idx && token0_is_url)
return svn_error_createf
(SVN_ERR_CLIENT_INVALID_EXTERNALS_DESCRIPTION, NULL,
_("Invalid %s property on '%s': "
"cannot use a URL '%s' as the target directory for an external "
"definition"),
SVN_PROP_EXTERNALS, parent_directory_display, token0);
/* The appearence of -r N or -rN forces the type of external.
If -r is at the beginning of the line or the first token is
an absolute URL or if the second token is not an absolute
URL, then the URL supports peg revisions. */
if (0 == rev_idx ||
(-1 == rev_idx && (token0_is_url || ! token1_is_url)))
{
/* The URL is passed to svn_opt_parse_path in
uncanonicalized form so that the scheme relative URL
//hostname/foo is not collapsed to a server root relative
URL /hostname/foo. */
SVN_ERR(svn_opt_parse_path(&item->peg_revision, &item->url,
token0, pool));
item->target_dir = token1;
}
else
{
item->target_dir = token0;
item->url = token1;
item->peg_revision = item->revision;
}
SVN_ERR(svn_opt_resolve_revisions(&item->peg_revision,
&item->revision, TRUE, FALSE,
pool));
item->target_dir = svn_dirent_internal_style(item->target_dir, pool);
if (item->target_dir[0] == '\0'
|| svn_dirent_is_absolute(item->target_dir)
|| svn_path_is_backpath_present(item->target_dir)
|| !svn_dirent_skip_ancestor("dummy",
svn_dirent_join("dummy",
item->target_dir,
pool)))
return svn_error_createf
(SVN_ERR_CLIENT_INVALID_EXTERNALS_DESCRIPTION, NULL,
_("Invalid %s property on '%s': "
"target '%s' is an absolute path or involves '..'"),
SVN_PROP_EXTERNALS,
parent_directory_display,
item->target_dir);
if (canonicalize_url)
{
/* Uh... this is stupid. But it's consistent with what our
code did before we split up the relpath/dirent/uri APIs.
Still, given this, it's no wonder that our own libraries
don't ask this function to canonicalize the results. */
if (svn_path_is_url(item->url))
item->url = svn_uri_canonicalize(item->url, pool);
else
item->url = svn_dirent_canonicalize(item->url, pool);
}
if (externals)
APR_ARRAY_PUSH(externals, svn_wc_external_item2_t *) = item;
}
if (externals_p)
*externals_p = externals;
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__externals_find_target_dups(apr_array_header_t **duplicate_targets,
apr_array_header_t *externals,
apr_pool_t *pool,
apr_pool_t *scratch_pool)
{
int i;
unsigned int len;
unsigned int len2;
const char *target;
apr_hash_t *targets = apr_hash_make(scratch_pool);
apr_hash_t *targets2 = NULL;
*duplicate_targets = NULL;
for (i = 0; i < externals->nelts; i++)
{
target = APR_ARRAY_IDX(externals, i,
svn_wc_external_item2_t*)->target_dir;
len = apr_hash_count(targets);
svn_hash_sets(targets, target, "");
if (len == apr_hash_count(targets))
{
/* Hashtable length is unchanged. This must be a duplicate. */
/* Collapse multiple duplicates of the same target by using a second
* hash layer. */
if (! targets2)
targets2 = apr_hash_make(scratch_pool);
len2 = apr_hash_count(targets2);
svn_hash_sets(targets2, target, "");
if (len2 < apr_hash_count(targets2))
{
/* The second hash list just got bigger, i.e. this target has
* not been counted as duplicate before. */
if (! *duplicate_targets)
{
*duplicate_targets = apr_array_make(
pool, 1, sizeof(svn_wc_external_item2_t*));
}
APR_ARRAY_PUSH((*duplicate_targets), const char *) = target;
}
/* Else, this same target has already been recorded as a duplicate,
* don't count it again. */
}
}
return SVN_NO_ERROR;
}
struct edit_baton
{
apr_pool_t *pool;
svn_wc__db_t *db;
/* We explicitly use wri_abspath and local_abspath here, because we
might want to install file externals in an obstructing working copy */
const char *wri_abspath; /* The working defining the file external */
const char *local_abspath; /* The file external itself */
const char *name; /* The basename of the file external itself */
/* Information from the caller */
svn_boolean_t use_commit_times;
const apr_array_header_t *ext_patterns;
const char *diff3cmd;
- const char *url;
const char *repos_root_url;
const char *repos_uuid;
+ const char *old_repos_relpath;
+ const char *new_repos_relpath;
const char *record_ancestor_abspath;
const char *recorded_repos_relpath;
svn_revnum_t recorded_peg_revision;
svn_revnum_t recorded_revision;
/* Introducing a new file external */
svn_boolean_t added;
svn_wc_conflict_resolver_func2_t conflict_func;
void *conflict_baton;
svn_cancel_func_t cancel_func;
void *cancel_baton;
svn_wc_notify_func2_t notify_func;
void *notify_baton;
svn_revnum_t *target_revision;
/* What was there before the update */
svn_revnum_t original_revision;
const svn_checksum_t *original_checksum;
/* What we are installing now */
const char *new_pristine_abspath;
svn_checksum_t *new_sha1_checksum;
svn_checksum_t *new_md5_checksum;
/* List of incoming propchanges */
apr_array_header_t *propchanges;
/* Array of svn_prop_inherited_item_t * structures representing the
properties inherited by the base node at LOCAL_ABSPATH. */
apr_array_header_t *iprops;
/* The last change information */
svn_revnum_t changed_rev;
apr_time_t changed_date;
const char *changed_author;
svn_boolean_t had_props;
svn_boolean_t file_closed;
};
/* svn_delta_editor_t function for svn_wc__get_file_external_editor */
static svn_error_t *
set_target_revision(void *edit_baton,
svn_revnum_t target_revision,
apr_pool_t *pool)
{
struct edit_baton *eb = edit_baton;
*eb->target_revision = target_revision;
return SVN_NO_ERROR;
}
/* svn_delta_editor_t function for svn_wc__get_file_external_editor */
static svn_error_t *
open_root(void *edit_baton,
svn_revnum_t base_revision,
apr_pool_t *dir_pool,
void **root_baton)
{
*root_baton = edit_baton;
return SVN_NO_ERROR;
}
/* svn_delta_editor_t function for svn_wc__get_file_external_editor */
static svn_error_t *
add_file(const char *path,
void *parent_baton,
const char *copyfrom_path,
svn_revnum_t copyfrom_revision,
apr_pool_t *file_pool,
void **file_baton)
{
struct edit_baton *eb = parent_baton;
if (strcmp(path, eb->name))
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("This editor can only update '%s'"),
svn_dirent_local_style(eb->local_abspath,
file_pool));
*file_baton = eb;
eb->original_revision = SVN_INVALID_REVNUM;
eb->added = TRUE;
return SVN_NO_ERROR;
}
/* svn_delta_editor_t function for svn_wc__get_file_external_editor */
static svn_error_t *
open_file(const char *path,
void *parent_baton,
svn_revnum_t base_revision,
apr_pool_t *file_pool,
void **file_baton)
{
struct edit_baton *eb = parent_baton;
svn_node_kind_t kind;
if (strcmp(path, eb->name))
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("This editor can only update '%s'"),
svn_dirent_local_style(eb->local_abspath,
file_pool));
*file_baton = eb;
SVN_ERR(svn_wc__db_base_get_info(NULL, &kind, &eb->original_revision,
- NULL, NULL, NULL, &eb->changed_rev,
+ &eb->old_repos_relpath, NULL, NULL,
+ &eb->changed_rev,
&eb->changed_date, &eb->changed_author,
NULL, &eb->original_checksum, NULL, NULL,
&eb->had_props, NULL, NULL,
eb->db, eb->local_abspath,
eb->pool, file_pool));
if (kind != svn_node_file)
return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
_("Node '%s' is no existing file external"),
svn_dirent_local_style(eb->local_abspath,
file_pool));
return SVN_NO_ERROR;
}
/* svn_delta_editor_t function for svn_wc__get_file_external_editor */
static svn_error_t *
apply_textdelta(void *file_baton,
const char *base_checksum_digest,
apr_pool_t *pool,
svn_txdelta_window_handler_t *handler,
void **handler_baton)
{
struct edit_baton *eb = file_baton;
svn_stream_t *src_stream;
svn_stream_t *dest_stream;
if (eb->original_checksum)
{
if (base_checksum_digest)
{
svn_checksum_t *expected_checksum;
const svn_checksum_t *original_md5;
SVN_ERR(svn_checksum_parse_hex(&expected_checksum, svn_checksum_md5,
base_checksum_digest, pool));
if (eb->original_checksum->kind != svn_checksum_md5)
SVN_ERR(svn_wc__db_pristine_get_md5(&original_md5,
eb->db, eb->wri_abspath,
eb->original_checksum,
pool, pool));
else
original_md5 = eb->original_checksum;
if (!svn_checksum_match(expected_checksum, original_md5))
return svn_error_trace(svn_checksum_mismatch_err(
expected_checksum,
original_md5,
pool,
_("Base checksum mismatch for '%s'"),
svn_dirent_local_style(eb->local_abspath,
pool)));
}
SVN_ERR(svn_wc__db_pristine_read(&src_stream, NULL, eb->db,
eb->wri_abspath, eb->original_checksum,
pool, pool));
}
else
src_stream = svn_stream_empty(pool);
SVN_ERR(svn_wc__open_writable_base(&dest_stream, &eb->new_pristine_abspath,
&eb->new_md5_checksum,
&eb->new_sha1_checksum,
eb->db, eb->wri_abspath,
eb->pool, pool));
svn_txdelta_apply(src_stream, dest_stream, NULL, eb->local_abspath, pool,
handler, handler_baton);
return SVN_NO_ERROR;
}
/* svn_delta_editor_t function for svn_wc__get_file_external_editor */
static svn_error_t *
change_file_prop(void *file_baton,
const char *name,
const svn_string_t *value,
apr_pool_t *pool)
{
struct edit_baton *eb = file_baton;
svn_prop_t *propchange;
propchange = apr_array_push(eb->propchanges);
propchange->name = apr_pstrdup(eb->pool, name);
propchange->value = value ? svn_string_dup(value, eb->pool) : NULL;
return SVN_NO_ERROR;
}
/* svn_delta_editor_t function for svn_wc__get_file_external_editor */
static svn_error_t *
close_file(void *file_baton,
const char *expected_md5_digest,
apr_pool_t *pool)
{
struct edit_baton *eb = file_baton;
svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
svn_wc_notify_state_t content_state = svn_wc_notify_state_unknown;
svn_boolean_t obstructed = FALSE;
eb->file_closed = TRUE; /* We bump the revision here */
/* Check the checksum, if provided */
if (expected_md5_digest)
{
svn_checksum_t *expected_md5_checksum;
const svn_checksum_t *actual_md5_checksum = eb->new_md5_checksum;
SVN_ERR(svn_checksum_parse_hex(&expected_md5_checksum, svn_checksum_md5,
expected_md5_digest, pool));
if (actual_md5_checksum == NULL)
{
actual_md5_checksum = eb->original_checksum;
if (actual_md5_checksum != NULL
&& actual_md5_checksum->kind != svn_checksum_md5)
{
SVN_ERR(svn_wc__db_pristine_get_md5(&actual_md5_checksum,
eb->db, eb->wri_abspath,
actual_md5_checksum,
pool, pool));
}
}
if (! svn_checksum_match(expected_md5_checksum, actual_md5_checksum))
return svn_checksum_mismatch_err(
expected_md5_checksum,
actual_md5_checksum, pool,
_("Checksum mismatch for '%s'"),
svn_dirent_local_style(eb->local_abspath, pool));
}
/* First move the file in the pristine store; this hands over the cleanup
behavior to the pristine store. */
if (eb->new_sha1_checksum)
{
SVN_ERR(svn_wc__db_pristine_install(eb->db, eb->new_pristine_abspath,
eb->new_sha1_checksum,
eb->new_md5_checksum, pool));
eb->new_pristine_abspath = NULL;
}
/* Merge the changes */
{
svn_skel_t *all_work_items = NULL;
svn_skel_t *conflict_skel = NULL;
svn_skel_t *work_item;
apr_hash_t *base_props = NULL;
apr_hash_t *actual_props = NULL;
apr_hash_t *new_pristine_props = NULL;
apr_hash_t *new_actual_props = NULL;
apr_hash_t *new_dav_props = NULL;
const svn_checksum_t *new_checksum = NULL;
const svn_checksum_t *original_checksum = NULL;
svn_boolean_t added = !SVN_IS_VALID_REVNUM(eb->original_revision);
- const char *repos_relpath = svn_uri_skip_ancestor(eb->repos_root_url,
- eb->url, pool);
if (! added)
{
new_checksum = eb->original_checksum;
if (eb->had_props)
SVN_ERR(svn_wc__db_base_get_props(
&base_props, eb->db, eb->local_abspath, pool, pool));
SVN_ERR(svn_wc__db_read_props(
&actual_props, eb->db, eb->local_abspath, pool, pool));
}
if (!base_props)
base_props = apr_hash_make(pool);
if (!actual_props)
actual_props = apr_hash_make(pool);
if (eb->new_sha1_checksum)
new_checksum = eb->new_sha1_checksum;
/* Merge the properties */
{
apr_array_header_t *entry_prop_changes;
apr_array_header_t *dav_prop_changes;
apr_array_header_t *regular_prop_changes;
int i;
SVN_ERR(svn_categorize_props(eb->propchanges, &entry_prop_changes,
&dav_prop_changes, &regular_prop_changes,
pool));
/* Read the entry-prop changes to update the last-changed info. */
for (i = 0; i < entry_prop_changes->nelts; i++)
{
const svn_prop_t *prop = &APR_ARRAY_IDX(entry_prop_changes, i,
svn_prop_t);
if (! prop->value)
continue; /* authz or something */
if (! strcmp(prop->name, SVN_PROP_ENTRY_LAST_AUTHOR))
eb->changed_author = apr_pstrdup(pool, prop->value->data);
else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_REV))
{
apr_int64_t rev;
SVN_ERR(svn_cstring_atoi64(&rev, prop->value->data));
eb->changed_rev = (svn_revnum_t)rev;
}
else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_DATE))
SVN_ERR(svn_time_from_cstring(&eb->changed_date, prop->value->data,
pool));
}
/* Store the DAV-prop (aka WC-prop) changes. (This treats a list
* of changes as a list of new props, but we only use this when
* adding a new file and it's equivalent in that case.) */
if (dav_prop_changes->nelts > 0)
new_dav_props = svn_prop_array_to_hash(dav_prop_changes, pool);
/* Merge the regular prop changes. */
if (regular_prop_changes->nelts > 0)
{
new_pristine_props = svn_prop__patch(base_props, regular_prop_changes,
pool);
SVN_ERR(svn_wc__merge_props(&conflict_skel,
&prop_state,
&new_actual_props,
eb->db, eb->local_abspath,
NULL /* server_baseprops*/,
base_props,
actual_props,
regular_prop_changes,
pool, pool));
}
else
{
new_pristine_props = base_props;
new_actual_props = actual_props;
}
}
/* Merge the text */
if (eb->new_sha1_checksum)
{
svn_node_kind_t disk_kind;
svn_boolean_t install_pristine = FALSE;
const char *install_from = NULL;
SVN_ERR(svn_io_check_path(eb->local_abspath, &disk_kind, pool));
if (disk_kind == svn_node_none)
{
/* Just install the file */
install_pristine = TRUE;
content_state = svn_wc_notify_state_changed;
}
else if (disk_kind != svn_node_file
|| (eb->added && disk_kind == svn_node_file))
{
/* The node is obstructed; we just change the DB */
obstructed = TRUE;
content_state = svn_wc_notify_state_unchanged;
}
else
{
svn_boolean_t is_mod;
SVN_ERR(svn_wc__internal_file_modified_p(&is_mod,
eb->db, eb->local_abspath,
FALSE, pool));
if (!is_mod)
{
install_pristine = TRUE;
content_state = svn_wc_notify_state_changed;
}
else
{
svn_boolean_t found_text_conflict;
/* Ok, we have to do some work to merge a local change */
SVN_ERR(svn_wc__perform_file_merge(&work_item,
&conflict_skel,
&found_text_conflict,
eb->db,
eb->local_abspath,
eb->wri_abspath,
new_checksum,
original_checksum,
actual_props,
eb->ext_patterns,
eb->original_revision,
*eb->target_revision,
eb->propchanges,
eb->diff3cmd,
eb->cancel_func,
eb->cancel_baton,
pool, pool));
all_work_items = svn_wc__wq_merge(all_work_items, work_item,
pool);
if (found_text_conflict)
content_state = svn_wc_notify_state_conflicted;
else
content_state = svn_wc_notify_state_merged;
}
}
if (install_pristine)
{
SVN_ERR(svn_wc__wq_build_file_install(&work_item, eb->db,
eb->local_abspath,
install_from,
eb->use_commit_times, TRUE,
pool, pool));
all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
}
}
else
{
content_state = svn_wc_notify_state_unchanged;
/* ### Retranslate on magic property changes, etc. */
}
/* Generate a conflict description, if needed */
if (conflict_skel)
{
SVN_ERR(svn_wc__conflict_skel_set_op_switch(
conflict_skel,
svn_wc_conflict_version_create2(
eb->repos_root_url,
eb->repos_uuid,
- repos_relpath,
+ eb->old_repos_relpath,
eb->original_revision,
svn_node_file,
pool),
svn_wc_conflict_version_create2(
eb->repos_root_url,
eb->repos_uuid,
- repos_relpath,
+ eb->new_repos_relpath,
*eb->target_revision,
svn_node_file,
pool),
pool, pool));
SVN_ERR(svn_wc__conflict_create_markers(&work_item,
eb->db, eb->local_abspath,
conflict_skel,
pool, pool));
all_work_items = svn_wc__wq_merge(all_work_items, work_item,
pool);
}
/* Install the file in the DB */
SVN_ERR(svn_wc__db_external_add_file(
eb->db,
eb->local_abspath,
eb->wri_abspath,
- repos_relpath,
+ eb->new_repos_relpath,
eb->repos_root_url,
eb->repos_uuid,
*eb->target_revision,
new_pristine_props,
eb->iprops,
eb->changed_rev,
eb->changed_date,
eb->changed_author,
new_checksum,
new_dav_props,
eb->record_ancestor_abspath,
eb->recorded_repos_relpath,
eb->recorded_peg_revision,
eb->recorded_revision,
TRUE, new_actual_props,
FALSE /* keep_recorded_info */,
conflict_skel,
all_work_items,
pool));
/* close_edit may also update iprops for switched files, catching
those for which close_file is never called (e.g. an update of a
file external with no changes). So as a minor optimization we
clear the iprops so as not to set them again in close_edit. */
eb->iprops = NULL;
/* Run the work queue to complete the installation */
SVN_ERR(svn_wc__wq_run(eb->db, eb->wri_abspath,
eb->cancel_func, eb->cancel_baton, pool));
}
/* Notify */
if (eb->notify_func)
{
svn_wc_notify_action_t action;
svn_wc_notify_t *notify;
if (!eb->added)
action = obstructed ? svn_wc_notify_update_shadowed_update
: svn_wc_notify_update_update;
else
action = obstructed ? svn_wc_notify_update_shadowed_add
: svn_wc_notify_update_add;
notify = svn_wc_create_notify(eb->local_abspath, action, pool);
notify->kind = svn_node_file;
notify->revision = *eb->target_revision;
notify->prop_state = prop_state;
notify->content_state = content_state;
notify->old_revision = eb->original_revision;
eb->notify_func(eb->notify_baton, notify, pool);
}
return SVN_NO_ERROR;
}
/* svn_delta_editor_t function for svn_wc__get_file_external_editor */
static svn_error_t *
close_edit(void *edit_baton,
apr_pool_t *pool)
{
struct edit_baton *eb = edit_baton;
- if (!eb->file_closed
- || eb->iprops)
+ if (!eb->file_closed)
{
apr_hash_t *wcroot_iprops = NULL;
+ /* The file wasn't updated, but its url or revision might have...
+ e.g. switch between branches for relative externals.
+ Just bump the information as that is just as expensive as
+ investigating when we should and shouldn't update it...
+ and avoid hard to debug edge cases */
+
if (eb->iprops)
{
wcroot_iprops = apr_hash_make(pool);
svn_hash_sets(wcroot_iprops, eb->local_abspath, eb->iprops);
}
- /* The node wasn't updated, so we just have to bump its revision */
SVN_ERR(svn_wc__db_op_bump_revisions_post_update(eb->db,
eb->local_abspath,
svn_depth_infinity,
- NULL, NULL, NULL,
+ eb->new_repos_relpath,
+ eb->repos_root_url,
+ eb->repos_uuid,
*eb->target_revision,
- apr_hash_make(pool),
+ apr_hash_make(pool)
+ /* exclude_relpaths */,
wcroot_iprops,
eb->notify_func,
eb->notify_baton,
pool));
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__get_file_external_editor(const svn_delta_editor_t **editor,
void **edit_baton,
svn_revnum_t *target_revision,
svn_wc_context_t *wc_ctx,
const char *local_abspath,
const char *wri_abspath,
const char *url,
const char *repos_root_url,
const char *repos_uuid,
apr_array_header_t *iprops,
svn_boolean_t use_commit_times,
const char *diff3_cmd,
const apr_array_header_t *preserved_exts,
const char *record_ancestor_abspath,
const char *recorded_url,
const svn_opt_revision_t *recorded_peg_rev,
const svn_opt_revision_t *recorded_rev,
svn_wc_conflict_resolver_func2_t conflict_func,
void *conflict_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_t *db = wc_ctx->db;
apr_pool_t *edit_pool = result_pool;
struct edit_baton *eb = apr_pcalloc(edit_pool, sizeof(*eb));
svn_delta_editor_t *tree_editor = svn_delta_default_editor(edit_pool);
eb->pool = edit_pool;
eb->db = db;
eb->local_abspath = apr_pstrdup(edit_pool, local_abspath);
if (wri_abspath)
eb->wri_abspath = apr_pstrdup(edit_pool, wri_abspath);
else
eb->wri_abspath = svn_dirent_dirname(local_abspath, edit_pool);
eb->name = svn_dirent_basename(eb->local_abspath, NULL);
eb->target_revision = target_revision;
- eb->url = apr_pstrdup(edit_pool, url);
eb->repos_root_url = apr_pstrdup(edit_pool, repos_root_url);
eb->repos_uuid = apr_pstrdup(edit_pool, repos_uuid);
+ eb->new_repos_relpath = svn_uri_skip_ancestor(eb->repos_root_url, url, edit_pool);
+ eb->old_repos_relpath = eb->new_repos_relpath;
+
+ eb->original_revision = SVN_INVALID_REVNUM;
eb->iprops = iprops;
eb->use_commit_times = use_commit_times;
eb->ext_patterns = preserved_exts;
eb->diff3cmd = diff3_cmd;
eb->record_ancestor_abspath = apr_pstrdup(edit_pool,record_ancestor_abspath);
eb->recorded_repos_relpath = svn_uri_skip_ancestor(repos_root_url, recorded_url,
edit_pool);
eb->changed_rev = SVN_INVALID_REVNUM;
if (recorded_peg_rev->kind == svn_opt_revision_number)
eb->recorded_peg_revision = recorded_peg_rev->value.number;
else
eb->recorded_peg_revision = SVN_INVALID_REVNUM; /* Not fixed/HEAD */
if (recorded_rev->kind == svn_opt_revision_number)
eb->recorded_revision = recorded_rev->value.number;
else
eb->recorded_revision = SVN_INVALID_REVNUM; /* Not fixed/HEAD */
eb->conflict_func = conflict_func;
eb->conflict_baton = conflict_baton;
eb->cancel_func = cancel_func;
eb->cancel_baton = cancel_baton;
eb->notify_func = notify_func;
eb->notify_baton = notify_baton;
eb->propchanges = apr_array_make(edit_pool, 1, sizeof(svn_prop_t));
tree_editor->open_root = open_root;
tree_editor->set_target_revision = set_target_revision;
tree_editor->add_file = add_file;
tree_editor->open_file = open_file;
tree_editor->apply_textdelta = apply_textdelta;
tree_editor->change_file_prop = change_file_prop;
tree_editor->close_file = close_file;
tree_editor->close_edit = close_edit;
return svn_delta_get_cancellation_editor(cancel_func, cancel_baton,
tree_editor, eb,
editor, edit_baton,
result_pool);
}
svn_error_t *
svn_wc__crawl_file_external(svn_wc_context_t *wc_ctx,
const char *local_abspath,
const svn_ra_reporter3_t *reporter,
void *report_baton,
svn_boolean_t restore_files,
svn_boolean_t use_commit_times,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool)
{
svn_wc__db_t *db = wc_ctx->db;
svn_error_t *err;
svn_node_kind_t kind;
svn_wc__db_lock_t *lock;
svn_revnum_t revision;
const char *repos_root_url;
const char *repos_relpath;
svn_boolean_t update_root;
err = svn_wc__db_base_get_info(NULL, &kind, &revision,
&repos_relpath, &repos_root_url, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, &lock,
NULL, NULL, &update_root,
db, local_abspath,
scratch_pool, scratch_pool);
if (err
|| kind == svn_node_dir
|| !update_root)
{
if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
svn_error_clear(err);
/* We don't know about this node, so all we have to do is tell
the reporter that we don't know this node.
But first we have to start the report by sending some basic
information for the root. */
SVN_ERR(reporter->set_path(report_baton, "", 0, svn_depth_infinity,
FALSE, NULL, scratch_pool));
SVN_ERR(reporter->delete_path(report_baton, "", scratch_pool));
/* Finish the report, which causes the update editor to be
driven. */
SVN_ERR(reporter->finish_report(report_baton, scratch_pool));
return SVN_NO_ERROR;
}
else
{
if (restore_files)
{
svn_node_kind_t disk_kind;
SVN_ERR(svn_io_check_path(local_abspath, &disk_kind, scratch_pool));
if (disk_kind == svn_node_none)
{
err = svn_wc_restore(wc_ctx, local_abspath, use_commit_times,
scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
return svn_error_trace(err);
svn_error_clear(err);
}
}
}
/* Report that we know the path */
SVN_ERR(reporter->set_path(report_baton, "", revision,
svn_depth_infinity, FALSE, NULL,
scratch_pool));
/* For compatibility with the normal update editor report we report
the target as switched.
### We can probably report a parent url and unswitched later */
SVN_ERR(reporter->link_path(report_baton, "",
svn_path_url_add_component2(repos_root_url,
repos_relpath,
scratch_pool),
revision,
svn_depth_infinity,
FALSE /* start_empty*/,
lock ? lock->token : NULL,
scratch_pool));
}
return svn_error_trace(reporter->finish_report(report_baton, scratch_pool));
}
svn_error_t *
svn_wc__read_external_info(svn_node_kind_t *external_kind,
const char **defining_abspath,
const char **defining_url,
svn_revnum_t *defining_operational_revision,
svn_revnum_t *defining_revision,
svn_wc_context_t *wc_ctx,
const char *wri_abspath,
const char *local_abspath,
svn_boolean_t ignore_enoent,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *repos_root_url;
svn_wc__db_status_t status;
svn_node_kind_t kind;
svn_error_t *err;
err = svn_wc__db_external_read(&status, &kind, defining_abspath,
defining_url ? &repos_root_url : NULL, NULL,
defining_url, defining_operational_revision,
defining_revision,
wc_ctx->db, local_abspath, wri_abspath,
result_pool, scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND || !ignore_enoent)
return svn_error_trace(err);
svn_error_clear(err);
if (external_kind)
*external_kind = svn_node_none;
if (defining_abspath)
*defining_abspath = NULL;
if (defining_url)
*defining_url = NULL;
if (defining_operational_revision)
*defining_operational_revision = SVN_INVALID_REVNUM;
if (defining_revision)
*defining_revision = SVN_INVALID_REVNUM;
return SVN_NO_ERROR;
}
if (external_kind)
{
if (status != svn_wc__db_status_normal)
*external_kind = svn_node_unknown;
else
switch(kind)
{
case svn_node_file:
case svn_node_symlink:
*external_kind = svn_node_file;
break;
case svn_node_dir:
*external_kind = svn_node_dir;
break;
default:
*external_kind = svn_node_none;
}
}
if (defining_url && *defining_url)
*defining_url = svn_path_url_add_component2(repos_root_url, *defining_url,
result_pool);
return SVN_NO_ERROR;
}
/* Return TRUE in *IS_ROLLED_OUT iff a node exists at XINFO->LOCAL_ABSPATH and
* if that node's origin corresponds with XINFO->REPOS_ROOT_URL and
* XINFO->REPOS_RELPATH. All allocations are made in SCRATCH_POOL. */
static svn_error_t *
is_external_rolled_out(svn_boolean_t *is_rolled_out,
svn_wc_context_t *wc_ctx,
svn_wc__committable_external_info_t *xinfo,
apr_pool_t *scratch_pool)
{
const char *repos_relpath;
const char *repos_root_url;
svn_error_t *err;
*is_rolled_out = FALSE;
err = svn_wc__db_base_get_info(NULL, NULL, NULL, &repos_relpath,
&repos_root_url, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
wc_ctx->db, xinfo->local_abspath,
scratch_pool, scratch_pool);
if (err)
{
if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
{
svn_error_clear(err);
return SVN_NO_ERROR;
}
SVN_ERR(err);
}
*is_rolled_out = (strcmp(xinfo->repos_root_url, repos_root_url) == 0 &&
strcmp(xinfo->repos_relpath, repos_relpath) == 0);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__committable_externals_below(apr_array_header_t **externals,
svn_wc_context_t *wc_ctx,
const char *local_abspath,
svn_depth_t depth,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_array_header_t *orig_externals;
int i;
apr_pool_t *iterpool;
/* For svn_depth_files, this also fetches dirs. They are filtered later. */
SVN_ERR(svn_wc__db_committable_externals_below(&orig_externals,
wc_ctx->db,
local_abspath,
depth != svn_depth_infinity,
result_pool, scratch_pool));
if (orig_externals == NULL)
return SVN_NO_ERROR;
iterpool = svn_pool_create(scratch_pool);
for (i = 0; i < orig_externals->nelts; i++)
{
svn_boolean_t is_rolled_out;
svn_wc__committable_external_info_t *xinfo =
APR_ARRAY_IDX(orig_externals, i,
svn_wc__committable_external_info_t *);
/* Discard dirs for svn_depth_files (s.a.). */
if (depth == svn_depth_files
&& xinfo->kind == svn_node_dir)
continue;
svn_pool_clear(iterpool);
/* Discard those externals that are not currently checked out. */
SVN_ERR(is_external_rolled_out(&is_rolled_out, wc_ctx, xinfo,
iterpool));
if (! is_rolled_out)
continue;
if (*externals == NULL)
*externals = apr_array_make(
result_pool, 0,
sizeof(svn_wc__committable_external_info_t *));
APR_ARRAY_PUSH(*externals,
svn_wc__committable_external_info_t *) = xinfo;
if (depth != svn_depth_infinity)
continue;
/* Are there any nested externals? */
SVN_ERR(svn_wc__committable_externals_below(externals, wc_ctx,
xinfo->local_abspath,
svn_depth_infinity,
result_pool, iterpool));
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__externals_defined_below(apr_hash_t **externals,
svn_wc_context_t *wc_ctx,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
return svn_error_trace(
svn_wc__db_externals_defined_below(externals,
wc_ctx->db, local_abspath,
result_pool, scratch_pool));
}
svn_error_t *
svn_wc__external_register(svn_wc_context_t *wc_ctx,
const char *defining_abspath,
const char *local_abspath,
svn_node_kind_t kind,
const char *repos_root_url,
const char *repos_uuid,
const char *repos_relpath,
svn_revnum_t operational_revision,
svn_revnum_t revision,
apr_pool_t *scratch_pool)
{
SVN_ERR_ASSERT(kind == svn_node_dir);
return svn_error_trace(
svn_wc__db_external_add_dir(wc_ctx->db, local_abspath,
defining_abspath,
repos_root_url,
repos_uuid,
defining_abspath,
repos_relpath,
operational_revision,
revision,
NULL,
scratch_pool));
}
svn_error_t *
svn_wc__external_remove(svn_wc_context_t *wc_ctx,
const char *wri_abspath,
const char *local_abspath,
svn_boolean_t declaration_only,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
svn_wc__db_status_t status;
svn_node_kind_t kind;
SVN_ERR(svn_wc__db_external_read(&status, &kind, NULL, NULL, NULL, NULL,
NULL, NULL,
wc_ctx->db, local_abspath, wri_abspath,
scratch_pool, scratch_pool));
SVN_ERR(svn_wc__db_external_remove(wc_ctx->db, local_abspath, wri_abspath,
NULL, scratch_pool));
if (declaration_only)
return SVN_NO_ERROR;
if (kind == svn_node_dir)
SVN_ERR(svn_wc_remove_from_revision_control2(wc_ctx, local_abspath,
TRUE, TRUE,
cancel_func, cancel_baton,
scratch_pool));
else
{
SVN_ERR(svn_wc__db_base_remove(wc_ctx->db, local_abspath,
FALSE /* keep_as_working */,
TRUE /* queue_deletes */,
FALSE /* remove_locks */,
SVN_INVALID_REVNUM,
NULL, NULL, scratch_pool));
SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath,
cancel_func, cancel_baton,
scratch_pool));
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__externals_gather_definitions(apr_hash_t **externals,
apr_hash_t **depths,
svn_wc_context_t *wc_ctx,
const char *local_abspath,
svn_depth_t depth,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
if (depth == svn_depth_infinity
|| depth == svn_depth_unknown)
{
return svn_error_trace(
svn_wc__db_externals_gather_definitions(externals, depths,
wc_ctx->db, local_abspath,
result_pool, scratch_pool));
}
else
{
const svn_string_t *value;
svn_error_t *err;
*externals = apr_hash_make(result_pool);
local_abspath = apr_pstrdup(result_pool, local_abspath);
err = svn_wc_prop_get2(&value, wc_ctx, local_abspath,
SVN_PROP_EXTERNALS, result_pool, scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
svn_error_clear(err);
value = NULL;
}
if (value)
svn_hash_sets(*externals, local_abspath, value->data);
if (value && depths)
{
svn_depth_t node_depth;
*depths = apr_hash_make(result_pool);
SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, &node_depth, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
wc_ctx->db, local_abspath,
scratch_pool, scratch_pool));
svn_hash_sets(*depths, local_abspath, svn_depth_to_word(node_depth));
}
return SVN_NO_ERROR;
}
}
svn_error_t *
svn_wc__close_db(const char *external_abspath,
svn_wc_context_t *wc_ctx,
apr_pool_t *scratch_pool)
{
SVN_ERR(svn_wc__db_drop_root(wc_ctx->db, external_abspath,
scratch_pool));
return SVN_NO_ERROR;
}
/* Return the scheme of @a uri in @a scheme allocated from @a pool.
If @a uri does not appear to be a valid URI, then @a scheme will
not be updated. */
static svn_error_t *
uri_scheme(const char **scheme, const char *uri, apr_pool_t *pool)
{
apr_size_t i;
for (i = 0; uri[i] && uri[i] != ':'; ++i)
if (uri[i] == '/')
goto error;
if (i > 0 && uri[i] == ':' && uri[i+1] == '/' && uri[i+2] == '/')
{
*scheme = apr_pstrmemdup(pool, uri, i);
return SVN_NO_ERROR;
}
error:
return svn_error_createf(SVN_ERR_BAD_URL, 0,
_("URL '%s' does not begin with a scheme"),
uri);
}
svn_error_t *
svn_wc__resolve_relative_external_url(const char **resolved_url,
const svn_wc_external_item2_t *item,
const char *repos_root_url,
const char *parent_dir_url,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *url = item->url;
apr_uri_t parent_dir_uri;
apr_status_t status;
*resolved_url = item->url;
/* If the URL is already absolute, there is nothing to do. */
if (svn_path_is_url(url))
{
/* "http://server/path" */
*resolved_url = svn_uri_canonicalize(url, result_pool);
return SVN_NO_ERROR;
}
if (url[0] == '/')
{
/* "/path", "//path", and "///path" */
int num_leading_slashes = 1;
if (url[1] == '/')
{
num_leading_slashes++;
if (url[2] == '/')
num_leading_slashes++;
}
/* "//schema-relative" and in some cases "///schema-relative".
This last format is supported on file:// schema relative. */
url = apr_pstrcat(scratch_pool,
apr_pstrndup(scratch_pool, url, num_leading_slashes),
svn_relpath_canonicalize(url + num_leading_slashes,
scratch_pool),
(char*)NULL);
}
else
{
/* "^/path" and "../path" */
url = svn_relpath_canonicalize(url, scratch_pool);
}
/* Parse the parent directory URL into its parts. */
status = apr_uri_parse(scratch_pool, parent_dir_url, &parent_dir_uri);
if (status)
return svn_error_createf(SVN_ERR_BAD_URL, 0,
_("Illegal parent directory URL '%s'"),
parent_dir_url);
/* If the parent directory URL is at the server root, then the URL
may have no / after the hostname so apr_uri_parse() will leave
the URL's path as NULL. */
if (! parent_dir_uri.path)
parent_dir_uri.path = apr_pstrmemdup(scratch_pool, "/", 1);
parent_dir_uri.query = NULL;
parent_dir_uri.fragment = NULL;
/* Handle URLs relative to the current directory or to the
repository root. The backpaths may only remove path elements,
not the hostname. This allows an external to refer to another
repository in the same server relative to the location of this
repository, say using SVNParentPath. */
if ((0 == strncmp("../", url, 3)) ||
(0 == strncmp("^/", url, 2)))
{
apr_array_header_t *base_components;
apr_array_header_t *relative_components;
int i;
/* Decompose either the parent directory's URL path or the
repository root's URL path into components. */
if (0 == strncmp("../", url, 3))
{
base_components = svn_path_decompose(parent_dir_uri.path,
scratch_pool);
relative_components = svn_path_decompose(url, scratch_pool);
}
else
{
apr_uri_t repos_root_uri;
status = apr_uri_parse(scratch_pool, repos_root_url,
&repos_root_uri);
if (status)
return svn_error_createf(SVN_ERR_BAD_URL, 0,
_("Illegal repository root URL '%s'"),
repos_root_url);
/* If the repository root URL is at the server root, then
the URL may have no / after the hostname so
apr_uri_parse() will leave the URL's path as NULL. */
if (! repos_root_uri.path)
repos_root_uri.path = apr_pstrmemdup(scratch_pool, "/", 1);
base_components = svn_path_decompose(repos_root_uri.path,
scratch_pool);
relative_components = svn_path_decompose(url + 2, scratch_pool);
}
for (i = 0; i < relative_components->nelts; ++i)
{
const char *component = APR_ARRAY_IDX(relative_components,
i,
const char *);
if (0 == strcmp("..", component))
{
/* Constructing the final absolute URL together with
apr_uri_unparse() requires that the path be absolute,
so only pop a component if the component being popped
is not the component for the root directory. */
if (base_components->nelts > 1)
apr_array_pop(base_components);
}
else
APR_ARRAY_PUSH(base_components, const char *) = component;
}
parent_dir_uri.path = (char *)svn_path_compose(base_components,
scratch_pool);
*resolved_url = svn_uri_canonicalize(apr_uri_unparse(scratch_pool,
&parent_dir_uri, 0),
result_pool);
return SVN_NO_ERROR;
}
/* The remaining URLs are relative to either the scheme or server root
and can only refer to locations inside that scope, so backpaths are
not allowed. */
if (svn_path_is_backpath_present(url))
return svn_error_createf(SVN_ERR_BAD_URL, 0,
_("The external relative URL '%s' cannot have "
"backpaths, i.e. '..'"),
item->url);
/* Relative to the scheme: Build a new URL from the parts we know. */
if (0 == strncmp("//", url, 2))
{
const char *scheme;
SVN_ERR(uri_scheme(&scheme, repos_root_url, scratch_pool));
*resolved_url = svn_uri_canonicalize(apr_pstrcat(scratch_pool, scheme,
":", url, (char *)NULL),
result_pool);
return SVN_NO_ERROR;
}
/* Relative to the server root: Just replace the path portion of the
parent's URL. */
if (url[0] == '/')
{
parent_dir_uri.path = (char *)url;
*resolved_url = svn_uri_canonicalize(apr_uri_unparse(scratch_pool,
&parent_dir_uri, 0),
result_pool);
return SVN_NO_ERROR;
}
return svn_error_createf(SVN_ERR_BAD_URL, 0,
_("Unrecognized format for the relative external "
"URL '%s'"),
item->url);
}
Index: vendor/subversion/dist/subversion/libsvn_wc/update_editor.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_wc/update_editor.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_wc/update_editor.c (revision 286501)
@@ -1,5561 +1,5559 @@
/*
* update_editor.c : main editor for checkouts and updates
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include <stdlib.h>
#include <string.h>
#include <apr_pools.h>
#include <apr_hash.h>
#include <apr_md5.h>
#include <apr_tables.h>
#include <apr_strings.h>
#include "svn_types.h"
#include "svn_pools.h"
#include "svn_hash.h"
#include "svn_string.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_error.h"
#include "svn_io.h"
#include "svn_private_config.h"
#include "svn_time.h"
#include "wc.h"
#include "adm_files.h"
#include "conflicts.h"
#include "translate.h"
#include "workqueue.h"
#include "private/svn_subr_private.h"
#include "private/svn_wc_private.h"
#include "private/svn_editor.h"
/* Checks whether a svn_wc__db_status_t indicates whether a node is
present in a working copy. Used by the editor implementation */
#define IS_NODE_PRESENT(status) \
((status) != svn_wc__db_status_server_excluded &&\
(status) != svn_wc__db_status_excluded && \
(status) != svn_wc__db_status_not_present)
static svn_error_t *
path_join_under_root(const char **result_path,
const char *base_path,
const char *add_path,
apr_pool_t *result_pool);
/*
* This code handles "checkout" and "update" and "switch".
* A checkout is similar to an update that is only adding new items.
*
* The intended behaviour of "update" and "switch", focusing on the checks
* to be made before applying a change, is:
*
* For each incoming change:
* if target is already in conflict or obstructed:
* skip this change
* else
* if this action will cause a tree conflict:
* record the tree conflict
* skip this change
* else:
* make this change
*
* In more detail:
*
* For each incoming change:
*
* 1. if # Incoming change is inside an item already in conflict:
* a. tree/text/prop change to node beneath tree-conflicted dir
* then # Skip all changes in this conflicted subtree [*1]:
* do not update the Base nor the Working
* notify "skipped because already in conflict" just once
* for the whole conflicted subtree
*
* if # Incoming change affects an item already in conflict:
* b. tree/text/prop change to tree-conflicted dir/file, or
* c. tree change to a text/prop-conflicted file/dir, or
* d. text/prop change to a text/prop-conflicted file/dir [*2], or
* e. tree change to a dir tree containing any conflicts,
* then # Skip this change [*1]:
* do not update the Base nor the Working
* notify "skipped because already in conflict"
*
* 2. if # Incoming change affects an item that's "obstructed":
* a. on-disk node kind doesn't match recorded Working node kind
* (including an absence/presence mis-match),
* then # Skip this change [*1]:
* do not update the Base nor the Working
* notify "skipped because obstructed"
*
* 3. if # Incoming change raises a tree conflict:
* a. tree/text/prop change to node beneath sched-delete dir, or
* b. tree/text/prop change to sched-delete dir/file, or
* c. text/prop change to tree-scheduled dir/file,
* then # Skip this change:
* do not update the Base nor the Working [*3]
* notify "tree conflict"
*
* 4. Apply the change:
* update the Base
* update the Working, possibly raising text/prop conflicts
* notify
*
* Notes:
*
* "Tree change" here refers to an add or delete of the target node,
* including the add or delete part of a copy or move or rename.
*
* [*1] We should skip changes to an entire node, as the base revision number
* applies to the entire node. Not sure how this affects attempts to
* handle text and prop changes separately.
*
* [*2] Details of which combinations of property and text changes conflict
* are not specified here.
*
* [*3] For now, we skip the update, and require the user to:
* - Modify the WC to be compatible with the incoming change;
* - Mark the conflict as resolved;
* - Repeat the update.
* Ideally, it would be possible to resolve any conflict without
* repeating the update. To achieve this, we would have to store the
* necessary data at conflict detection time, and delay the update of
* the Base until the time of resolving.
*/
/*** batons ***/
struct edit_baton
{
/* For updates, the "destination" of the edit is ANCHOR_ABSPATH, the
directory containing TARGET_ABSPATH. If ANCHOR_ABSPATH itself is the
target, the values are identical.
TARGET_BASENAME is the name of TARGET_ABSPATH in ANCHOR_ABSPATH, or "" if
ANCHOR_ABSPATH is the target */
const char *target_basename;
/* Absolute variants of ANCHOR and TARGET */
const char *anchor_abspath;
const char *target_abspath;
/* The DB handle for managing the working copy state. */
svn_wc__db_t *db;
/* Array of file extension patterns to preserve as extensions in
generated conflict files. */
const apr_array_header_t *ext_patterns;
/* Hash mapping const char * absolute working copy paths to depth-first
ordered arrays of svn_prop_inherited_item_t * structures representing
the properties inherited by the base node at that working copy path.
May be NULL. */
apr_hash_t *wcroot_iprops;
/* The revision we're targeting...or something like that. This
starts off as a pointer to the revision to which we are updating,
or SVN_INVALID_REVNUM, but by the end of the edit, should be
pointing to the final revision. */
svn_revnum_t *target_revision;
/* The requested depth of this edit. */
svn_depth_t requested_depth;
/* Is the requested depth merely an operational limitation, or is
also the new sticky ambient depth of the update target? */
svn_boolean_t depth_is_sticky;
/* Need to know if the user wants us to overwrite the 'now' times on
edited/added files with the last-commit-time. */
svn_boolean_t use_commit_times;
/* Was the root actually opened (was this a non-empty edit)? */
svn_boolean_t root_opened;
/* Was the update-target deleted? This is a special situation. */
svn_boolean_t target_deleted;
/* Allow unversioned obstructions when adding a path. */
svn_boolean_t allow_unver_obstructions;
/* Handle local additions as modifications of new nodes */
svn_boolean_t adds_as_modification;
/* If set, we check out into an empty directory. This allows for a number
of conflict checks to be omitted. */
svn_boolean_t clean_checkout;
/* If this is a 'switch' operation, the new relpath of target_abspath,
else NULL. */
const char *switch_relpath;
/* The URL to the root of the repository. */
const char *repos_root;
/* The UUID of the repos, or NULL. */
const char *repos_uuid;
/* External diff3 to use for merges (can be null, in which case
internal merge code is used). */
const char *diff3_cmd;
/* Externals handler */
svn_wc_external_update_t external_func;
void *external_baton;
/* This editor sends back notifications as it edits. */
svn_wc_notify_func2_t notify_func;
void *notify_baton;
/* This editor is normally wrapped in a cancellation editor anyway,
so it doesn't bother to check for cancellation itself. However,
it needs a cancel_func and cancel_baton available to pass to
long-running functions. */
svn_cancel_func_t cancel_func;
void *cancel_baton;
/* This editor will invoke a interactive conflict-resolution
callback, if available. */
svn_wc_conflict_resolver_func2_t conflict_func;
void *conflict_baton;
/* Subtrees that were skipped during the edit, and therefore shouldn't
have their revision/url info updated at the end. If a path is a
directory, its descendants will also be skipped. The keys are paths
relative to the working copy root and the values unspecified. */
apr_hash_t *skipped_trees;
/* A mapping from const char * repos_relpaths to the apr_hash_t * instances
returned from fetch_dirents_func for that repos_relpath. These
are used to avoid issue #3569 in specific update scenarios where a
restricted depth is used. */
apr_hash_t *dir_dirents;
/* Absolute path of the working copy root or NULL if not initialized yet */
const char *wcroot_abspath;
apr_pool_t *pool;
};
/* Record in the edit baton EB that LOCAL_ABSPATH's base version is not being
* updated.
*
* Add to EB->skipped_trees a copy (allocated in EB->pool) of the string
* LOCAL_ABSPATH.
*/
static svn_error_t *
remember_skipped_tree(struct edit_baton *eb,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
svn_hash_sets(eb->skipped_trees,
apr_pstrdup(eb->pool,
svn_dirent_skip_ancestor(eb->wcroot_abspath,
local_abspath)),
(void *)1);
return SVN_NO_ERROR;
}
/* Per directory baton. Lives in its own subpool of the parent directory
or of the edit baton if there is no parent directory */
struct dir_baton
{
/* Basename of this directory. */
const char *name;
/* Absolute path of this directory */
const char *local_abspath;
/* The repository relative path this directory will correspond to. */
const char *new_relpath;
/* The revision of the directory before updating */
svn_revnum_t old_revision;
/* The repos_relpath before updating/switching */
const char *old_repos_relpath;
/* The global edit baton. */
struct edit_baton *edit_baton;
/* Baton for this directory's parent, or NULL if this is the root
directory. */
struct dir_baton *parent_baton;
/* Set if updates to this directory are skipped */
svn_boolean_t skip_this;
/* Set if there was a previous notification for this directory */
svn_boolean_t already_notified;
/* Set if this directory is being added during this editor drive. */
svn_boolean_t adding_dir;
/* Set on a node and its descendants are not present in the working copy
but should still be updated (not skipped). These nodes should all be
marked as deleted. */
svn_boolean_t shadowed;
/* Set on a node when the existing node is obstructed, and the edit operation
continues as semi-shadowed update */
svn_boolean_t edit_obstructed;
/* The (new) changed_* information, cached to avoid retrieving it later */
svn_revnum_t changed_rev;
apr_time_t changed_date;
const char *changed_author;
/* If not NULL, contains a mapping of const char* basenames of children that
have been deleted to their svn_skel_t* tree conflicts.
We store this hash to allow replacements to continue under a just
installed tree conflict.
The add after the delete will then update the tree conflicts information
and reinstall it. */
apr_hash_t *deletion_conflicts;
/* A hash of file names (only the hash key matters) seen by add_file
and not yet added to the database by close_file. */
apr_hash_t *not_present_files;
/* Set if an unversioned dir of the same name already existed in
this directory. */
svn_boolean_t obstruction_found;
/* Set if a dir of the same name already exists and is
scheduled for addition without history. */
svn_boolean_t add_existed;
/* An array of svn_prop_t structures, representing all the property
changes to be applied to this directory. */
apr_array_header_t *propchanges;
/* A boolean indicating whether this node or one of its children has
received any 'real' changes. Used to avoid tree conflicts for simple
entryprop changes, like lock management */
svn_boolean_t edited;
/* The tree conflict to install once the node is really edited */
svn_skel_t *edit_conflict;
/* The bump information for this directory. */
struct bump_dir_info *bump_info;
/* The depth of the directory in the wc (or inferred if added). Not
used for filtering; we have a separate wrapping editor for that. */
svn_depth_t ambient_depth;
/* Was the directory marked as incomplete before the update?
(In other words, are we resuming an interrupted update?)
If WAS_INCOMPLETE is set to TRUE we expect to receive all child nodes
and properties for/of the directory. If WAS_INCOMPLETE is FALSE then
we only receive the changes in/for children and properties.*/
svn_boolean_t was_incomplete;
/* The pool in which this baton itself is allocated. */
apr_pool_t *pool;
/* how many nodes are referring to baton? */
int ref_count;
};
struct handler_baton
{
svn_txdelta_window_handler_t apply_handler;
void *apply_baton;
apr_pool_t *pool;
struct file_baton *fb;
/* Where we are assembling the new file. */
const char *new_text_base_tmp_abspath;
/* The expected source checksum of the text source or NULL if no base
checksum is available (MD5 if the server provides a checksum, SHA1 if
the server doesn't) */
svn_checksum_t *expected_source_checksum;
/* Why two checksums?
The editor currently provides an md5 which we use to detect corruption
during transmission. We use the sha1 inside libsvn_wc both for pristine
handling and corruption detection. In the future, the editor will also
provide a sha1, so we may not have to calculate both, but for the time
being, that's the way it is. */
/* The calculated checksum of the text source or NULL if the acual
checksum is not being calculated. The checksum kind is identical to the
kind of expected_source_checksum. */
svn_checksum_t *actual_source_checksum;
/* The stream used to calculate the source checksums */
svn_stream_t *source_checksum_stream;
/* A calculated MD5 digest of NEW_TEXT_BASE_TMP_ABSPATH.
This is initialized to all zeroes when the baton is created, then
populated with the MD5 digest of the resultant fulltext after the
last window is handled by the handler returned from
apply_textdelta(). */
unsigned char new_text_base_md5_digest[APR_MD5_DIGESTSIZE];
/* A calculated SHA-1 of NEW_TEXT_BASE_TMP_ABSPATH, which we'll use for
eventually writing the pristine. */
svn_checksum_t * new_text_base_sha1_checksum;
};
/* Get an empty file in the temporary area for WRI_ABSPATH. The file will
not be set for automatic deletion, and the name will be returned in
TMP_FILENAME.
This implementation creates a new empty file with a unique name.
### This is inefficient for callers that just want an empty file to read
### from. There could be (and there used to be) a permanent, shared
### empty file for this purpose.
### This is inefficient for callers that just want to reserve a unique
### file name to create later. A better way may not be readily available.
*/
static svn_error_t *
get_empty_tmp_file(const char **tmp_filename,
svn_wc__db_t *db,
const char *wri_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *temp_dir_abspath;
SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db, wri_abspath,
scratch_pool, scratch_pool));
SVN_ERR(svn_io_open_unique_file3(NULL, tmp_filename, temp_dir_abspath,
svn_io_file_del_none,
scratch_pool, scratch_pool));
return SVN_NO_ERROR;
}
/* An APR pool cleanup handler. This runs the working queue for an
editor baton. */
static apr_status_t
cleanup_edit_baton(void *edit_baton)
{
struct edit_baton *eb = edit_baton;
svn_error_t *err;
apr_pool_t *pool = apr_pool_parent_get(eb->pool);
err = svn_wc__wq_run(eb->db, eb->wcroot_abspath,
NULL /* cancel_func */, NULL /* cancel_baton */,
pool);
if (err)
{
apr_status_t apr_err = err->apr_err;
svn_error_clear(err);
return apr_err;
}
return APR_SUCCESS;
}
/* Make a new dir baton in a subpool of PB->pool. PB is the parent baton.
If PATH and PB are NULL, this is the root directory of the edit; in this
case, make the new dir baton in a subpool of EB->pool.
ADDING should be TRUE if we are adding this directory. */
static svn_error_t *
make_dir_baton(struct dir_baton **d_p,
const char *path,
struct edit_baton *eb,
struct dir_baton *pb,
svn_boolean_t adding,
apr_pool_t *scratch_pool)
{
apr_pool_t *dir_pool;
struct dir_baton *d;
if (pb != NULL)
dir_pool = svn_pool_create(pb->pool);
else
dir_pool = svn_pool_create(eb->pool);
SVN_ERR_ASSERT(path || (! pb));
/* Okay, no easy out, so allocate and initialize a dir baton. */
d = apr_pcalloc(dir_pool, sizeof(*d));
/* Construct the PATH and baseNAME of this directory. */
if (path)
{
d->name = svn_dirent_basename(path, dir_pool);
SVN_ERR(path_join_under_root(&d->local_abspath,
pb->local_abspath, d->name, dir_pool));
}
else
{
/* This is the root baton. */
d->name = NULL;
d->local_abspath = eb->anchor_abspath;
}
/* Figure out the new_relpath for this directory. */
if (eb->switch_relpath)
{
/* Handle switches... */
if (pb == NULL)
{
if (*eb->target_basename == '\0')
{
/* No parent baton and target_basename=="" means that we are
the target of the switch. Thus, our NEW_RELPATH will be
the SWITCH_RELPATH. */
d->new_relpath = eb->switch_relpath;
}
else
{
/* This node is NOT the target of the switch (one of our
children is the target); therefore, it must already exist.
Get its old REPOS_RELPATH, as it won't be changing. */
SVN_ERR(svn_wc__db_scan_base_repos(&d->new_relpath, NULL, NULL,
eb->db, d->local_abspath,
dir_pool, scratch_pool));
}
}
else
{
/* This directory is *not* the root (has a parent). If there is
no grandparent, then we may have anchored at the parent,
and self is the target. If we match the target, then set
NEW_RELPATH to the SWITCH_RELPATH.
Otherwise, we simply extend NEW_RELPATH from the parent. */
if (pb->parent_baton == NULL
&& strcmp(eb->target_basename, d->name) == 0)
d->new_relpath = eb->switch_relpath;
else
d->new_relpath = svn_relpath_join(pb->new_relpath, d->name,
dir_pool);
}
}
else /* must be an update */
{
/* If we are adding the node, then simply extend the parent's
relpath for our own. */
if (adding)
{
SVN_ERR_ASSERT(pb != NULL);
d->new_relpath = svn_relpath_join(pb->new_relpath, d->name,
dir_pool);
}
else
{
SVN_ERR(svn_wc__db_scan_base_repos(&d->new_relpath, NULL, NULL,
eb->db, d->local_abspath,
dir_pool, scratch_pool));
SVN_ERR_ASSERT(d->new_relpath);
}
}
d->edit_baton = eb;
d->parent_baton = pb;
d->pool = dir_pool;
d->propchanges = apr_array_make(dir_pool, 1, sizeof(svn_prop_t));
d->obstruction_found = FALSE;
d->add_existed = FALSE;
d->ref_count = 1;
d->old_revision = SVN_INVALID_REVNUM;
d->adding_dir = adding;
d->changed_rev = SVN_INVALID_REVNUM;
d->not_present_files = apr_hash_make(dir_pool);
/* Copy some flags from the parent baton */
if (pb)
{
d->skip_this = pb->skip_this;
d->shadowed = pb->shadowed || pb->edit_obstructed;
/* the parent's bump info has one more referer */
pb->ref_count++;
}
/* The caller of this function needs to fill these in. */
d->ambient_depth = svn_depth_unknown;
d->was_incomplete = FALSE;
*d_p = d;
return SVN_NO_ERROR;
}
/* Forward declarations. */
static svn_error_t *
already_in_a_tree_conflict(svn_boolean_t *conflicted,
svn_boolean_t *ignored,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool);
static void
do_notification(const struct edit_baton *eb,
const char *local_abspath,
svn_node_kind_t kind,
svn_wc_notify_action_t action,
apr_pool_t *scratch_pool)
{
svn_wc_notify_t *notify;
if (eb->notify_func == NULL)
return;
notify = svn_wc_create_notify(local_abspath, action, scratch_pool);
notify->kind = kind;
(*eb->notify_func)(eb->notify_baton, notify, scratch_pool);
}
/* Decrement the directory's reference count. If it hits zero,
then this directory is "done". This means it is safe to clear its pool.
In addition, when the directory is "done", we recurse to possible cleanup
the parent directory.
*/
static svn_error_t *
maybe_release_dir_info(struct dir_baton *db)
{
db->ref_count--;
if (!db->ref_count)
{
struct dir_baton *pb = db->parent_baton;
svn_pool_destroy(db->pool);
if (pb)
SVN_ERR(maybe_release_dir_info(pb));
}
return SVN_NO_ERROR;
}
/* Per file baton. Lives in its own subpool below the pool of the parent
directory */
struct file_baton
{
/* Pool specific to this file_baton. */
apr_pool_t *pool;
/* Name of this file (its entry in the directory). */
const char *name;
/* Absolute path to this file */
const char *local_abspath;
/* The repository relative path this file will correspond to. */
const char *new_relpath;
/* The revision of the file before updating */
svn_revnum_t old_revision;
/* The repos_relpath before updating/switching */
const char *old_repos_relpath;
/* The global edit baton. */
struct edit_baton *edit_baton;
/* The parent directory of this file. */
struct dir_baton *dir_baton;
/* Set if updates to this directory are skipped */
svn_boolean_t skip_this;
/* Set if there was a previous notification */
svn_boolean_t already_notified;
/* Set if this file is new. */
svn_boolean_t adding_file;
/* Set if an unversioned file of the same name already existed in
this directory. */
svn_boolean_t obstruction_found;
/* Set if a file of the same name already exists and is
scheduled for addition without history. */
svn_boolean_t add_existed;
/* Set if this file is being added in the BASE layer, but is not-present
in the working copy (replaced, deleted, etc.). */
svn_boolean_t shadowed;
/* Set on a node when the existing node is obstructed, and the edit operation
continues as semi-shadowed update */
svn_boolean_t edit_obstructed;
/* The (new) changed_* information, cached to avoid retrieving it later */
svn_revnum_t changed_rev;
apr_time_t changed_date;
const char *changed_author;
/* If there are file content changes, these are the checksums of the
resulting new text base, which is in the pristine store, else NULL. */
const svn_checksum_t *new_text_base_md5_checksum;
const svn_checksum_t *new_text_base_sha1_checksum;
/* The checksum of the file before the update */
const svn_checksum_t *original_checksum;
/* An array of svn_prop_t structures, representing all the property
changes to be applied to this file. Once a file baton is
initialized, this is never NULL, but it may have zero elements. */
apr_array_header_t *propchanges;
/* For existing files, whether there are local modifications. FALSE for added
files */
svn_boolean_t local_prop_mods;
/* Bump information for the directory this file lives in */
struct bump_dir_info *bump_info;
/* A boolean indicating whether this node or one of its children has
received any 'real' changes. Used to avoid tree conflicts for simple
entryprop changes, like lock management */
svn_boolean_t edited;
/* The tree conflict to install once the node is really edited */
svn_skel_t *edit_conflict;
};
/* Make a new file baton in a subpool of PB->pool. PB is the parent baton.
* PATH is relative to the root of the edit. ADDING tells whether this file
* is being added. */
static svn_error_t *
make_file_baton(struct file_baton **f_p,
struct dir_baton *pb,
const char *path,
svn_boolean_t adding,
apr_pool_t *scratch_pool)
{
struct edit_baton *eb = pb->edit_baton;
apr_pool_t *file_pool = svn_pool_create(pb->pool);
struct file_baton *f = apr_pcalloc(file_pool, sizeof(*f));
SVN_ERR_ASSERT(path);
/* Make the file's on-disk name. */
f->name = svn_dirent_basename(path, file_pool);
f->old_revision = SVN_INVALID_REVNUM;
SVN_ERR(path_join_under_root(&f->local_abspath,
pb->local_abspath, f->name, file_pool));
/* Figure out the new URL for this file. */
if (eb->switch_relpath)
{
/* Handle switches... */
/* This file has a parent directory. If there is
no grandparent, then we may have anchored at the parent,
and self is the target. If we match the target, then set
NEW_RELPATH to the SWITCH_RELPATH.
Otherwise, we simply extend NEW_RELPATH from the parent. */
if (pb->parent_baton == NULL
&& strcmp(eb->target_basename, f->name) == 0)
f->new_relpath = eb->switch_relpath;
else
f->new_relpath = svn_relpath_join(pb->new_relpath, f->name,
file_pool);
}
else /* must be an update */
{
if (adding)
f->new_relpath = svn_relpath_join(pb->new_relpath, f->name, file_pool);
else
{
SVN_ERR(svn_wc__db_scan_base_repos(&f->new_relpath, NULL, NULL,
eb->db, f->local_abspath,
file_pool, scratch_pool));
SVN_ERR_ASSERT(f->new_relpath);
}
}
f->pool = file_pool;
f->edit_baton = pb->edit_baton;
f->propchanges = apr_array_make(file_pool, 1, sizeof(svn_prop_t));
f->bump_info = pb->bump_info;
f->adding_file = adding;
f->obstruction_found = FALSE;
f->add_existed = FALSE;
f->skip_this = pb->skip_this;
f->shadowed = pb->shadowed || pb->edit_obstructed;
f->dir_baton = pb;
f->changed_rev = SVN_INVALID_REVNUM;
/* the directory has one more referer now */
pb->ref_count++;
*f_p = f;
return SVN_NO_ERROR;
}
/* Complete a conflict skel by describing the update.
*
* LOCAL_KIND is the node kind of the tree conflict victim in the
* working copy.
*
* All temporary allocations are be made in SCRATCH_POOL, while allocations
* needed for the returned conflict struct are made in RESULT_POOL.
*/
static svn_error_t *
complete_conflict(svn_skel_t *conflict,
const struct edit_baton *eb,
const char *local_abspath,
const char *old_repos_relpath,
svn_revnum_t old_revision,
const char *new_repos_relpath,
svn_node_kind_t local_kind,
svn_node_kind_t target_kind,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc_conflict_version_t *original_version;
svn_wc_conflict_version_t *target_version;
svn_boolean_t is_complete;
if (!conflict)
return SVN_NO_ERROR; /* Not conflicted */
SVN_ERR(svn_wc__conflict_skel_is_complete(&is_complete, conflict));
if (is_complete)
return SVN_NO_ERROR; /* Already completed */
if (old_repos_relpath)
original_version = svn_wc_conflict_version_create2(eb->repos_root,
eb->repos_uuid,
old_repos_relpath,
old_revision,
local_kind,
result_pool);
else
original_version = NULL;
if (new_repos_relpath)
target_version = svn_wc_conflict_version_create2(eb->repos_root,
eb->repos_uuid,
new_repos_relpath,
*eb->target_revision,
target_kind,
result_pool);
else
target_version = NULL;
if (eb->switch_relpath)
SVN_ERR(svn_wc__conflict_skel_set_op_switch(conflict,
original_version,
target_version,
result_pool, scratch_pool));
else
SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict,
original_version,
target_version,
result_pool, scratch_pool));
return SVN_NO_ERROR;
}
/* Called when a directory is really edited, to avoid marking a
tree conflict on a node for a no-change edit */
static svn_error_t *
mark_directory_edited(struct dir_baton *db, apr_pool_t *scratch_pool)
{
if (db->edited)
return SVN_NO_ERROR;
if (db->parent_baton)
SVN_ERR(mark_directory_edited(db->parent_baton, scratch_pool));
db->edited = TRUE;
if (db->edit_conflict)
{
/* We have a (delayed) tree conflict to install */
SVN_ERR(complete_conflict(db->edit_conflict, db->edit_baton,
db->local_abspath,
db->old_repos_relpath, db->old_revision,
db->new_relpath,
svn_node_dir, svn_node_dir,
db->pool, scratch_pool));
SVN_ERR(svn_wc__db_op_mark_conflict(db->edit_baton->db,
db->local_abspath,
db->edit_conflict, NULL,
scratch_pool));
do_notification(db->edit_baton, db->local_abspath, svn_node_dir,
svn_wc_notify_tree_conflict, scratch_pool);
db->already_notified = TRUE;
}
return SVN_NO_ERROR;
}
/* Called when a file is really edited, to avoid marking a
tree conflict on a node for a no-change edit */
static svn_error_t *
mark_file_edited(struct file_baton *fb, apr_pool_t *scratch_pool)
{
if (fb->edited)
return SVN_NO_ERROR;
SVN_ERR(mark_directory_edited(fb->dir_baton, scratch_pool));
fb->edited = TRUE;
if (fb->edit_conflict)
{
/* We have a (delayed) tree conflict to install */
SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton,
fb->local_abspath, fb->old_repos_relpath,
fb->old_revision, fb->new_relpath,
svn_node_file, svn_node_file,
fb->pool, scratch_pool));
SVN_ERR(svn_wc__db_op_mark_conflict(fb->edit_baton->db,
fb->local_abspath,
fb->edit_conflict, NULL,
scratch_pool));
do_notification(fb->edit_baton, fb->local_abspath, svn_node_file,
svn_wc_notify_tree_conflict, scratch_pool);
fb->already_notified = TRUE;
}
return SVN_NO_ERROR;
}
/* Handle the next delta window of the file described by BATON. If it is
* the end (WINDOW == NULL), then check the checksum, store the text in the
* pristine store and write its details into BATON->fb->new_text_base_*. */
static svn_error_t *
window_handler(svn_txdelta_window_t *window, void *baton)
{
struct handler_baton *hb = baton;
struct file_baton *fb = hb->fb;
svn_wc__db_t *db = fb->edit_baton->db;
svn_error_t *err;
/* Apply this window. We may be done at that point. */
err = hb->apply_handler(window, hb->apply_baton);
if (window != NULL && !err)
return SVN_NO_ERROR;
if (hb->expected_source_checksum)
{
/* Close the stream to calculate HB->actual_source_md5_checksum. */
svn_error_t *err2 = svn_stream_close(hb->source_checksum_stream);
if (!err2)
{
SVN_ERR_ASSERT(hb->expected_source_checksum->kind ==
hb->actual_source_checksum->kind);
if (!svn_checksum_match(hb->expected_source_checksum,
hb->actual_source_checksum))
{
err = svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, err,
_("Checksum mismatch while updating '%s':\n"
" expected: %s\n"
" actual: %s\n"),
svn_dirent_local_style(fb->local_abspath, hb->pool),
svn_checksum_to_cstring(hb->expected_source_checksum,
hb->pool),
svn_checksum_to_cstring(hb->actual_source_checksum,
hb->pool));
}
}
err = svn_error_compose_create(err, err2);
}
if (err)
{
/* We failed to apply the delta; clean up the temporary file if it
already created by lazy_open_target(). */
if (hb->new_text_base_tmp_abspath)
{
svn_error_clear(svn_io_remove_file2(hb->new_text_base_tmp_abspath,
TRUE, hb->pool));
}
}
else
{
/* Tell the file baton about the new text base's checksums. */
fb->new_text_base_md5_checksum =
svn_checksum__from_digest_md5(hb->new_text_base_md5_digest, fb->pool);
fb->new_text_base_sha1_checksum =
svn_checksum_dup(hb->new_text_base_sha1_checksum, fb->pool);
/* Store the new pristine text in the pristine store now. Later, in a
single transaction we will update the BASE_NODE to include a
reference to this pristine text's checksum. */
SVN_ERR(svn_wc__db_pristine_install(db, hb->new_text_base_tmp_abspath,
fb->new_text_base_sha1_checksum,
fb->new_text_base_md5_checksum,
hb->pool));
}
svn_pool_destroy(hb->pool);
return err;
}
/* Find the last-change info within ENTRY_PROPS, and return then in the
CHANGED_* parameters. Each parameter will be initialized to its "none"
value, and will contain the relavent info if found.
CHANGED_AUTHOR will be allocated in RESULT_POOL. SCRATCH_POOL will be
used for some temporary allocations.
*/
static svn_error_t *
accumulate_last_change(svn_revnum_t *changed_rev,
apr_time_t *changed_date,
const char **changed_author,
const apr_array_header_t *entry_props,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
int i;
*changed_rev = SVN_INVALID_REVNUM;
*changed_date = 0;
*changed_author = NULL;
for (i = 0; i < entry_props->nelts; ++i)
{
const svn_prop_t *prop = &APR_ARRAY_IDX(entry_props, i, svn_prop_t);
/* A prop value of NULL means the information was not
available. We don't remove this field from the entries
file; we have convention just leave it empty. So let's
just skip those entry props that have no values. */
if (! prop->value)
continue;
if (! strcmp(prop->name, SVN_PROP_ENTRY_LAST_AUTHOR))
*changed_author = apr_pstrdup(result_pool, prop->value->data);
else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_REV))
{
apr_int64_t rev;
SVN_ERR(svn_cstring_atoi64(&rev, prop->value->data));
*changed_rev = (svn_revnum_t)rev;
}
else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_DATE))
SVN_ERR(svn_time_from_cstring(changed_date, prop->value->data,
scratch_pool));
/* Starting with Subversion 1.7 we ignore the SVN_PROP_ENTRY_UUID
property here. */
}
return SVN_NO_ERROR;
}
/* Join ADD_PATH to BASE_PATH. If ADD_PATH is absolute, or if any ".."
* component of it resolves to a path above BASE_PATH, then return
* SVN_ERR_WC_OBSTRUCTED_UPDATE.
*
* This is to prevent the situation where the repository contains,
* say, "..\nastyfile". Although that's perfectly legal on some
* systems, when checked out onto Win32 it would cause "nastyfile" to
* be created in the parent of the current edit directory.
*
* (http://cve.mitre.org/cgi-bin/cvename.cgi?name=2007-3846)
*/
static svn_error_t *
path_join_under_root(const char **result_path,
const char *base_path,
const char *add_path,
apr_pool_t *pool)
{
svn_boolean_t under_root;
SVN_ERR(svn_dirent_is_under_root(&under_root,
result_path, base_path, add_path, pool));
if (! under_root)
{
return svn_error_createf(
SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
_("Path '%s' is not in the working copy"),
svn_dirent_local_style(svn_dirent_join(base_path, add_path, pool),
pool));
}
/* This catches issue #3288 */
if (strcmp(add_path, svn_dirent_basename(*result_path, NULL)) != 0)
{
return svn_error_createf(
SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
_("'%s' is not valid as filename in directory '%s'"),
svn_dirent_local_style(add_path, pool),
svn_dirent_local_style(base_path, pool));
}
return SVN_NO_ERROR;
}
/*** The callbacks we'll plug into an svn_delta_editor_t structure. ***/
/* An svn_delta_editor_t function. */
static svn_error_t *
set_target_revision(void *edit_baton,
svn_revnum_t target_revision,
apr_pool_t *pool)
{
struct edit_baton *eb = edit_baton;
*(eb->target_revision) = target_revision;
return SVN_NO_ERROR;
}
static svn_error_t *
check_tree_conflict(svn_skel_t **pconflict,
struct edit_baton *eb,
const char *local_abspath,
svn_wc__db_status_t working_status,
svn_boolean_t exists_in_repos,
svn_node_kind_t expected_kind,
svn_wc_conflict_action_t action,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* An svn_delta_editor_t function. */
static svn_error_t *
open_root(void *edit_baton,
svn_revnum_t base_revision, /* This is ignored in co */
apr_pool_t *pool,
void **dir_baton)
{
struct edit_baton *eb = edit_baton;
struct dir_baton *db;
svn_boolean_t already_conflicted, conflict_ignored;
svn_error_t *err;
svn_wc__db_status_t status;
svn_wc__db_status_t base_status;
svn_node_kind_t kind;
svn_boolean_t have_work;
/* Note that something interesting is actually happening in this
edit run. */
eb->root_opened = TRUE;
SVN_ERR(make_dir_baton(&db, NULL, eb, NULL, FALSE, pool));
*dir_baton = db;
err = already_in_a_tree_conflict(&already_conflicted, &conflict_ignored,
eb->db, db->local_abspath, pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
svn_error_clear(err);
already_conflicted = conflict_ignored = FALSE;
}
else if (already_conflicted)
{
/* Record a skip of both the anchor and target in the skipped tree
as the anchor itself might not be updated */
SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
SVN_ERR(remember_skipped_tree(eb, eb->target_abspath, pool));
db->skip_this = TRUE;
db->already_notified = TRUE;
/* Notify that we skipped the target, while we actually skipped
the anchor */
do_notification(eb, eb->target_abspath, svn_node_unknown,
svn_wc_notify_skip_conflicted, pool);
return SVN_NO_ERROR;
}
SVN_ERR(svn_wc__db_read_info(&status, &kind, &db->old_revision,
&db->old_repos_relpath, NULL, NULL,
&db->changed_rev, &db->changed_date,
&db->changed_author, &db->ambient_depth,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, &have_work,
eb->db, db->local_abspath,
db->pool, pool));
if (conflict_ignored)
{
db->shadowed = TRUE;
}
else if (have_work)
{
const char *move_src_root_abspath;
SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, &move_src_root_abspath,
NULL, eb->db, db->local_abspath,
pool, pool));
if (move_src_root_abspath || *eb->target_basename == '\0')
SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL,
&db->old_revision,
&db->old_repos_relpath, NULL, NULL,
&db->changed_rev, &db->changed_date,
&db->changed_author,
&db->ambient_depth,
NULL, NULL, NULL, NULL, NULL, NULL,
eb->db, db->local_abspath,
db->pool, pool));
if (move_src_root_abspath)
{
/* This is an update anchored inside a move. We need to
raise a move-edit tree-conflict on the move root to
update the move destination. */
svn_skel_t *tree_conflict = svn_wc__conflict_skel_create(pool);
SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
tree_conflict, eb->db, move_src_root_abspath,
svn_wc_conflict_reason_moved_away,
svn_wc_conflict_action_edit,
move_src_root_abspath, pool, pool));
if (strcmp(db->local_abspath, move_src_root_abspath))
{
/* We are raising the tree-conflict on some parent of
the edit root, we won't be handling that path again
so raise the conflict now. */
SVN_ERR(complete_conflict(tree_conflict, eb,
move_src_root_abspath,
db->old_repos_relpath,
db->old_revision, db->new_relpath,
svn_node_dir, svn_node_dir,
pool, pool));
SVN_ERR(svn_wc__db_op_mark_conflict(eb->db,
move_src_root_abspath,
tree_conflict,
NULL, pool));
do_notification(eb, move_src_root_abspath, svn_node_dir,
svn_wc_notify_tree_conflict, pool);
}
else
db->edit_conflict = tree_conflict;
}
db->shadowed = TRUE; /* Needed for the close_directory() on the root, to
make sure it doesn't use the ACTUAL tree */
}
else
base_status = status;
if (*eb->target_basename == '\0')
{
/* For an update with a NULL target, this is equivalent to open_dir(): */
db->was_incomplete = (base_status == svn_wc__db_status_incomplete);
/* ### TODO: Add some tree conflict and obstruction detection, etc. like
open_directory() does.
(or find a way to reuse that code here)
### BH 2013: I don't think we need all of the detection here, as the
user explicitly asked to update this node. So we don't
have to tell that it is a local replacement/delete.
*/
SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db,
db->local_abspath,
db->new_relpath,
*eb->target_revision,
pool));
}
return SVN_NO_ERROR;
}
/* ===================================================================== */
/* Checking for local modifications. */
/* A baton for use with modcheck_found_entry(). */
typedef struct modcheck_baton_t {
svn_wc__db_t *db; /* wc_db to access nodes */
svn_boolean_t found_mod; /* whether a modification has been found */
svn_boolean_t found_not_delete; /* Found a not-delete modification */
} modcheck_baton_t;
/* An implementation of svn_wc_status_func4_t. */
static svn_error_t *
modcheck_callback(void *baton,
const char *local_abspath,
const svn_wc_status3_t *status,
apr_pool_t *scratch_pool)
{
modcheck_baton_t *mb = baton;
switch (status->node_status)
{
case svn_wc_status_normal:
case svn_wc_status_incomplete:
case svn_wc_status_ignored:
case svn_wc_status_none:
case svn_wc_status_unversioned:
case svn_wc_status_external:
break;
case svn_wc_status_deleted:
mb->found_mod = TRUE;
break;
case svn_wc_status_missing:
case svn_wc_status_obstructed:
mb->found_mod = TRUE;
mb->found_not_delete = TRUE;
/* Exit from the status walker: We know what we want to know */
return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
default:
case svn_wc_status_added:
case svn_wc_status_replaced:
case svn_wc_status_modified:
mb->found_mod = TRUE;
mb->found_not_delete = TRUE;
/* Exit from the status walker: We know what we want to know */
return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
}
return SVN_NO_ERROR;
}
/* Set *MODIFIED to true iff there are any local modifications within the
* tree rooted at LOCAL_ABSPATH, using DB. If *MODIFIED
* is set to true and all the local modifications were deletes then set
* *ALL_EDITS_ARE_DELETES to true, set it to false otherwise. LOCAL_ABSPATH
* may be a file or a directory. */
svn_error_t *
svn_wc__node_has_local_mods(svn_boolean_t *modified,
svn_boolean_t *all_edits_are_deletes,
svn_wc__db_t *db,
const char *local_abspath,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
modcheck_baton_t modcheck_baton = { NULL, FALSE, FALSE };
svn_error_t *err;
modcheck_baton.db = db;
/* Walk the WC tree for status with depth infinity, looking for any local
* modifications. If it's a "sparse" directory, that's OK: there can be
* no local mods in the pieces that aren't present in the WC. */
err = svn_wc__internal_walk_status(db, local_abspath,
svn_depth_infinity,
FALSE, FALSE, FALSE, NULL,
modcheck_callback, &modcheck_baton,
cancel_func, cancel_baton,
scratch_pool);
if (err && err->apr_err == SVN_ERR_CEASE_INVOCATION)
svn_error_clear(err);
else
SVN_ERR(err);
*modified = modcheck_baton.found_mod;
*all_edits_are_deletes = (modcheck_baton.found_mod
&& !modcheck_baton.found_not_delete);
return SVN_NO_ERROR;
}
/* Indicates an unset svn_wc_conflict_reason_t. */
#define SVN_WC_CONFLICT_REASON_NONE (svn_wc_conflict_reason_t)(-1)
/* Check whether the incoming change ACTION on FULL_PATH would conflict with
* LOCAL_ABSPATH's scheduled change. If so, then raise a tree conflict with
* LOCAL_ABSPATH as the victim.
*
* The edit baton EB gives information including whether the operation is
* an update or a switch.
*
* WORKING_STATUS is the current node status of LOCAL_ABSPATH
* and EXISTS_IN_REPOS specifies whether a BASE_NODE representation for exists
* for this node. In that case the on disk type is compared to EXPECTED_KIND.
*
* If a tree conflict reason was found for the incoming action, the resulting
* tree conflict info is returned in *PCONFLICT. PCONFLICT must be non-NULL,
* while *PCONFLICT is always overwritten.
*
* The tree conflict is allocated in RESULT_POOL. Temporary allocations use
* SCRATCH_POOL.
*/
static svn_error_t *
check_tree_conflict(svn_skel_t **pconflict,
struct edit_baton *eb,
const char *local_abspath,
svn_wc__db_status_t working_status,
svn_boolean_t exists_in_repos,
svn_node_kind_t expected_kind,
svn_wc_conflict_action_t action,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc_conflict_reason_t reason = SVN_WC_CONFLICT_REASON_NONE;
svn_boolean_t modified = FALSE;
svn_boolean_t all_mods_are_deletes = FALSE;
const char *move_src_op_root_abspath = NULL;
*pconflict = NULL;
/* Find out if there are any local changes to this node that may
* be the "reason" of a tree-conflict with the incoming "action". */
switch (working_status)
{
case svn_wc__db_status_added:
case svn_wc__db_status_moved_here:
case svn_wc__db_status_copied:
if (!exists_in_repos)
{
/* The node is locally added, and it did not exist before. This
* is an 'update', so the local add can only conflict with an
* incoming 'add'. In fact, if we receive anything else than an
* svn_wc_conflict_action_add (which includes 'added',
* 'copied-here' and 'moved-here') during update on a node that
* did not exist before, then something is very wrong.
* Note that if there was no action on the node, this code
* would not have been called in the first place. */
SVN_ERR_ASSERT(action == svn_wc_conflict_action_add);
/* Scan the addition in case our caller didn't. */
if (working_status == svn_wc__db_status_added)
SVN_ERR(svn_wc__db_scan_addition(&working_status, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL,
eb->db, local_abspath,
scratch_pool, scratch_pool));
if (working_status == svn_wc__db_status_moved_here)
reason = svn_wc_conflict_reason_moved_here;
else
reason = svn_wc_conflict_reason_added;
}
else
{
/* The node is locally replaced but could also be moved-away. */
SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, NULL,
&move_src_op_root_abspath,
eb->db, local_abspath,
scratch_pool, scratch_pool));
if (move_src_op_root_abspath)
reason = svn_wc_conflict_reason_moved_away;
else
reason = svn_wc_conflict_reason_replaced;
}
break;
case svn_wc__db_status_deleted:
{
SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, NULL,
&move_src_op_root_abspath,
eb->db, local_abspath,
scratch_pool, scratch_pool));
if (move_src_op_root_abspath)
reason = svn_wc_conflict_reason_moved_away;
else
reason = svn_wc_conflict_reason_deleted;
}
break;
case svn_wc__db_status_incomplete:
/* We used svn_wc__db_read_info(), so 'incomplete' means
* - there is no node in the WORKING tree
* - a BASE node is known to exist
* So the node exists and is essentially 'normal'. We still need to
* check prop and text mods, and those checks will retrieve the
* missing information (hopefully). */
case svn_wc__db_status_normal:
if (action == svn_wc_conflict_action_edit)
{
/* An edit onto a local edit or onto *no* local changes is no
* tree-conflict. (It's possibly a text- or prop-conflict,
* but we don't handle those here.)
*
* Except when there is a local obstruction
*/
if (exists_in_repos)
{
svn_node_kind_t disk_kind;
SVN_ERR(svn_io_check_path(local_abspath, &disk_kind,
scratch_pool));
if (disk_kind != expected_kind && disk_kind != svn_node_none)
{
reason = svn_wc_conflict_reason_obstructed;
break;
}
}
return SVN_NO_ERROR;
}
/* Replace is handled as delete and then specifically in
add_directory() and add_file(), so we only expect deletes here */
SVN_ERR_ASSERT(action == svn_wc_conflict_action_delete);
/* Check if the update wants to delete or replace a locally
* modified node. */
/* Do a deep tree detection of local changes. The update editor will
* not visit the subdirectories of a directory that it wants to delete.
* Therefore, we need to start a separate crawl here. */
SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_mods_are_deletes,
eb->db, local_abspath,
eb->cancel_func, eb->cancel_baton,
scratch_pool));
if (modified)
{
if (all_mods_are_deletes)
reason = svn_wc_conflict_reason_deleted;
else
reason = svn_wc_conflict_reason_edited;
}
break;
case svn_wc__db_status_server_excluded:
/* Not allowed to view the node. Not allowed to report tree
* conflicts. */
case svn_wc__db_status_excluded:
/* Locally marked as excluded. No conflicts wanted. */
case svn_wc__db_status_not_present:
/* A committed delete (but parent not updated). The delete is
committed, so no conflict possible during update. */
return SVN_NO_ERROR;
case svn_wc__db_status_base_deleted:
/* An internal status. Should never show up here. */
SVN_ERR_MALFUNCTION();
break;
}
if (reason == SVN_WC_CONFLICT_REASON_NONE)
/* No conflict with the current action. */
return SVN_NO_ERROR;
/* Sanity checks. Note that if there was no action on the node, this function
* would not have been called in the first place.*/
if (reason == svn_wc_conflict_reason_edited
|| reason == svn_wc_conflict_reason_obstructed
|| reason == svn_wc_conflict_reason_deleted
|| reason == svn_wc_conflict_reason_moved_away
|| reason == svn_wc_conflict_reason_replaced)
{
/* When the node existed before (it was locally deleted, replaced or
* edited), then 'update' cannot add it "again". So it can only send
* _action_edit, _delete or _replace. */
if (action != svn_wc_conflict_action_edit
&& action != svn_wc_conflict_action_delete
&& action != svn_wc_conflict_action_replace)
return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
_("Unexpected attempt to add a node at path '%s'"),
svn_dirent_local_style(local_abspath, scratch_pool));
}
else if (reason == svn_wc_conflict_reason_added ||
reason == svn_wc_conflict_reason_moved_here)
{
/* When the node did not exist before (it was locally added),
* then 'update' cannot want to modify it in any way.
* It can only send _action_add. */
if (action != svn_wc_conflict_action_add)
return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
_("Unexpected attempt to edit, delete, or replace "
"a node at path '%s'"),
svn_dirent_local_style(local_abspath, scratch_pool));
}
/* A conflict was detected. Create a conflict skel to record it. */
*pconflict = svn_wc__conflict_skel_create(result_pool);
SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(*pconflict,
eb->db, local_abspath,
reason,
action,
move_src_op_root_abspath,
result_pool, scratch_pool));
return SVN_NO_ERROR;
}
/* If LOCAL_ABSPATH is inside a conflicted tree and the conflict is
* not a moved-away-edit conflict, set *CONFLICTED to TRUE. Otherwise
* set *CONFLICTED to FALSE.
*/
static svn_error_t *
already_in_a_tree_conflict(svn_boolean_t *conflicted,
svn_boolean_t *ignored,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
const char *ancestor_abspath = local_abspath;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
*conflicted = *ignored = FALSE;
while (TRUE)
{
svn_boolean_t is_wc_root;
svn_pool_clear(iterpool);
SVN_ERR(svn_wc__conflicted_for_update_p(conflicted, ignored, db,
ancestor_abspath, TRUE,
scratch_pool));
if (*conflicted || *ignored)
break;
SVN_ERR(svn_wc__db_is_wcroot(&is_wc_root, db, ancestor_abspath,
iterpool));
if (is_wc_root)
break;
ancestor_abspath = svn_dirent_dirname(ancestor_abspath, scratch_pool);
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Temporary helper until the new conflict handling is in place */
static svn_error_t *
node_already_conflicted(svn_boolean_t *conflicted,
svn_boolean_t *conflict_ignored,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
SVN_ERR(svn_wc__conflicted_for_update_p(conflicted, conflict_ignored, db,
local_abspath, FALSE,
scratch_pool));
return SVN_NO_ERROR;
}
/* An svn_delta_editor_t function. */
static svn_error_t *
delete_entry(const char *path,
svn_revnum_t revision,
void *parent_baton,
apr_pool_t *pool)
{
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
const char *base = svn_relpath_basename(path, NULL);
const char *local_abspath;
const char *repos_relpath;
svn_node_kind_t kind, base_kind;
svn_revnum_t old_revision;
svn_boolean_t conflicted;
svn_boolean_t have_work;
svn_skel_t *tree_conflict = NULL;
svn_wc__db_status_t status;
svn_wc__db_status_t base_status;
apr_pool_t *scratch_pool;
svn_boolean_t deleting_target;
svn_boolean_t deleting_switched;
svn_boolean_t keep_as_working = FALSE;
svn_boolean_t queue_deletes = TRUE;
if (pb->skip_this)
return SVN_NO_ERROR;
scratch_pool = svn_pool_create(pb->pool);
SVN_ERR(mark_directory_edited(pb, scratch_pool));
SVN_ERR(path_join_under_root(&local_abspath, pb->local_abspath, base,
scratch_pool));
deleting_target = (strcmp(local_abspath, eb->target_abspath) == 0);
/* Detect obstructing working copies */
{
svn_boolean_t is_root;
SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, local_abspath,
scratch_pool));
if (is_root)
{
/* Just skip this node; a future update will handle it */
SVN_ERR(remember_skipped_tree(eb, local_abspath, pool));
do_notification(eb, local_abspath, svn_node_unknown,
svn_wc_notify_update_skip_obstruction, scratch_pool);
svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
}
}
SVN_ERR(svn_wc__db_read_info(&status, &kind, &old_revision, &repos_relpath,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
&conflicted, NULL, NULL, NULL,
NULL, NULL, &have_work,
eb->db, local_abspath,
scratch_pool, scratch_pool));
if (!have_work)
{
base_status = status;
base_kind = kind;
}
else
SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, &old_revision,
&repos_relpath,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
eb->db, local_abspath,
scratch_pool, scratch_pool));
if (pb->old_repos_relpath && repos_relpath)
{
const char *expected_name;
expected_name = svn_relpath_skip_ancestor(pb->old_repos_relpath,
repos_relpath);
deleting_switched = (!expected_name || strcmp(expected_name, base) != 0);
}
else
deleting_switched = FALSE;
/* Is this path a conflict victim? */
if (pb->shadowed)
conflicted = FALSE; /* Conflict applies to WORKING */
else if (conflicted)
SVN_ERR(node_already_conflicted(&conflicted, NULL,
eb->db, local_abspath, scratch_pool));
if (conflicted)
{
SVN_ERR(remember_skipped_tree(eb, local_abspath, scratch_pool));
do_notification(eb, local_abspath, svn_node_unknown,
svn_wc_notify_skip_conflicted,
scratch_pool);
svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
}
/* Receive the remote removal of excluded/server-excluded/not present node.
Do not notify, but perform the change even when the node is shadowed */
if (base_status == svn_wc__db_status_not_present
|| base_status == svn_wc__db_status_excluded
|| base_status == svn_wc__db_status_server_excluded)
{
SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
FALSE /* keep_as_working */,
FALSE /* queue_deletes */,
FALSE /* remove_locks */,
SVN_INVALID_REVNUM /* not_present_rev */,
NULL, NULL,
scratch_pool));
if (deleting_target)
eb->target_deleted = TRUE;
svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
}
/* Is this path the victim of a newly-discovered tree conflict? If so,
* remember it and notify the client. Then (if it was existing and
* modified), re-schedule the node to be added back again, as a (modified)
* copy of the previous base version. */
/* Check for conflicts only when we haven't already recorded
* a tree-conflict on a parent node. */
if (!pb->shadowed && !pb->edit_obstructed)
{
SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath,
status, TRUE,
(kind == svn_node_dir)
? svn_node_dir
: svn_node_file,
svn_wc_conflict_action_delete,
pb->pool, scratch_pool));
}
else
queue_deletes = FALSE; /* There is no in-wc representation */
if (tree_conflict != NULL)
{
svn_wc_conflict_reason_t reason;
/* When we raise a tree conflict on a node, we don't want to mark the
* node as skipped, to allow a replacement to continue doing at least
* a bit of its work (possibly adding a not present node, for the
* next update) */
if (!pb->deletion_conflicts)
pb->deletion_conflicts = apr_hash_make(pb->pool);
svn_hash_sets(pb->deletion_conflicts, apr_pstrdup(pb->pool, base),
tree_conflict);
SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
eb->db, local_abspath,
tree_conflict,
scratch_pool, scratch_pool));
if (reason == svn_wc_conflict_reason_edited
|| reason == svn_wc_conflict_reason_obstructed)
{
/* The item exists locally and has some sort of local mod.
* It no longer exists in the repository at its target URL@REV.
*
* To prepare the "accept mine" resolution for the tree conflict,
* we must schedule the existing content for re-addition as a copy
* of what it was, but with its local modifications preserved. */
keep_as_working = TRUE;
/* Fall through to remove the BASE_NODEs properly, with potentially
keeping a not-present marker */
}
else if (reason == svn_wc_conflict_reason_deleted
|| reason == svn_wc_conflict_reason_moved_away
|| reason == svn_wc_conflict_reason_replaced)
{
/* The item does not exist locally because it was already shadowed.
* We must complete the deletion, leaving the tree conflict info
* as the only difference from a normal deletion. */
/* Fall through to the normal "delete" code path. */
}
else
SVN_ERR_MALFUNCTION(); /* other reasons are not expected here */
}
SVN_ERR(complete_conflict(tree_conflict, eb, local_abspath, repos_relpath,
old_revision, NULL,
(kind == svn_node_dir)
? svn_node_dir
: svn_node_file,
svn_node_none,
pb->pool, scratch_pool));
/* Issue a wq operation to delete the BASE_NODE data and to delete actual
nodes based on that from disk, but leave any WORKING_NODEs on disk.
Local modifications are already turned into copies at this point.
If the thing being deleted is the *target* of this update, then
we need to recreate a 'deleted' entry, so that the parent can give
accurate reports about itself in the future. */
if (! deleting_target && ! deleting_switched)
{
/* Delete, and do not leave a not-present node. */
SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
keep_as_working, queue_deletes, FALSE,
SVN_INVALID_REVNUM /* not_present_rev */,
tree_conflict, NULL,
scratch_pool));
}
else
{
/* Delete, leaving a not-present node. */
SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
keep_as_working, queue_deletes, FALSE,
*eb->target_revision,
tree_conflict, NULL,
scratch_pool));
if (deleting_target)
eb->target_deleted = TRUE;
else
{
/* Don't remove the not-present marker at the final bump */
SVN_ERR(remember_skipped_tree(eb, local_abspath, pool));
}
}
SVN_ERR(svn_wc__wq_run(eb->db, pb->local_abspath,
eb->cancel_func, eb->cancel_baton,
scratch_pool));
/* Notify. */
if (tree_conflict)
{
if (eb->conflict_func)
SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, local_abspath,
tree_conflict,
NULL /* merge_options */,
eb->conflict_func,
eb->conflict_baton,
eb->cancel_func,
eb->cancel_baton,
scratch_pool));
do_notification(eb, local_abspath, svn_node_unknown,
svn_wc_notify_tree_conflict, scratch_pool);
}
else
{
svn_wc_notify_action_t action = svn_wc_notify_update_delete;
svn_node_kind_t node_kind;
if (pb->shadowed || pb->edit_obstructed)
action = svn_wc_notify_update_shadowed_delete;
if (kind == svn_node_dir)
node_kind = svn_node_dir;
else
node_kind = svn_node_file;
do_notification(eb, local_abspath, node_kind, action, scratch_pool);
}
svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
}
/* An svn_delta_editor_t function. */
static svn_error_t *
add_directory(const char *path,
void *parent_baton,
const char *copyfrom_path,
svn_revnum_t copyfrom_rev,
apr_pool_t *pool,
void **child_baton)
{
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
struct dir_baton *db;
svn_node_kind_t kind;
svn_wc__db_status_t status;
svn_node_kind_t wc_kind;
svn_boolean_t conflicted;
svn_boolean_t conflict_ignored = FALSE;
svn_boolean_t versioned_locally_and_present;
svn_skel_t *tree_conflict = NULL;
svn_error_t *err;
SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev)));
SVN_ERR(make_dir_baton(&db, path, eb, pb, TRUE, pool));
*child_baton = db;
if (db->skip_this)
return SVN_NO_ERROR;
SVN_ERR(mark_directory_edited(db, pool));
if (strcmp(eb->target_abspath, db->local_abspath) == 0)
{
/* The target of the edit is being added, give it the requested
depth of the edit (but convert svn_depth_unknown to
svn_depth_infinity). */
db->ambient_depth = (eb->requested_depth == svn_depth_unknown)
? svn_depth_infinity : eb->requested_depth;
}
else if (eb->requested_depth == svn_depth_immediates
|| (eb->requested_depth == svn_depth_unknown
&& pb->ambient_depth == svn_depth_immediates))
{
db->ambient_depth = svn_depth_empty;
}
else
{
db->ambient_depth = svn_depth_infinity;
}
/* It may not be named the same as the administrative directory. */
if (svn_wc_is_adm_dir(db->name, pool))
return svn_error_createf(
SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
_("Failed to add directory '%s': object of the same name as the "
"administrative directory"),
svn_dirent_local_style(db->local_abspath, pool));
SVN_ERR(svn_io_check_path(db->local_abspath, &kind, db->pool));
err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
&conflicted, NULL, NULL, NULL, NULL, NULL, NULL,
eb->db, db->local_abspath, db->pool, db->pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
svn_error_clear(err);
wc_kind = svn_node_unknown;
status = svn_wc__db_status_normal;
conflicted = FALSE;
versioned_locally_and_present = FALSE;
}
else if (wc_kind == svn_node_dir
&& status == svn_wc__db_status_normal)
{
/* !! We found the root of a separate working copy obstructing the wc !!
If the directory would be part of our own working copy then
we wouldn't have been called as an add_directory().
The only thing we can do is add a not-present node, to allow
a future update to bring in the new files when the problem is
resolved. Note that svn_wc__db_base_add_not_present_node()
explicitly adds the node into the parent's node database. */
SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, db->local_abspath,
db->new_relpath,
eb->repos_root,
eb->repos_uuid,
*eb->target_revision,
svn_node_file,
NULL, NULL,
pool));
SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
db->skip_this = TRUE;
db->already_notified = TRUE;
do_notification(eb, db->local_abspath, svn_node_dir,
svn_wc_notify_update_skip_obstruction, pool);
return SVN_NO_ERROR;
}
else if (status == svn_wc__db_status_normal
&& (wc_kind == svn_node_file
|| wc_kind == svn_node_symlink))
{
/* We found a file external occupating the place we need in BASE.
We can't add a not-present node in this case as that would overwrite
the file external. Luckily the file external itself stops us from
forgetting a child of this parent directory like an obstructing
working copy would.
The reason we get here is that the adm crawler doesn't report
file externals.
*/
SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
db->skip_this = TRUE;
db->already_notified = TRUE;
do_notification(eb, db->local_abspath, svn_node_file,
svn_wc_notify_update_skip_obstruction, pool);
return SVN_NO_ERROR;
}
else if (wc_kind == svn_node_unknown)
versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */
else
versioned_locally_and_present = IS_NODE_PRESENT(status);
/* Is this path a conflict victim? */
if (conflicted)
{
if (pb->deletion_conflicts)
tree_conflict = svn_hash_gets(pb->deletion_conflicts, db->name);
if (tree_conflict)
{
svn_wc_conflict_reason_t reason;
+ const char *move_src_op_root_abspath;
/* So this deletion wasn't just a deletion, it is actually a
replacement. Let's install a better tree conflict. */
- /* ### Should store the conflict in DB to allow reinstalling
- ### with theoretically more data in close_directory() */
-
- SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
+ SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL,
+ &move_src_op_root_abspath,
eb->db,
db->local_abspath,
tree_conflict,
db->pool, db->pool));
tree_conflict = svn_wc__conflict_skel_create(db->pool);
SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
tree_conflict,
eb->db, db->local_abspath,
reason, svn_wc_conflict_action_replace,
- NULL,
+ move_src_op_root_abspath,
db->pool, db->pool));
/* And now stop checking for conflicts here and just perform
a shadowed update */
db->edit_conflict = tree_conflict; /* Cache for close_directory */
tree_conflict = NULL; /* No direct notification */
db->shadowed = TRUE; /* Just continue */
conflicted = FALSE; /* No skip */
}
else
SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
eb->db, db->local_abspath, pool));
}
/* Now the "usual" behaviour if already conflicted. Skip it. */
if (conflicted)
{
/* Record this conflict so that its descendants are skipped silently. */
SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
db->skip_this = TRUE;
db->already_notified = TRUE;
/* We skip this node, but once the update completes the parent node will
be updated to the new revision. So a future recursive update of the
parent will not bring in this new node as the revision of the parent
describes to the repository that all children are available.
To resolve this problem, we add a not-present node to allow bringing
the node in once this conflict is resolved.
Note that we can safely assume that no present base node exists,
because then we would not have received an add_directory.
*/
SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, db->local_abspath,
db->new_relpath,
eb->repos_root,
eb->repos_uuid,
*eb->target_revision,
svn_node_dir,
NULL, NULL,
pool));
/* ### TODO: Also print victim_path in the skip msg. */
do_notification(eb, db->local_abspath, svn_node_dir,
svn_wc_notify_skip_conflicted, pool);
return SVN_NO_ERROR;
}
else if (conflict_ignored)
{
db->shadowed = TRUE;
}
if (db->shadowed)
{
/* Nothing to check; does not and will not exist in working copy */
}
else if (versioned_locally_and_present)
{
/* What to do with a versioned or schedule-add dir:
A dir already added without history is OK. Set add_existed
so that user notification is delayed until after any prop
conflicts have been found.
An existing versioned dir is an error. In the future we may
relax this restriction and simply update such dirs.
A dir added with history is a tree conflict. */
svn_boolean_t local_is_non_dir;
svn_wc__db_status_t add_status = svn_wc__db_status_normal;
/* Is the local add a copy? */
if (status == svn_wc__db_status_added)
SVN_ERR(svn_wc__db_scan_addition(&add_status, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
eb->db, db->local_abspath,
pool, pool));
/* Is there *something* that is not a dir? */
local_is_non_dir = (wc_kind != svn_node_dir
&& status != svn_wc__db_status_deleted);
/* Do tree conflict checking if
* - if there is a local copy.
* - if this is a switch operation
* - the node kinds mismatch
*
* During switch, local adds at the same path as incoming adds get
* "lost" in that switching back to the original will no longer have the
* local add. So switch always alerts the user with a tree conflict. */
if (!eb->adds_as_modification
|| local_is_non_dir
|| add_status != svn_wc__db_status_added)
{
SVN_ERR(check_tree_conflict(&tree_conflict, eb,
db->local_abspath,
status, FALSE, svn_node_none,
svn_wc_conflict_action_add,
pool, pool));
}
if (tree_conflict == NULL)
db->add_existed = TRUE; /* Take over WORKING */
else
db->shadowed = TRUE; /* Only update BASE */
}
else if (kind != svn_node_none)
{
/* There's an unversioned node at this path. */
db->obstruction_found = TRUE;
/* Unversioned, obstructing dirs are handled by prop merge/conflict,
* if unversioned obstructions are allowed. */
if (! (kind == svn_node_dir && eb->allow_unver_obstructions))
{
/* Bring in the node as deleted */ /* ### Obstructed Conflict */
db->shadowed = TRUE;
/* Mark a conflict */
tree_conflict = svn_wc__conflict_skel_create(db->pool);
SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
tree_conflict,
eb->db, db->local_abspath,
svn_wc_conflict_reason_unversioned,
svn_wc_conflict_action_add, NULL,
db->pool, pool));
db->edit_conflict = tree_conflict;
}
}
if (tree_conflict)
SVN_ERR(complete_conflict(tree_conflict, eb, db->local_abspath,
db->old_repos_relpath, db->old_revision,
db->new_relpath,
wc_kind,
svn_node_dir,
db->pool, pool));
SVN_ERR(svn_wc__db_base_add_incomplete_directory(
eb->db, db->local_abspath,
db->new_relpath,
eb->repos_root,
eb->repos_uuid,
*eb->target_revision,
db->ambient_depth,
(db->shadowed && db->obstruction_found),
(! db->shadowed
&& status == svn_wc__db_status_added),
tree_conflict, NULL,
pool));
/* Make sure there is a real directory at LOCAL_ABSPATH, unless we are just
updating the DB */
if (!db->shadowed)
SVN_ERR(svn_wc__ensure_directory(db->local_abspath, pool));
if (tree_conflict != NULL)
{
if (eb->conflict_func)
SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath,
tree_conflict,
NULL /* merge_options */,
eb->conflict_func,
eb->conflict_baton,
eb->cancel_func,
eb->cancel_baton,
pool));
db->already_notified = TRUE;
do_notification(eb, db->local_abspath, svn_node_dir,
svn_wc_notify_tree_conflict, pool);
}
/* If this add was obstructed by dir scheduled for addition without
history let close_directory() handle the notification because there
might be properties to deal with. If PATH was added inside a locally
deleted tree, then suppress notification, a tree conflict was already
issued. */
if (eb->notify_func && !db->already_notified && !db->add_existed)
{
svn_wc_notify_action_t action;
if (db->shadowed)
action = svn_wc_notify_update_shadowed_add;
else if (db->obstruction_found || db->add_existed)
action = svn_wc_notify_exists;
else
action = svn_wc_notify_update_add;
db->already_notified = TRUE;
do_notification(eb, db->local_abspath, svn_node_dir, action, pool);
}
return SVN_NO_ERROR;
}
/* An svn_delta_editor_t function. */
static svn_error_t *
open_directory(const char *path,
void *parent_baton,
svn_revnum_t base_revision,
apr_pool_t *pool,
void **child_baton)
{
struct dir_baton *db, *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
svn_boolean_t have_work;
svn_boolean_t conflicted;
svn_boolean_t conflict_ignored = FALSE;
svn_skel_t *tree_conflict = NULL;
svn_wc__db_status_t status, base_status;
svn_node_kind_t wc_kind;
SVN_ERR(make_dir_baton(&db, path, eb, pb, FALSE, pool));
*child_baton = db;
if (db->skip_this)
return SVN_NO_ERROR;
/* Detect obstructing working copies */
{
svn_boolean_t is_root;
SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, db->local_abspath,
pool));
if (is_root)
{
/* Just skip this node; a future update will handle it */
SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
db->skip_this = TRUE;
db->already_notified = TRUE;
do_notification(eb, db->local_abspath, svn_node_dir,
svn_wc_notify_update_skip_obstruction, pool);
return SVN_NO_ERROR;
}
}
/* We should have a write lock on every directory touched. */
SVN_ERR(svn_wc__write_check(eb->db, db->local_abspath, pool));
SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &db->old_revision,
&db->old_repos_relpath, NULL, NULL,
&db->changed_rev, &db->changed_date,
&db->changed_author, &db->ambient_depth,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
&conflicted, NULL, NULL, NULL,
NULL, NULL, &have_work,
eb->db, db->local_abspath,
db->pool, pool));
if (!have_work)
base_status = status;
else
SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db->old_revision,
&db->old_repos_relpath, NULL, NULL,
&db->changed_rev, &db->changed_date,
&db->changed_author, &db->ambient_depth,
NULL, NULL, NULL, NULL, NULL, NULL,
eb->db, db->local_abspath,
db->pool, pool));
db->was_incomplete = (base_status == svn_wc__db_status_incomplete);
/* Is this path a conflict victim? */
if (db->shadowed)
conflicted = FALSE; /* Conflict applies to WORKING */
else if (conflicted)
SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
eb->db, db->local_abspath, pool));
if (conflicted)
{
SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
db->skip_this = TRUE;
db->already_notified = TRUE;
do_notification(eb, db->local_abspath, svn_node_unknown,
svn_wc_notify_skip_conflicted, pool);
return SVN_NO_ERROR;
}
else if (conflict_ignored)
{
db->shadowed = TRUE;
}
/* Is this path a fresh tree conflict victim? If so, skip the tree
with one notification. */
/* Check for conflicts only when we haven't already recorded
* a tree-conflict on a parent node. */
if (!db->shadowed)
SVN_ERR(check_tree_conflict(&tree_conflict, eb, db->local_abspath,
status, TRUE, svn_node_dir,
svn_wc_conflict_action_edit,
db->pool, pool));
/* Remember the roots of any locally deleted trees. */
if (tree_conflict != NULL)
{
svn_wc_conflict_reason_t reason;
db->edit_conflict = tree_conflict;
/* Other modifications wouldn't be a tree conflict */
SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
eb->db, db->local_abspath,
tree_conflict,
db->pool, db->pool));
SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted
|| reason == svn_wc_conflict_reason_moved_away
|| reason == svn_wc_conflict_reason_replaced
|| reason == svn_wc_conflict_reason_obstructed);
/* Continue updating BASE */
if (reason == svn_wc_conflict_reason_obstructed)
db->edit_obstructed = TRUE;
else
db->shadowed = TRUE;
}
/* Mark directory as being at target_revision and URL, but incomplete. */
SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db, db->local_abspath,
db->new_relpath,
*eb->target_revision,
pool));
return SVN_NO_ERROR;
}
/* An svn_delta_editor_t function. */
static svn_error_t *
change_dir_prop(void *dir_baton,
const char *name,
const svn_string_t *value,
apr_pool_t *pool)
{
svn_prop_t *propchange;
struct dir_baton *db = dir_baton;
if (db->skip_this)
return SVN_NO_ERROR;
propchange = apr_array_push(db->propchanges);
propchange->name = apr_pstrdup(db->pool, name);
propchange->value = value ? svn_string_dup(value, db->pool) : NULL;
if (!db->edited && svn_property_kind2(name) == svn_prop_regular_kind)
SVN_ERR(mark_directory_edited(db, pool));
return SVN_NO_ERROR;
}
/* If any of the svn_prop_t objects in PROPCHANGES represents a change
to the SVN_PROP_EXTERNALS property, return that change, else return
null. If PROPCHANGES contains more than one such change, return
the first. */
static const svn_prop_t *
externals_prop_changed(const apr_array_header_t *propchanges)
{
int i;
for (i = 0; i < propchanges->nelts; i++)
{
const svn_prop_t *p = &(APR_ARRAY_IDX(propchanges, i, svn_prop_t));
if (strcmp(p->name, SVN_PROP_EXTERNALS) == 0)
return p;
}
return NULL;
}
/* An svn_delta_editor_t function. */
static svn_error_t *
close_directory(void *dir_baton,
apr_pool_t *pool)
{
struct dir_baton *db = dir_baton;
struct edit_baton *eb = db->edit_baton;
svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
apr_array_header_t *entry_prop_changes;
apr_array_header_t *dav_prop_changes;
apr_array_header_t *regular_prop_changes;
apr_hash_t *base_props;
apr_hash_t *actual_props;
apr_hash_t *new_base_props = NULL;
apr_hash_t *new_actual_props = NULL;
svn_revnum_t new_changed_rev = SVN_INVALID_REVNUM;
apr_time_t new_changed_date = 0;
const char *new_changed_author = NULL;
apr_pool_t *scratch_pool = db->pool;
svn_skel_t *all_work_items = NULL;
svn_skel_t *conflict_skel = NULL;
/* Skip if we're in a conflicted tree. */
if (db->skip_this)
{
/* Allow the parent to complete its update. */
SVN_ERR(maybe_release_dir_info(db));
return SVN_NO_ERROR;
}
if (db->edited)
conflict_skel = db->edit_conflict;
SVN_ERR(svn_categorize_props(db->propchanges, &entry_prop_changes,
&dav_prop_changes, &regular_prop_changes, pool));
/* Fetch the existing properties. */
if ((!db->adding_dir || db->add_existed)
&& !db->shadowed)
{
SVN_ERR(svn_wc__get_actual_props(&actual_props,
eb->db, db->local_abspath,
scratch_pool, scratch_pool));
}
else
actual_props = apr_hash_make(pool);
if (db->add_existed)
{
/* This node already exists. Grab the current pristine properties. */
SVN_ERR(svn_wc__db_read_pristine_props(&base_props,
eb->db, db->local_abspath,
scratch_pool, scratch_pool));
}
else if (!db->adding_dir)
{
/* Get the BASE properties for proper merging. */
SVN_ERR(svn_wc__db_base_get_props(&base_props,
eb->db, db->local_abspath,
scratch_pool, scratch_pool));
}
else
base_props = apr_hash_make(pool);
/* An incomplete directory might have props which were supposed to be
deleted but weren't. Because the server sent us all the props we're
supposed to have, any previous base props not in this list must be
deleted (issue #1672). */
if (db->was_incomplete)
{
int i;
apr_hash_t *props_to_delete;
apr_hash_index_t *hi;
/* In a copy of the BASE props, remove every property that we see an
incoming change for. The remaining unmentioned properties are those
which need to be deleted. */
props_to_delete = apr_hash_copy(pool, base_props);
for (i = 0; i < regular_prop_changes->nelts; i++)
{
const svn_prop_t *prop;
prop = &APR_ARRAY_IDX(regular_prop_changes, i, svn_prop_t);
svn_hash_sets(props_to_delete, prop->name, NULL);
}
/* Add these props to the incoming propchanges (in
* regular_prop_changes). */
for (hi = apr_hash_first(pool, props_to_delete);
hi != NULL;
hi = apr_hash_next(hi))
{
const char *propname = svn__apr_hash_index_key(hi);
svn_prop_t *prop = apr_array_push(regular_prop_changes);
/* Record a deletion for PROPNAME. */
prop->name = propname;
prop->value = NULL;
}
}
/* If this directory has property changes stored up, now is the time
to deal with them. */
if (regular_prop_changes->nelts)
{
/* If recording traversal info, then see if the
SVN_PROP_EXTERNALS property on this directory changed,
and record before and after for the change. */
if (eb->external_func)
{
const svn_prop_t *change
= externals_prop_changed(regular_prop_changes);
if (change)
{
const svn_string_t *new_val_s = change->value;
const svn_string_t *old_val_s;
old_val_s = svn_hash_gets(base_props, SVN_PROP_EXTERNALS);
if ((new_val_s == NULL) && (old_val_s == NULL))
; /* No value before, no value after... so do nothing. */
else if (new_val_s && old_val_s
&& (svn_string_compare(old_val_s, new_val_s)))
; /* Value did not change... so do nothing. */
else if (old_val_s || new_val_s)
/* something changed, record the change */
{
SVN_ERR((eb->external_func)(
eb->external_baton,
db->local_abspath,
old_val_s,
new_val_s,
db->ambient_depth,
db->pool));
}
}
}
if (db->shadowed)
{
/* We don't have a relevant actual row, but we need actual properties
to allow property merging without conflicts. */
if (db->adding_dir)
actual_props = apr_hash_make(scratch_pool);
else
actual_props = base_props;
}
/* Merge pending properties. */
new_base_props = svn_prop__patch(base_props, regular_prop_changes,
db->pool);
SVN_ERR_W(svn_wc__merge_props(&conflict_skel,
&prop_state,
&new_actual_props,
eb->db,
db->local_abspath,
NULL /* use baseprops */,
base_props,
actual_props,
regular_prop_changes,
db->pool,
scratch_pool),
_("Couldn't do property merge"));
/* After a (not-dry-run) merge, we ALWAYS have props to save. */
SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL);
}
SVN_ERR(accumulate_last_change(&new_changed_rev, &new_changed_date,
&new_changed_author, entry_prop_changes,
scratch_pool, scratch_pool));
/* Check if we should add some not-present markers before marking the
directory complete (Issue #3569) */
{
apr_hash_t *new_children = svn_hash_gets(eb->dir_dirents, db->new_relpath);
if (new_children != NULL)
{
apr_hash_index_t *hi;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
for (hi = apr_hash_first(scratch_pool, new_children);
hi;
hi = apr_hash_next(hi))
{
const char *child_name;
const char *child_abspath;
const char *child_relpath;
const svn_dirent_t *dirent;
svn_wc__db_status_t status;
svn_node_kind_t child_kind;
svn_error_t *err;
svn_pool_clear(iterpool);
child_name = svn__apr_hash_index_key(hi);
child_abspath = svn_dirent_join(db->local_abspath, child_name,
iterpool);
dirent = svn__apr_hash_index_val(hi);
child_kind = (dirent->kind == svn_node_dir)
? svn_node_dir
: svn_node_file;
if (db->ambient_depth < svn_depth_immediates
&& child_kind == svn_node_dir)
continue; /* We don't need the subdirs */
/* ### We just check if there is some node in BASE at this path */
err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
eb->db, child_abspath,
iterpool, iterpool);
if (!err)
{
svn_boolean_t is_wcroot;
SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, eb->db, child_abspath,
iterpool));
if (!is_wcroot)
continue; /* Everything ok... Nothing to do here */
/* Fall through to allow recovering later */
}
else if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
svn_error_clear(err);
child_relpath = svn_relpath_join(db->new_relpath, child_name,
iterpool);
SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db,
child_abspath,
child_relpath,
eb->repos_root,
eb->repos_uuid,
*eb->target_revision,
child_kind,
NULL, NULL,
iterpool));
}
svn_pool_destroy(iterpool);
}
}
if (apr_hash_count(db->not_present_files))
{
apr_hash_index_t *hi;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
/* This should call some new function (which could also be used
for new_children above) to add all the names in single
transaction, but I can't even trigger it. I've tried
ra_local, ra_svn, ra_neon, ra_serf and they all call
close_file before close_dir. */
for (hi = apr_hash_first(scratch_pool, db->not_present_files);
hi;
hi = apr_hash_next(hi))
{
const char *child = svn__apr_hash_index_key(hi);
const char *child_abspath, *child_relpath;
svn_pool_clear(iterpool);
child_abspath = svn_dirent_join(db->local_abspath, child, iterpool);
child_relpath = svn_dirent_join(db->new_relpath, child, iterpool);
SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db,
child_abspath,
child_relpath,
eb->repos_root,
eb->repos_uuid,
*eb->target_revision,
svn_node_file,
NULL, NULL,
iterpool));
}
svn_pool_destroy(iterpool);
}
/* If this directory is merely an anchor for a targeted child, then we
should not be updating the node at all. */
if (db->parent_baton == NULL
&& *eb->target_basename != '\0')
{
/* And we should not have received any changes! */
SVN_ERR_ASSERT(db->propchanges->nelts == 0);
/* ... which also implies NEW_CHANGED_* are not set,
and NEW_BASE_PROPS == NULL. */
}
else
{
apr_hash_t *props;
apr_array_header_t *iprops = NULL;
/* ### we know a base node already exists. it was created in
### open_directory or add_directory. let's just preserve the
### existing DEPTH value, and possibly CHANGED_*. */
/* If we received any changed_* values, then use them. */
if (SVN_IS_VALID_REVNUM(new_changed_rev))
db->changed_rev = new_changed_rev;
if (new_changed_date != 0)
db->changed_date = new_changed_date;
if (new_changed_author != NULL)
db->changed_author = new_changed_author;
/* If no depth is set yet, set to infinity. */
if (db->ambient_depth == svn_depth_unknown)
db->ambient_depth = svn_depth_infinity;
if (eb->depth_is_sticky
&& db->ambient_depth != eb->requested_depth)
{
/* After a depth upgrade the entry must reflect the new depth.
Upgrading to infinity changes the depth of *all* directories,
upgrading to something else only changes the target. */
if (eb->requested_depth == svn_depth_infinity
|| (strcmp(db->local_abspath, eb->target_abspath) == 0
&& eb->requested_depth > db->ambient_depth))
{
db->ambient_depth = eb->requested_depth;
}
}
/* Do we have new properties to install? Or shall we simply retain
the prior set of properties? If we're installing new properties,
then we also want to write them to an old-style props file. */
props = new_base_props;
if (props == NULL)
props = base_props;
if (conflict_skel)
{
svn_skel_t *work_item;
SVN_ERR(complete_conflict(conflict_skel,
db->edit_baton,
db->local_abspath,
db->old_repos_relpath,
db->old_revision,
db->new_relpath,
svn_node_dir, svn_node_dir,
db->pool, scratch_pool));
SVN_ERR(svn_wc__conflict_create_markers(&work_item,
eb->db, db->local_abspath,
conflict_skel,
scratch_pool, scratch_pool));
all_work_items = svn_wc__wq_merge(all_work_items, work_item,
scratch_pool);
}
/* Any inherited props to be set set for this base node? */
if (eb->wcroot_iprops)
{
iprops = svn_hash_gets(eb->wcroot_iprops, db->local_abspath);
/* close_edit may also update iprops for switched nodes, catching
those for which close_directory is never called (e.g. a switch
with no changes). So as a minor optimization we remove any
iprops from the hash so as not to set them again in
close_edit. */
if (iprops)
svn_hash_sets(eb->wcroot_iprops, db->local_abspath, NULL);
}
/* Update the BASE data for the directory and mark the directory
complete */
SVN_ERR(svn_wc__db_base_add_directory(
eb->db, db->local_abspath,
eb->wcroot_abspath,
db->new_relpath,
eb->repos_root, eb->repos_uuid,
*eb->target_revision,
props,
db->changed_rev, db->changed_date, db->changed_author,
NULL /* children */,
db->ambient_depth,
(dav_prop_changes->nelts > 0)
? svn_prop_array_to_hash(dav_prop_changes, pool)
: NULL,
conflict_skel,
(! db->shadowed) && new_base_props != NULL,
new_actual_props,
iprops, all_work_items,
scratch_pool));
}
/* Process all of the queued work items for this directory. */
SVN_ERR(svn_wc__wq_run(eb->db, db->local_abspath,
eb->cancel_func, eb->cancel_baton,
scratch_pool));
if (conflict_skel && eb->conflict_func)
SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath,
conflict_skel,
NULL /* merge_options */,
eb->conflict_func,
eb->conflict_baton,
eb->cancel_func,
eb->cancel_baton,
scratch_pool));
/* Notify of any prop changes on this directory -- but do nothing if
it's an added or skipped directory, because notification has already
happened in that case - unless the add was obstructed by a dir
scheduled for addition without history, in which case we handle
notification here). */
if (!db->already_notified && eb->notify_func && db->edited)
{
svn_wc_notify_t *notify;
svn_wc_notify_action_t action;
if (db->shadowed || db->edit_obstructed)
action = svn_wc_notify_update_shadowed_update;
else if (db->obstruction_found || db->add_existed)
action = svn_wc_notify_exists;
else
action = svn_wc_notify_update_update;
notify = svn_wc_create_notify(db->local_abspath, action, pool);
notify->kind = svn_node_dir;
notify->prop_state = prop_state;
notify->revision = *eb->target_revision;
notify->old_revision = db->old_revision;
eb->notify_func(eb->notify_baton, notify, scratch_pool);
}
/* We're done with this directory, so remove one reference from the
bump information. */
SVN_ERR(maybe_release_dir_info(db));
return SVN_NO_ERROR;
}
/* Common code for 'absent_file' and 'absent_directory'. */
static svn_error_t *
absent_node(const char *path,
svn_node_kind_t absent_kind,
void *parent_baton,
apr_pool_t *pool)
{
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
apr_pool_t *scratch_pool = svn_pool_create(pool);
const char *name = svn_dirent_basename(path, NULL);
const char *local_abspath;
svn_error_t *err;
svn_wc__db_status_t status;
svn_node_kind_t kind;
if (pb->skip_this)
return SVN_NO_ERROR;
SVN_ERR(mark_directory_edited(pb, scratch_pool));
local_abspath = svn_dirent_join(pb->local_abspath, name, scratch_pool);
/* If an item by this name is scheduled for addition that's a
genuine tree-conflict. */
err = svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
eb->db, local_abspath,
scratch_pool, scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
svn_error_clear(err);
status = svn_wc__db_status_not_present;
kind = svn_node_unknown;
}
if (status == svn_wc__db_status_normal)
{
svn_boolean_t wcroot;
/* We found an obstructing working copy or a file external! */
SVN_ERR(svn_wc__db_is_wcroot(&wcroot, eb->db, local_abspath,
scratch_pool));
if (wcroot)
{
/*
We have an obstructing working copy; possibly a directory external
We can do two things now:
1) notify the user, record a skip, etc.
2) Just record the absent node in BASE in the parent
working copy.
As option 2 happens to be exactly what we do anyway, fall through.
*/
}
else
{
/* The server asks us to replace a file external
(Existing BASE node; not reported by the working copy crawler or
there would have been a delete_entry() call.
There is no way we can store this state in the working copy as
the BASE layer is already filled.
We could error out, but that is not helping anybody; the user is not
even seeing with what the file external would be replaced, so let's
report a skip and continue the update.
*/
if (eb->notify_func)
{
svn_wc_notify_t *notify;
notify = svn_wc_create_notify(
local_abspath,
svn_wc_notify_update_skip_obstruction,
scratch_pool);
eb->notify_func(eb->notify_baton, notify, scratch_pool);
}
svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
}
}
else if (status == svn_wc__db_status_not_present
|| status == svn_wc__db_status_server_excluded
|| status == svn_wc__db_status_excluded)
{
/* The BASE node is not actually there, so we can safely turn it into
an absent node */
}
else
{
/* We have a local addition. If this would be a BASE node it would have
been deleted before we get here. (Which might have turned it into
a copy).
### This should be recorded as a tree conflict and the update
### can just continue, as we can just record the absent status
### in BASE.
*/
SVN_ERR_ASSERT(status != svn_wc__db_status_normal);
return svn_error_createf(
SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
_("Failed to mark '%s' absent: item of the same name is already "
"scheduled for addition"),
svn_dirent_local_style(local_abspath, pool));
}
{
const char *repos_relpath;
repos_relpath = svn_relpath_join(pb->new_relpath, name, scratch_pool);
/* Insert an excluded node below the parent node to note that this child
is absent. (This puts it in the parent db if the child is obstructed) */
SVN_ERR(svn_wc__db_base_add_excluded_node(eb->db, local_abspath,
repos_relpath, eb->repos_root,
eb->repos_uuid,
*(eb->target_revision),
absent_kind,
svn_wc__db_status_server_excluded,
NULL, NULL,
scratch_pool));
}
svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
}
/* An svn_delta_editor_t function. */
static svn_error_t *
absent_file(const char *path,
void *parent_baton,
apr_pool_t *pool)
{
return absent_node(path, svn_node_file, parent_baton, pool);
}
/* An svn_delta_editor_t function. */
static svn_error_t *
absent_directory(const char *path,
void *parent_baton,
apr_pool_t *pool)
{
return absent_node(path, svn_node_dir, parent_baton, pool);
}
/* An svn_delta_editor_t function. */
static svn_error_t *
add_file(const char *path,
void *parent_baton,
const char *copyfrom_path,
svn_revnum_t copyfrom_rev,
apr_pool_t *pool,
void **file_baton)
{
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
struct file_baton *fb;
svn_node_kind_t kind = svn_node_none;
svn_node_kind_t wc_kind = svn_node_unknown;
svn_wc__db_status_t status = svn_wc__db_status_normal;
apr_pool_t *scratch_pool;
svn_boolean_t conflicted = FALSE;
svn_boolean_t conflict_ignored = FALSE;
svn_boolean_t versioned_locally_and_present = FALSE;
svn_skel_t *tree_conflict = NULL;
svn_error_t *err = SVN_NO_ERROR;
SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev)));
SVN_ERR(make_file_baton(&fb, pb, path, TRUE, pool));
*file_baton = fb;
if (fb->skip_this)
return SVN_NO_ERROR;
SVN_ERR(mark_file_edited(fb, pool));
/* The file_pool can stick around for a *long* time, so we want to
use a subpool for any temporary allocations. */
scratch_pool = svn_pool_create(pool);
/* It may not be named the same as the administrative directory. */
if (svn_wc_is_adm_dir(fb->name, pool))
return svn_error_createf(
SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
_("Failed to add file '%s': object of the same name as the "
"administrative directory"),
svn_dirent_local_style(fb->local_abspath, pool));
if (!eb->clean_checkout)
{
SVN_ERR(svn_io_check_path(fb->local_abspath, &kind, scratch_pool));
err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
&conflicted, NULL, NULL, NULL, NULL, NULL, NULL,
eb->db, fb->local_abspath,
scratch_pool, scratch_pool);
}
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
svn_error_clear(err);
wc_kind = svn_node_unknown;
conflicted = FALSE;
versioned_locally_and_present = FALSE;
}
else if (wc_kind == svn_node_dir
&& status == svn_wc__db_status_normal)
{
/* !! We found the root of a separate working copy obstructing the wc !!
If the directory would be part of our own working copy then
we wouldn't have been called as an add_file().
The only thing we can do is add a not-present node, to allow
a future update to bring in the new files when the problem is
resolved. */
svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
(void *)1);
SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
fb->skip_this = TRUE;
fb->already_notified = TRUE;
do_notification(eb, fb->local_abspath, svn_node_file,
svn_wc_notify_update_skip_obstruction, scratch_pool);
svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
}
else if (status == svn_wc__db_status_normal
&& (wc_kind == svn_node_file
|| wc_kind == svn_node_symlink))
{
/* We found a file external occupating the place we need in BASE.
We can't add a not-present node in this case as that would overwrite
the file external. Luckily the file external itself stops us from
forgetting a child of this parent directory like an obstructing
working copy would.
The reason we get here is that the adm crawler doesn't report
file externals.
*/
SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
fb->skip_this = TRUE;
fb->already_notified = TRUE;
do_notification(eb, fb->local_abspath, svn_node_file,
svn_wc_notify_update_skip_obstruction, scratch_pool);
svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
}
else if (wc_kind == svn_node_unknown)
versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */
else
versioned_locally_and_present = IS_NODE_PRESENT(status);
/* Is this path a conflict victim? */
if (fb->shadowed)
conflicted = FALSE; /* Conflict applies to WORKING */
else if (conflicted)
{
if (pb->deletion_conflicts)
tree_conflict = svn_hash_gets(pb->deletion_conflicts, fb->name);
if (tree_conflict)
{
svn_wc_conflict_reason_t reason;
+ const char *move_src_op_root_abspath;
/* So this deletion wasn't just a deletion, it is actually a
replacement. Let's install a better tree conflict. */
- /* ### Should store the conflict in DB to allow reinstalling
- ### with theoretically more data in close_directory() */
-
- SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
+ SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL,
+ &move_src_op_root_abspath,
eb->db,
fb->local_abspath,
tree_conflict,
fb->pool, fb->pool));
tree_conflict = svn_wc__conflict_skel_create(fb->pool);
SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
tree_conflict,
eb->db, fb->local_abspath,
reason, svn_wc_conflict_action_replace,
- NULL,
+ move_src_op_root_abspath,
fb->pool, fb->pool));
/* And now stop checking for conflicts here and just perform
a shadowed update */
fb->edit_conflict = tree_conflict; /* Cache for close_file */
tree_conflict = NULL; /* No direct notification */
fb->shadowed = TRUE; /* Just continue */
conflicted = FALSE; /* No skip */
}
else
SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
eb->db, fb->local_abspath, pool));
}
/* Now the usual conflict handling: skip. */
if (conflicted)
{
SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
fb->skip_this = TRUE;
fb->already_notified = TRUE;
/* We skip this node, but once the update completes the parent node will
be updated to the new revision. So a future recursive update of the
parent will not bring in this new node as the revision of the parent
describes to the repository that all children are available.
To resolve this problem, we add a not-present node to allow bringing
the node in once this conflict is resolved.
Note that we can safely assume that no present base node exists,
because then we would not have received an add_file.
*/
svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
(void *)1);
do_notification(eb, fb->local_abspath, svn_node_unknown,
svn_wc_notify_skip_conflicted, scratch_pool);
svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
}
else if (conflict_ignored)
{
fb->shadowed = TRUE;
}
if (fb->shadowed)
{
/* Nothing to check; does not and will not exist in working copy */
}
else if (versioned_locally_and_present)
{
/* What to do with a versioned or schedule-add file:
If the UUID doesn't match the parent's, or the URL isn't a child of
the parent dir's URL, it's an error.
Set add_existed so that user notification is delayed until after any
text or prop conflicts have been found.
Whether the incoming add is a symlink or a file will only be known in
close_file(), when the props are known. So with a locally added file
or symlink, let close_file() check for a tree conflict.
We will never see missing files here, because these would be
re-added during the crawler phase. */
svn_boolean_t local_is_file;
/* Is the local node a copy or move */
if (status == svn_wc__db_status_added)
SVN_ERR(svn_wc__db_scan_addition(&status, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL,
eb->db, fb->local_abspath,
scratch_pool, scratch_pool));
/* Is there something that is a file? */
local_is_file = (wc_kind == svn_node_file
|| wc_kind == svn_node_symlink);
/* Do tree conflict checking if
* - if there is a local copy.
* - if this is a switch operation
* - the node kinds mismatch
*
* During switch, local adds at the same path as incoming adds get
* "lost" in that switching back to the original will no longer have the
* local add. So switch always alerts the user with a tree conflict. */
if (!eb->adds_as_modification
|| !local_is_file
|| status != svn_wc__db_status_added)
{
SVN_ERR(check_tree_conflict(&tree_conflict, eb,
fb->local_abspath,
status, FALSE, svn_node_none,
svn_wc_conflict_action_add,
scratch_pool, scratch_pool));
}
if (tree_conflict == NULL)
fb->add_existed = TRUE; /* Take over WORKING */
else
fb->shadowed = TRUE; /* Only update BASE */
}
else if (kind != svn_node_none)
{
/* There's an unversioned node at this path. */
fb->obstruction_found = TRUE;
/* Unversioned, obstructing files are handled by text merge/conflict,
* if unversioned obstructions are allowed. */
if (! (kind == svn_node_file && eb->allow_unver_obstructions))
{
/* Bring in the node as deleted */ /* ### Obstructed Conflict */
fb->shadowed = TRUE;
/* Mark a conflict */
tree_conflict = svn_wc__conflict_skel_create(fb->pool);
SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
tree_conflict,
eb->db, fb->local_abspath,
svn_wc_conflict_reason_unversioned,
svn_wc_conflict_action_add,
NULL,
fb->pool, scratch_pool));
}
}
/* When this is not the update target add a not-present BASE node now,
to allow marking the parent directory complete in its close_edit() call.
This resolves issues when that occurs before the close_file(). */
if (pb->parent_baton
|| *eb->target_basename == '\0'
|| (strcmp(fb->local_abspath, eb->target_abspath) != 0))
{
svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
(void *)1);
}
if (tree_conflict != NULL)
{
SVN_ERR(complete_conflict(tree_conflict,
fb->edit_baton,
fb->local_abspath,
fb->old_repos_relpath,
fb->old_revision,
fb->new_relpath,
wc_kind,
svn_node_file,
fb->pool, scratch_pool));
SVN_ERR(svn_wc__db_op_mark_conflict(eb->db,
fb->local_abspath,
tree_conflict, NULL,
scratch_pool));
if (eb->conflict_func)
SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath,
tree_conflict,
NULL /* merge_options */,
eb->conflict_func,
eb->conflict_baton,
eb->cancel_func,
eb->cancel_baton,
scratch_pool));
fb->already_notified = TRUE;
do_notification(eb, fb->local_abspath, svn_node_file,
svn_wc_notify_tree_conflict, scratch_pool);
}
svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
}
/* An svn_delta_editor_t function. */
static svn_error_t *
open_file(const char *path,
void *parent_baton,
svn_revnum_t base_revision,
apr_pool_t *pool,
void **file_baton)
{
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
struct file_baton *fb;
svn_boolean_t conflicted;
svn_boolean_t conflict_ignored = FALSE;
svn_boolean_t have_work;
svn_wc__db_status_t status;
svn_node_kind_t wc_kind;
svn_skel_t *tree_conflict = NULL;
/* the file_pool can stick around for a *long* time, so we want to use
a subpool for any temporary allocations. */
apr_pool_t *scratch_pool = svn_pool_create(pool);
SVN_ERR(make_file_baton(&fb, pb, path, FALSE, pool));
*file_baton = fb;
if (fb->skip_this)
return SVN_NO_ERROR;
/* Detect obstructing working copies */
{
svn_boolean_t is_root;
SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, fb->local_abspath,
pool));
if (is_root)
{
/* Just skip this node; a future update will handle it */
SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
fb->skip_this = TRUE;
fb->already_notified = TRUE;
do_notification(eb, fb->local_abspath, svn_node_file,
svn_wc_notify_update_skip_obstruction, pool);
return SVN_NO_ERROR;
}
}
/* Sanity check. */
/* If replacing, make sure the .svn entry already exists. */
SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &fb->old_revision,
&fb->old_repos_relpath, NULL, NULL,
&fb->changed_rev, &fb->changed_date,
&fb->changed_author, NULL,
&fb->original_checksum, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
&conflicted, NULL, NULL, &fb->local_prop_mods,
NULL, NULL, &have_work,
eb->db, fb->local_abspath,
fb->pool, scratch_pool));
if (have_work)
SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &fb->old_revision,
&fb->old_repos_relpath, NULL, NULL,
&fb->changed_rev, &fb->changed_date,
&fb->changed_author, NULL,
&fb->original_checksum, NULL, NULL,
NULL, NULL, NULL,
eb->db, fb->local_abspath,
fb->pool, scratch_pool));
/* Is this path a conflict victim? */
if (fb->shadowed)
conflicted = FALSE; /* Conflict applies to WORKING */
else if (conflicted)
SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
eb->db, fb->local_abspath, pool));
if (conflicted)
{
SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
fb->skip_this = TRUE;
fb->already_notified = TRUE;
do_notification(eb, fb->local_abspath, svn_node_unknown,
svn_wc_notify_skip_conflicted, scratch_pool);
svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
}
else if (conflict_ignored)
{
fb->shadowed = TRUE;
}
/* Check for conflicts only when we haven't already recorded
* a tree-conflict on a parent node. */
if (!fb->shadowed)
SVN_ERR(check_tree_conflict(&tree_conflict, eb, fb->local_abspath,
status, TRUE, svn_node_file,
svn_wc_conflict_action_edit,
fb->pool, scratch_pool));
/* Is this path the victim of a newly-discovered tree conflict? */
if (tree_conflict != NULL)
{
svn_wc_conflict_reason_t reason;
fb->edit_conflict = tree_conflict;
/* Other modifications wouldn't be a tree conflict */
SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
eb->db, fb->local_abspath,
tree_conflict,
scratch_pool, scratch_pool));
SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted
|| reason == svn_wc_conflict_reason_moved_away
|| reason == svn_wc_conflict_reason_replaced
|| reason == svn_wc_conflict_reason_obstructed);
/* Continue updating BASE */
if (reason == svn_wc_conflict_reason_obstructed)
fb->edit_obstructed = TRUE;
else
fb->shadowed = TRUE;
}
svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
}
/* Implements svn_stream_lazyopen_func_t. */
static svn_error_t *
lazy_open_source(svn_stream_t **stream,
void *baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
struct file_baton *fb = baton;
SVN_ERR(svn_wc__db_pristine_read(stream, NULL, fb->edit_baton->db,
fb->local_abspath,
fb->original_checksum,
result_pool, scratch_pool));
return SVN_NO_ERROR;
}
struct lazy_target_baton {
struct file_baton *fb;
struct handler_baton *hb;
struct edit_baton *eb;
};
/* Implements svn_stream_lazyopen_func_t. */
static svn_error_t *
lazy_open_target(svn_stream_t **stream,
void *baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
struct lazy_target_baton *tb = baton;
SVN_ERR(svn_wc__open_writable_base(stream, &tb->hb->new_text_base_tmp_abspath,
NULL, &tb->hb->new_text_base_sha1_checksum,
tb->fb->edit_baton->db,
tb->eb->wcroot_abspath,
result_pool, scratch_pool));
return SVN_NO_ERROR;
}
/* An svn_delta_editor_t function. */
static svn_error_t *
apply_textdelta(void *file_baton,
const char *expected_checksum,
apr_pool_t *pool,
svn_txdelta_window_handler_t *handler,
void **handler_baton)
{
struct file_baton *fb = file_baton;
apr_pool_t *handler_pool = svn_pool_create(fb->pool);
struct handler_baton *hb = apr_pcalloc(handler_pool, sizeof(*hb));
struct edit_baton *eb = fb->edit_baton;
const svn_checksum_t *recorded_base_checksum;
svn_checksum_t *expected_base_checksum;
svn_stream_t *source;
struct lazy_target_baton *tb;
svn_stream_t *target;
if (fb->skip_this)
{
*handler = svn_delta_noop_window_handler;
*handler_baton = NULL;
return SVN_NO_ERROR;
}
SVN_ERR(mark_file_edited(fb, pool));
/* Parse checksum or sets expected_base_checksum to NULL */
SVN_ERR(svn_checksum_parse_hex(&expected_base_checksum, svn_checksum_md5,
expected_checksum, pool));
/* Before applying incoming svndiff data to text base, make sure
text base hasn't been corrupted, and that its checksum
matches the expected base checksum. */
/* The incoming delta is targeted against EXPECTED_BASE_CHECKSUM. Find and
check our RECORDED_BASE_CHECKSUM. (In WC-1, we could not do this test
for replaced nodes because we didn't store the checksum of the "revert
base". In WC-NG, we do and we can.) */
recorded_base_checksum = fb->original_checksum;
/* If we have a checksum that we want to compare to a MD5 checksum,
ensure that it is a MD5 checksum */
if (recorded_base_checksum
&& expected_base_checksum
&& recorded_base_checksum->kind != svn_checksum_md5)
SVN_ERR(svn_wc__db_pristine_get_md5(&recorded_base_checksum,
eb->db, eb->wcroot_abspath,
recorded_base_checksum, pool, pool));
if (!svn_checksum_match(expected_base_checksum, recorded_base_checksum))
return svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL,
_("Checksum mismatch for '%s':\n"
" expected: %s\n"
" recorded: %s\n"),
svn_dirent_local_style(fb->local_abspath, pool),
svn_checksum_to_cstring_display(expected_base_checksum,
pool),
svn_checksum_to_cstring_display(recorded_base_checksum,
pool));
/* Open the text base for reading, unless this is an added file. */
/*
kff todo: what we really need to do here is:
1. See if there's a file or dir by this name already here.
2. See if it's under revision control.
3. If both are true, open text-base.
4. If only 1 is true, bail, because we can't go destroying user's
files (or as an alternative to bailing, move it to some tmp
name and somehow tell the user, but communicating with the
user without erroring is a whole callback system we haven't
finished inventing yet.)
*/
if (! fb->adding_file)
{
SVN_ERR_ASSERT(!fb->original_checksum
|| fb->original_checksum->kind == svn_checksum_sha1);
source = svn_stream_lazyopen_create(lazy_open_source, fb, FALSE,
handler_pool);
}
else
{
source = svn_stream_empty(handler_pool);
}
/* If we don't have a recorded checksum, use the ra provided checksum */
if (!recorded_base_checksum)
recorded_base_checksum = expected_base_checksum;
/* Checksum the text base while applying deltas */
if (recorded_base_checksum)
{
hb->expected_source_checksum = svn_checksum_dup(recorded_base_checksum,
handler_pool);
/* Wrap stream and store reference to allow calculating the
checksum. */
source = svn_stream_checksummed2(source,
&hb->actual_source_checksum,
NULL, recorded_base_checksum->kind,
TRUE, handler_pool);
hb->source_checksum_stream = source;
}
tb = apr_palloc(handler_pool, sizeof(struct lazy_target_baton));
tb->hb = hb;
tb->fb = fb;
tb->eb = eb;
target = svn_stream_lazyopen_create(lazy_open_target, tb, TRUE, handler_pool);
/* Prepare to apply the delta. */
svn_txdelta_apply(source, target,
hb->new_text_base_md5_digest,
hb->new_text_base_tmp_abspath /* error_info */,
handler_pool,
&hb->apply_handler, &hb->apply_baton);
hb->pool = handler_pool;
hb->fb = fb;
/* We're all set. */
*handler_baton = hb;
*handler = window_handler;
return SVN_NO_ERROR;
}
/* An svn_delta_editor_t function. */
static svn_error_t *
change_file_prop(void *file_baton,
const char *name,
const svn_string_t *value,
apr_pool_t *scratch_pool)
{
struct file_baton *fb = file_baton;
svn_prop_t *propchange;
if (fb->skip_this)
return SVN_NO_ERROR;
/* Push a new propchange to the file baton's array of propchanges */
propchange = apr_array_push(fb->propchanges);
propchange->name = apr_pstrdup(fb->pool, name);
propchange->value = value ? svn_string_dup(value, fb->pool) : NULL;
if (!fb->edited && svn_property_kind2(name) == svn_prop_regular_kind)
SVN_ERR(mark_file_edited(fb, scratch_pool));
if (! fb->shadowed
&& strcmp(name, SVN_PROP_SPECIAL) == 0)
{
struct edit_baton *eb = fb->edit_baton;
svn_boolean_t modified = FALSE;
svn_boolean_t becomes_symlink;
svn_boolean_t was_symlink;
/* Let's see if we have a change as in some scenarios servers report
non-changes of properties. */
becomes_symlink = (value != NULL);
if (fb->adding_file)
was_symlink = becomes_symlink; /* No change */
else
{
apr_hash_t *props;
/* We read the server-props, not the ACTUAL props here as we just
want to see if this is really an incoming prop change. */
SVN_ERR(svn_wc__db_base_get_props(&props, eb->db,
fb->local_abspath,
scratch_pool, scratch_pool));
was_symlink = ((props
&& svn_hash_gets(props, SVN_PROP_SPECIAL) != NULL)
? svn_tristate_true
: svn_tristate_false);
}
if (was_symlink != becomes_symlink)
{
/* If the local node was not modified, we continue as usual, if
modified we want a tree conflict just like how we would handle
it when receiving a delete + add (aka "replace") */
if (fb->local_prop_mods)
modified = TRUE;
else
SVN_ERR(svn_wc__internal_file_modified_p(&modified, eb->db,
fb->local_abspath,
FALSE, scratch_pool));
}
if (modified)
{
if (!fb->edit_conflict)
fb->edit_conflict = svn_wc__conflict_skel_create(fb->pool);
SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
fb->edit_conflict,
eb->db, fb->local_abspath,
svn_wc_conflict_reason_edited,
svn_wc_conflict_action_replace,
NULL,
fb->pool, scratch_pool));
SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton,
fb->local_abspath, fb->old_repos_relpath,
fb->old_revision, fb->new_relpath,
svn_node_file, svn_node_file,
fb->pool, scratch_pool));
/* Create a copy of the existing (pre update) BASE node in WORKING,
mark a tree conflict and handle the rest of the update as
shadowed */
SVN_ERR(svn_wc__db_op_make_copy(eb->db, fb->local_abspath,
fb->edit_conflict, NULL,
scratch_pool));
do_notification(eb, fb->local_abspath, svn_node_file,
svn_wc_notify_tree_conflict, scratch_pool);
/* Ok, we introduced a replacement, so we can now handle the rest
as a normal shadowed update */
fb->shadowed = TRUE;
fb->add_existed = FALSE;
fb->already_notified = TRUE;
}
}
return SVN_NO_ERROR;
}
/* Perform the actual merge of file changes between an original file,
identified by ORIGINAL_CHECKSUM (an empty file if NULL) to a new file
identified by NEW_CHECKSUM.
Merge the result into LOCAL_ABSPATH, which is part of the working copy
identified by WRI_ABSPATH. Use OLD_REVISION and TARGET_REVISION for naming
the intermediate files.
The rest of the arguments are passed to svn_wc__internal_merge().
*/
svn_error_t *
svn_wc__perform_file_merge(svn_skel_t **work_items,
svn_skel_t **conflict_skel,
svn_boolean_t *found_conflict,
svn_wc__db_t *db,
const char *local_abspath,
const char *wri_abspath,
const svn_checksum_t *new_checksum,
const svn_checksum_t *original_checksum,
apr_hash_t *old_actual_props,
const apr_array_header_t *ext_patterns,
svn_revnum_t old_revision,
svn_revnum_t target_revision,
const apr_array_header_t *propchanges,
const char *diff3_cmd,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
/* Actual file exists and has local mods:
Now we need to let loose svn_wc__internal_merge() to merge
the textual changes into the working file. */
const char *oldrev_str, *newrev_str, *mine_str;
const char *merge_left;
svn_boolean_t delete_left = FALSE;
const char *path_ext = "";
const char *new_text_base_tmp_abspath;
enum svn_wc_merge_outcome_t merge_outcome = svn_wc_merge_unchanged;
svn_skel_t *work_item;
*work_items = NULL;
SVN_ERR(svn_wc__db_pristine_get_path(&new_text_base_tmp_abspath,
db, wri_abspath, new_checksum,
scratch_pool, scratch_pool));
/* If we have any file extensions we're supposed to
preserve in generated conflict file names, then find
this path's extension. But then, if it isn't one of
the ones we want to keep in conflict filenames,
pretend it doesn't have an extension at all. */
if (ext_patterns && ext_patterns->nelts)
{
svn_path_splitext(NULL, &path_ext, local_abspath, scratch_pool);
if (! (*path_ext && svn_cstring_match_glob_list(path_ext, ext_patterns)))
path_ext = "";
}
/* old_revision can be invalid when the conflict is against a
local addition */
if (!SVN_IS_VALID_REVNUM(old_revision))
old_revision = 0;
oldrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s",
old_revision,
*path_ext ? "." : "",
*path_ext ? path_ext : "");
newrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s",
target_revision,
*path_ext ? "." : "",
*path_ext ? path_ext : "");
mine_str = apr_psprintf(scratch_pool, ".mine%s%s",
*path_ext ? "." : "",
*path_ext ? path_ext : "");
if (! original_checksum)
{
SVN_ERR(get_empty_tmp_file(&merge_left, db, wri_abspath,
result_pool, scratch_pool));
delete_left = TRUE;
}
else
SVN_ERR(svn_wc__db_pristine_get_path(&merge_left, db, wri_abspath,
original_checksum,
result_pool, scratch_pool));
/* Merge the changes from the old textbase to the new
textbase into the file we're updating.
Remember that this function wants full paths! */
SVN_ERR(svn_wc__internal_merge(&work_item,
conflict_skel,
&merge_outcome,
db,
merge_left,
new_text_base_tmp_abspath,
local_abspath,
wri_abspath,
oldrev_str, newrev_str, mine_str,
old_actual_props,
FALSE /* dry_run */,
diff3_cmd, NULL, propchanges,
cancel_func, cancel_baton,
result_pool, scratch_pool));
*work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
*found_conflict = (merge_outcome == svn_wc_merge_conflict);
/* If we created a temporary left merge file, get rid of it. */
if (delete_left)
{
SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, wri_abspath,
merge_left,
result_pool, scratch_pool));
*work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
}
return SVN_NO_ERROR;
}
/* This is the small planet. It has the complex responsibility of
* "integrating" a new revision of a file into a working copy.
*
* Given a file_baton FB for a file either already under version control, or
* prepared (see below) to join version control, fully install a
* new revision of the file.
*
* ### transitional: installation of the working file will be handled
* ### by the *INSTALL_PRISTINE flag.
*
* By "install", we mean: create a new text-base and prop-base, merge
* any textual and property changes into the working file, and finally
* update all metadata so that the working copy believes it has a new
* working revision of the file. All of this work includes being
* sensitive to eol translation, keyword substitution, and performing
* all actions accumulated the parent directory's work queue.
*
* Set *CONTENT_STATE to the state of the contents after the
* installation.
*
* Return values are allocated in RESULT_POOL and temporary allocations
* are performed in SCRATCH_POOL.
*/
static svn_error_t *
merge_file(svn_skel_t **work_items,
svn_skel_t **conflict_skel,
svn_boolean_t *install_pristine,
const char **install_from,
svn_wc_notify_state_t *content_state,
struct file_baton *fb,
apr_hash_t *actual_props,
apr_time_t last_changed_date,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
struct edit_baton *eb = fb->edit_baton;
struct dir_baton *pb = fb->dir_baton;
svn_boolean_t is_locally_modified;
svn_boolean_t found_text_conflict = FALSE;
SVN_ERR_ASSERT(! fb->shadowed
&& ! fb->obstruction_found
&& ! fb->edit_obstructed);
/*
When this function is called on file F, we assume the following
things are true:
- The new pristine text of F is present in the pristine store
iff FB->NEW_TEXT_BASE_SHA1_CHECKSUM is not NULL.
- The WC metadata still reflects the old version of F.
(We can still access the old pristine base text of F.)
The goal is to update the local working copy of F to reflect
the changes received from the repository, preserving any local
modifications.
*/
*work_items = NULL;
*install_pristine = FALSE;
*install_from = NULL;
/* Start by splitting the file path, getting an access baton for the parent,
and an entry for the file if any. */
/* Has the user made local mods to the working file?
Note that this compares to the current pristine file, which is
different from fb->old_text_base_path if we have a replaced-with-history
file. However, in the case we had an obstruction, we check against the
new text base.
*/
if (fb->adding_file && !fb->add_existed)
{
is_locally_modified = FALSE; /* There is no file: Don't check */
}
else
{
/* The working file is not an obstruction.
So: is the file modified, relative to its ORIGINAL pristine?
This function sets is_locally_modified to FALSE for
files that do not exist and for directories. */
SVN_ERR(svn_wc__internal_file_modified_p(&is_locally_modified,
eb->db, fb->local_abspath,
FALSE /* exact_comparison */,
scratch_pool));
}
/* For 'textual' merging, we use the following system:
When a file is modified and we have a new BASE:
- For text files
* svn_wc_merge uses diff3
* possibly makes backups and marks files as conflicted.
- For binary files
* svn_wc_merge makes backups and marks files as conflicted.
If a file is not modified and we have a new BASE:
* Install from pristine.
If we have property changes related to magic properties or if the
svn:keywords property is set:
* Retranslate from the working file.
*/
if (! is_locally_modified
&& fb->new_text_base_sha1_checksum)
{
/* If there are no local mods, who cares whether it's a text
or binary file! Just write a log command to overwrite
any working file with the new text-base. If newline
conversion or keyword substitution is activated, this
will happen as well during the copy.
For replaced files, though, we want to merge in the changes
even if the file is not modified compared to the (non-revert)
text-base. */
*install_pristine = TRUE;
}
else if (fb->new_text_base_sha1_checksum)
{
/* Actual file exists and has local mods:
Now we need to let loose svn_wc__merge_internal() to merge
the textual changes into the working file. */
SVN_ERR(svn_wc__perform_file_merge(work_items,
conflict_skel,
&found_text_conflict,
eb->db,
fb->local_abspath,
pb->local_abspath,
fb->new_text_base_sha1_checksum,
fb->add_existed
? NULL
: fb->original_checksum,
actual_props,
eb->ext_patterns,
fb->old_revision,
*eb->target_revision,
fb->propchanges,
eb->diff3_cmd,
eb->cancel_func, eb->cancel_baton,
result_pool, scratch_pool));
} /* end: working file exists and has mods */
else
{
/* There is no new text base, but let's see if the working file needs
to be updated for any other reason. */
apr_hash_t *keywords;
/* Determine if any of the propchanges are the "magic" ones that
might require changing the working file. */
svn_boolean_t magic_props_changed;
magic_props_changed = svn_wc__has_magic_property(fb->propchanges);
SVN_ERR(svn_wc__get_translate_info(NULL, NULL,
&keywords,
NULL,
eb->db, fb->local_abspath,
actual_props, TRUE,
scratch_pool, scratch_pool));
if (magic_props_changed || keywords)
{
/* Special edge-case: it's possible that this file installation
only involves propchanges, but that some of those props still
require a retranslation of the working file.
OR that the file doesn't involve propchanges which by themselves
require retranslation, but receiving a change bumps the revision
number which requires re-expansion of keywords... */
if (is_locally_modified)
{
const char *tmptext;
/* Copy and DEtranslate the working file to a temp text-base.
Note that detranslation is done according to the old props. */
SVN_ERR(svn_wc__internal_translated_file(
&tmptext, fb->local_abspath, eb->db, fb->local_abspath,
SVN_WC_TRANSLATE_TO_NF
| SVN_WC_TRANSLATE_NO_OUTPUT_CLEANUP,
eb->cancel_func, eb->cancel_baton,
result_pool, scratch_pool));
/* We always want to reinstall the working file if the magic
properties have changed, or there are any keywords present.
Note that TMPTEXT might actually refer to the working file
itself (the above function skips a detranslate when not
required). This is acceptable, as we will (re)translate
according to the new properties into a temporary file (from
the working file), and then rename the temp into place. Magic!
*/
*install_pristine = TRUE;
*install_from = tmptext;
}
else
{
/* Use our existing 'copy' from the pristine store instead
of making a new copy. This way we can use the standard code
to update the recorded size and modification time.
(Issue #3842) */
*install_pristine = TRUE;
}
}
}
/* Set the returned content state. */
if (found_text_conflict)
*content_state = svn_wc_notify_state_conflicted;
else if (fb->new_text_base_sha1_checksum)
{
if (is_locally_modified)
*content_state = svn_wc_notify_state_merged;
else
*content_state = svn_wc_notify_state_changed;
}
else
*content_state = svn_wc_notify_state_unchanged;
return SVN_NO_ERROR;
}
/* An svn_delta_editor_t function. */
/* Mostly a wrapper around merge_file. */
static svn_error_t *
close_file(void *file_baton,
const char *expected_md5_digest,
apr_pool_t *pool)
{
struct file_baton *fb = file_baton;
struct dir_baton *pdb = fb->dir_baton;
struct edit_baton *eb = fb->edit_baton;
svn_wc_notify_state_t content_state, prop_state;
svn_wc_notify_lock_state_t lock_state;
svn_checksum_t *expected_md5_checksum = NULL;
apr_hash_t *new_base_props = NULL;
apr_hash_t *new_actual_props = NULL;
apr_array_header_t *entry_prop_changes;
apr_array_header_t *dav_prop_changes;
apr_array_header_t *regular_prop_changes;
apr_hash_t *current_base_props = NULL;
apr_hash_t *current_actual_props = NULL;
apr_hash_t *local_actual_props = NULL;
svn_skel_t *all_work_items = NULL;
svn_skel_t *conflict_skel = NULL;
svn_skel_t *work_item;
apr_pool_t *scratch_pool = fb->pool; /* Destroyed at function exit */
svn_boolean_t keep_recorded_info = FALSE;
const svn_checksum_t *new_checksum;
apr_array_header_t *iprops = NULL;
if (fb->skip_this)
{
svn_pool_destroy(fb->pool);
SVN_ERR(maybe_release_dir_info(pdb));
return SVN_NO_ERROR;
}
if (fb->edited)
conflict_skel = fb->edit_conflict;
if (expected_md5_digest)
SVN_ERR(svn_checksum_parse_hex(&expected_md5_checksum, svn_checksum_md5,
expected_md5_digest, scratch_pool));
if (fb->new_text_base_md5_checksum && expected_md5_checksum
&& !svn_checksum_match(expected_md5_checksum,
fb->new_text_base_md5_checksum))
return svn_error_trace(
svn_checksum_mismatch_err(expected_md5_checksum,
fb->new_text_base_md5_checksum,
scratch_pool,
_("Checksum mismatch for '%s'"),
svn_dirent_local_style(
fb->local_abspath, pool)));
/* Gather the changes for each kind of property. */
SVN_ERR(svn_categorize_props(fb->propchanges, &entry_prop_changes,
&dav_prop_changes, &regular_prop_changes,
scratch_pool));
/* Extract the changed_* and lock state information. */
{
svn_revnum_t new_changed_rev;
apr_time_t new_changed_date;
const char *new_changed_author;
SVN_ERR(accumulate_last_change(&new_changed_rev,
&new_changed_date,
&new_changed_author,
entry_prop_changes,
scratch_pool, scratch_pool));
if (SVN_IS_VALID_REVNUM(new_changed_rev))
fb->changed_rev = new_changed_rev;
if (new_changed_date != 0)
fb->changed_date = new_changed_date;
if (new_changed_author != NULL)
fb->changed_author = new_changed_author;
}
/* Determine whether the file has become unlocked. */
{
int i;
lock_state = svn_wc_notify_lock_state_unchanged;
for (i = 0; i < entry_prop_changes->nelts; ++i)
{
const svn_prop_t *prop
= &APR_ARRAY_IDX(entry_prop_changes, i, svn_prop_t);
/* If we see a change to the LOCK_TOKEN entry prop, then the only
possible change is its REMOVAL. Thus, the lock has been removed,
and we should likewise remove our cached copy of it. */
if (! strcmp(prop->name, SVN_PROP_ENTRY_LOCK_TOKEN))
{
/* If we lose the lock, but not because we are switching to
another url, remove the state lock from the wc */
if (! eb->switch_relpath
|| strcmp(fb->new_relpath, fb->old_repos_relpath) == 0)
{
SVN_ERR_ASSERT(prop->value == NULL);
SVN_ERR(svn_wc__db_lock_remove(eb->db, fb->local_abspath,
scratch_pool));
lock_state = svn_wc_notify_lock_state_unlocked;
}
break;
}
}
}
/* Install all kinds of properties. It is important to do this before
any file content merging, since that process might expand keywords, in
which case we want the new entryprops to be in place. */
/* Write log commands to merge REGULAR_PROPS into the existing
properties of FB->LOCAL_ABSPATH. Update *PROP_STATE to reflect
the result of the regular prop merge.
BASE_PROPS and WORKING_PROPS are hashes of the base and
working props of the file; if NULL they are read from the wc. */
/* ### some of this feels like voodoo... */
if ((!fb->adding_file || fb->add_existed)
&& !fb->shadowed)
SVN_ERR(svn_wc__get_actual_props(&local_actual_props,
eb->db, fb->local_abspath,
scratch_pool, scratch_pool));
if (local_actual_props == NULL)
local_actual_props = apr_hash_make(scratch_pool);
if (fb->add_existed)
{
/* This node already exists. Grab the current pristine properties. */
SVN_ERR(svn_wc__db_read_pristine_props(&current_base_props,
eb->db, fb->local_abspath,
scratch_pool, scratch_pool));
current_actual_props = local_actual_props;
}
else if (!fb->adding_file)
{
/* Get the BASE properties for proper merging. */
SVN_ERR(svn_wc__db_base_get_props(&current_base_props,
eb->db, fb->local_abspath,
scratch_pool, scratch_pool));
current_actual_props = local_actual_props;
}
/* Note: even if the node existed before, it may not have
pristine props (e.g a local-add) */
if (current_base_props == NULL)
current_base_props = apr_hash_make(scratch_pool);
/* And new nodes need an empty set of ACTUAL props. */
if (current_actual_props == NULL)
current_actual_props = apr_hash_make(scratch_pool);
prop_state = svn_wc_notify_state_unknown;
if (! fb->shadowed)
{
svn_boolean_t install_pristine;
const char *install_from = NULL;
/* Merge the 'regular' props into the existing working proplist. */
/* This will merge the old and new props into a new prop db, and
write <cp> commands to the logfile to install the merged
props. */
new_base_props = svn_prop__patch(current_base_props, regular_prop_changes,
scratch_pool);
SVN_ERR(svn_wc__merge_props(&conflict_skel,
&prop_state,
&new_actual_props,
eb->db,
fb->local_abspath,
NULL /* server_baseprops (update, not merge) */,
current_base_props,
current_actual_props,
regular_prop_changes, /* propchanges */
scratch_pool,
scratch_pool));
/* We will ALWAYS have properties to save (after a not-dry-run merge). */
SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL);
/* Merge the text. This will queue some additional work. */
if (!fb->obstruction_found && !fb->edit_obstructed)
{
svn_error_t *err;
err = merge_file(&work_item, &conflict_skel,
&install_pristine, &install_from,
&content_state, fb, current_actual_props,
fb->changed_date, scratch_pool, scratch_pool);
if (err && err->apr_err == SVN_ERR_WC_PATH_ACCESS_DENIED)
{
if (eb->notify_func)
{
svn_wc_notify_t *notify =svn_wc_create_notify(
fb->local_abspath,
svn_wc_notify_update_skip_access_denied,
scratch_pool);
notify->kind = svn_node_file;
notify->err = err;
eb->notify_func(eb->notify_baton, notify, scratch_pool);
}
svn_error_clear(err);
SVN_ERR(remember_skipped_tree(eb, fb->local_abspath,
scratch_pool));
fb->skip_this = TRUE;
svn_pool_destroy(fb->pool);
SVN_ERR(maybe_release_dir_info(pdb));
return SVN_NO_ERROR;
}
else
SVN_ERR(err);
all_work_items = svn_wc__wq_merge(all_work_items, work_item,
scratch_pool);
}
else
{
install_pristine = FALSE;
if (fb->new_text_base_sha1_checksum)
content_state = svn_wc_notify_state_changed;
else
content_state = svn_wc_notify_state_unchanged;
}
if (install_pristine)
{
svn_boolean_t record_fileinfo;
/* If we are installing from the pristine contents, then go ahead and
record the fileinfo. That will be the "proper" values. Installing
from some random file means the fileinfo does NOT correspond to
the pristine (in which case, the fileinfo will be cleared for
safety's sake). */
record_fileinfo = (install_from == NULL);
SVN_ERR(svn_wc__wq_build_file_install(&work_item,
eb->db,
fb->local_abspath,
install_from,
eb->use_commit_times,
record_fileinfo,
scratch_pool, scratch_pool));
all_work_items = svn_wc__wq_merge(all_work_items, work_item,
scratch_pool);
}
else if (lock_state == svn_wc_notify_lock_state_unlocked
&& !fb->obstruction_found)
{
/* If a lock was removed and we didn't update the text contents, we
might need to set the file read-only.
Note: this will also update the executable flag, but ... meh. */
SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, eb->db,
fb->local_abspath,
scratch_pool, scratch_pool));
all_work_items = svn_wc__wq_merge(all_work_items, work_item,
scratch_pool);
}
if (! install_pristine
&& (content_state == svn_wc_notify_state_unchanged))
{
/* It is safe to keep the current recorded timestamp and size */
keep_recorded_info = TRUE;
}
/* Clean up any temporary files. */
/* Remove the INSTALL_FROM file, as long as it doesn't refer to the
working file. */
if (install_from != NULL
&& strcmp(install_from, fb->local_abspath) != 0)
{
SVN_ERR(svn_wc__wq_build_file_remove(&work_item, eb->db,
fb->local_abspath, install_from,
scratch_pool, scratch_pool));
all_work_items = svn_wc__wq_merge(all_work_items, work_item,
scratch_pool);
}
}
else
{
/* Adding or updating a BASE node under a locally added node. */
apr_hash_t *fake_actual_props;
if (fb->adding_file)
fake_actual_props = apr_hash_make(scratch_pool);
else
fake_actual_props = current_base_props;
/* Store the incoming props (sent as propchanges) in new_base_props
and create a set of new actual props to use for notifications */
new_base_props = svn_prop__patch(current_base_props, regular_prop_changes,
scratch_pool);
SVN_ERR(svn_wc__merge_props(&conflict_skel,
&prop_state,
&new_actual_props,
eb->db,
fb->local_abspath,
NULL /* server_baseprops (not merging) */,
current_base_props /* pristine_props */,
fake_actual_props /* actual_props */,
regular_prop_changes, /* propchanges */
scratch_pool,
scratch_pool));
if (fb->new_text_base_sha1_checksum)
content_state = svn_wc_notify_state_changed;
else
content_state = svn_wc_notify_state_unchanged;
}
/* Insert/replace the BASE node with all of the new metadata. */
/* Set the 'checksum' column of the file's BASE_NODE row to
* NEW_TEXT_BASE_SHA1_CHECKSUM. The pristine text identified by that
* checksum is already in the pristine store. */
new_checksum = fb->new_text_base_sha1_checksum;
/* If we don't have a NEW checksum, then the base must not have changed.
Just carry over the old checksum. */
if (new_checksum == NULL)
new_checksum = fb->original_checksum;
if (conflict_skel)
{
SVN_ERR(complete_conflict(conflict_skel,
fb->edit_baton,
fb->local_abspath,
fb->old_repos_relpath,
fb->old_revision,
fb->new_relpath,
svn_node_file, svn_node_file,
fb->pool, scratch_pool));
SVN_ERR(svn_wc__conflict_create_markers(&work_item,
eb->db, fb->local_abspath,
conflict_skel,
scratch_pool, scratch_pool));
all_work_items = svn_wc__wq_merge(all_work_items, work_item,
scratch_pool);
}
/* Any inherited props to be set set for this base node? */
if (eb->wcroot_iprops)
{
iprops = svn_hash_gets(eb->wcroot_iprops, fb->local_abspath);
/* close_edit may also update iprops for switched nodes, catching
those for which close_directory is never called (e.g. a switch
with no changes). So as a minor optimization we remove any
iprops from the hash so as not to set them again in
close_edit. */
if (iprops)
svn_hash_sets(eb->wcroot_iprops, fb->local_abspath, NULL);
}
SVN_ERR(svn_wc__db_base_add_file(eb->db, fb->local_abspath,
eb->wcroot_abspath,
fb->new_relpath,
eb->repos_root, eb->repos_uuid,
*eb->target_revision,
new_base_props,
fb->changed_rev,
fb->changed_date,
fb->changed_author,
new_checksum,
(dav_prop_changes->nelts > 0)
? svn_prop_array_to_hash(
dav_prop_changes,
scratch_pool)
: NULL,
(fb->add_existed && fb->adding_file),
(! fb->shadowed) && new_base_props,
new_actual_props,
iprops,
keep_recorded_info,
(fb->shadowed && fb->obstruction_found),
conflict_skel,
all_work_items,
scratch_pool));
if (conflict_skel && eb->conflict_func)
SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath,
conflict_skel,
NULL /* merge_options */,
eb->conflict_func,
eb->conflict_baton,
eb->cancel_func,
eb->cancel_baton,
scratch_pool));
/* Deal with the WORKING tree, based on updates to the BASE tree. */
svn_hash_sets(fb->dir_baton->not_present_files, fb->name, NULL);
/* Send a notification to the callback function. (Skip notifications
about files which were already notified for another reason.) */
if (eb->notify_func && !fb->already_notified
&& (fb->edited || lock_state == svn_wc_notify_lock_state_unlocked))
{
svn_wc_notify_t *notify;
svn_wc_notify_action_t action = svn_wc_notify_update_update;
if (fb->edited)
{
if (fb->shadowed || fb->edit_obstructed)
action = fb->adding_file
? svn_wc_notify_update_shadowed_add
: svn_wc_notify_update_shadowed_update;
else if (fb->obstruction_found || fb->add_existed)
{
if (content_state != svn_wc_notify_state_conflicted)
action = svn_wc_notify_exists;
}
else if (fb->adding_file)
{
action = svn_wc_notify_update_add;
}
}
else
{
SVN_ERR_ASSERT(lock_state == svn_wc_notify_lock_state_unlocked);
action = svn_wc_notify_update_broken_lock;
}
/* If the file was moved-away, notify for the moved-away node.
* The original location only had its BASE info changed and
* we don't usually notify about such changes. */
notify = svn_wc_create_notify(fb->local_abspath, action, scratch_pool);
notify->kind = svn_node_file;
notify->content_state = content_state;
notify->prop_state = prop_state;
notify->lock_state = lock_state;
notify->revision = *eb->target_revision;
notify->old_revision = fb->old_revision;
/* Fetch the mimetype from the actual properties */
notify->mime_type = svn_prop_get_value(new_actual_props,
SVN_PROP_MIME_TYPE);
eb->notify_func(eb->notify_baton, notify, scratch_pool);
}
svn_pool_destroy(fb->pool); /* Destroy scratch_pool */
/* We have one less referrer to the directory */
SVN_ERR(maybe_release_dir_info(pdb));
return SVN_NO_ERROR;
}
/* An svn_delta_editor_t function. */
static svn_error_t *
close_edit(void *edit_baton,
apr_pool_t *pool)
{
struct edit_baton *eb = edit_baton;
apr_pool_t *scratch_pool = eb->pool;
/* The editor didn't even open the root; we have to take care of
some cleanup stuffs. */
if (! eb->root_opened
&& *eb->target_basename == '\0')
{
/* We need to "un-incomplete" the root directory. */
SVN_ERR(svn_wc__db_temp_op_end_directory_update(eb->db,
eb->anchor_abspath,
scratch_pool));
}
/* By definition, anybody "driving" this editor for update or switch
purposes at a *minimum* must have called set_target_revision() at
the outset, and close_edit() at the end -- even if it turned out
that no changes ever had to be made, and open_root() was never
called. That's fine. But regardless, when the edit is over,
this editor needs to make sure that *all* paths have had their
revisions bumped to the new target revision. */
/* Make sure our update target now has the new working revision.
Also, if this was an 'svn switch', then rewrite the target's
url. All of this tweaking might happen recursively! Note
that if eb->target is NULL, that's okay (albeit "sneaky",
some might say). */
/* Extra check: if the update did nothing but make its target
'deleted', then do *not* run cleanup on the target, as it
will only remove the deleted entry! */
if (! eb->target_deleted)
{
SVN_ERR(svn_wc__db_op_bump_revisions_post_update(eb->db,
eb->target_abspath,
eb->requested_depth,
eb->switch_relpath,
eb->repos_root,
eb->repos_uuid,
*(eb->target_revision),
eb->skipped_trees,
eb->wcroot_iprops,
eb->notify_func,
eb->notify_baton,
eb->pool));
if (*eb->target_basename != '\0')
{
svn_wc__db_status_t status;
svn_error_t *err;
/* Note: we are fetching information about the *target*, not anchor.
There is no guarantee that the target has a BASE node.
For example:
The node was not present in BASE, but locally-added, and the
update did not create a new BASE node "under" the local-add.
If there is no BASE node for the target, then we certainly don't
have to worry about removing it. */
err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
eb->db, eb->target_abspath,
scratch_pool, scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
svn_error_clear(err);
}
else if (status == svn_wc__db_status_excluded)
{
/* There is a small chance that the explicit target of an update/
switch is gone in the repository, in that specific case the
node hasn't been re-added to the BASE tree by this update.
If so, we should get rid of this excluded node now. */
SVN_ERR(svn_wc__db_base_remove(eb->db, eb->target_abspath,
FALSE /* keep_as_working */,
FALSE /* queue_deletes */,
FALSE /* remove_locks */,
SVN_INVALID_REVNUM,
NULL, NULL, scratch_pool));
}
}
}
/* The edit is over: run the wq with proper cancel support,
but first kill the handler that would run it on the pool
cleanup at the end of this function. */
apr_pool_cleanup_kill(eb->pool, eb, cleanup_edit_baton);
SVN_ERR(svn_wc__wq_run(eb->db, eb->wcroot_abspath,
eb->cancel_func, eb->cancel_baton,
eb->pool));
/* The edit is over, free its pool.
### No, this is wrong. Who says this editor/baton won't be used
again? But the change is not merely to remove this call. We
should also make eb->pool not be a subpool (see make_editor),
and change callers of svn_client_{checkout,update,switch} to do
better pool management. ### */
svn_pool_destroy(eb->pool);
return SVN_NO_ERROR;
}
/*** Returning editors. ***/
/* Helper for the three public editor-supplying functions. */
static svn_error_t *
make_editor(svn_revnum_t *target_revision,
svn_wc__db_t *db,
const char *anchor_abspath,
const char *target_basename,
apr_hash_t *wcroot_iprops,
svn_boolean_t use_commit_times,
const char *switch_url,
svn_depth_t depth,
svn_boolean_t depth_is_sticky,
svn_boolean_t allow_unver_obstructions,
svn_boolean_t adds_as_modification,
svn_boolean_t server_performs_filtering,
svn_boolean_t clean_checkout,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_wc_dirents_func_t fetch_dirents_func,
void *fetch_dirents_baton,
svn_wc_conflict_resolver_func2_t conflict_func,
void *conflict_baton,
svn_wc_external_update_t external_func,
void *external_baton,
const char *diff3_cmd,
const apr_array_header_t *preserved_exts,
const svn_delta_editor_t **editor,
void **edit_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
struct edit_baton *eb;
void *inner_baton;
apr_pool_t *edit_pool = svn_pool_create(result_pool);
svn_delta_editor_t *tree_editor = svn_delta_default_editor(edit_pool);
const svn_delta_editor_t *inner_editor;
const char *repos_root, *repos_uuid;
struct svn_wc__shim_fetch_baton_t *sfb;
svn_delta_shim_callbacks_t *shim_callbacks =
svn_delta_shim_callbacks_default(edit_pool);
/* An unknown depth can't be sticky. */
if (depth == svn_depth_unknown)
depth_is_sticky = FALSE;
/* Get the anchor's repository root and uuid. The anchor must already exist
in BASE. */
SVN_ERR(svn_wc__db_scan_base_repos(NULL, &repos_root, &repos_uuid,
db, anchor_abspath,
result_pool, scratch_pool));
/* With WC-NG we need a valid repository root */
SVN_ERR_ASSERT(repos_root != NULL && repos_uuid != NULL);
/* Disallow a switch operation to change the repository root of the target,
if that is known. */
if (switch_url && !svn_uri__is_ancestor(repos_root, switch_url))
return svn_error_createf(SVN_ERR_WC_INVALID_SWITCH, NULL,
_("'%s'\nis not the same repository as\n'%s'"),
switch_url, repos_root);
/* Construct an edit baton. */
eb = apr_pcalloc(edit_pool, sizeof(*eb));
eb->pool = edit_pool;
eb->use_commit_times = use_commit_times;
eb->target_revision = target_revision;
eb->repos_root = repos_root;
eb->repos_uuid = repos_uuid;
eb->db = db;
eb->target_basename = target_basename;
eb->anchor_abspath = anchor_abspath;
eb->wcroot_iprops = wcroot_iprops;
SVN_ERR(svn_wc__db_get_wcroot(&eb->wcroot_abspath, db, anchor_abspath,
edit_pool, scratch_pool));
if (switch_url)
eb->switch_relpath =
svn_uri_skip_ancestor(repos_root, switch_url, scratch_pool);
else
eb->switch_relpath = NULL;
if (svn_path_is_empty(target_basename))
eb->target_abspath = eb->anchor_abspath;
else
eb->target_abspath = svn_dirent_join(eb->anchor_abspath, target_basename,
edit_pool);
eb->requested_depth = depth;
eb->depth_is_sticky = depth_is_sticky;
eb->notify_func = notify_func;
eb->notify_baton = notify_baton;
eb->external_func = external_func;
eb->external_baton = external_baton;
eb->diff3_cmd = diff3_cmd;
eb->cancel_func = cancel_func;
eb->cancel_baton = cancel_baton;
eb->conflict_func = conflict_func;
eb->conflict_baton = conflict_baton;
eb->allow_unver_obstructions = allow_unver_obstructions;
eb->adds_as_modification = adds_as_modification;
eb->clean_checkout = clean_checkout;
eb->skipped_trees = apr_hash_make(edit_pool);
eb->dir_dirents = apr_hash_make(edit_pool);
eb->ext_patterns = preserved_exts;
apr_pool_cleanup_register(edit_pool, eb, cleanup_edit_baton,
apr_pool_cleanup_null);
/* Construct an editor. */
tree_editor->set_target_revision = set_target_revision;
tree_editor->open_root = open_root;
tree_editor->delete_entry = delete_entry;
tree_editor->add_directory = add_directory;
tree_editor->open_directory = open_directory;
tree_editor->change_dir_prop = change_dir_prop;
tree_editor->close_directory = close_directory;
tree_editor->absent_directory = absent_directory;
tree_editor->add_file = add_file;
tree_editor->open_file = open_file;
tree_editor->apply_textdelta = apply_textdelta;
tree_editor->change_file_prop = change_file_prop;
tree_editor->close_file = close_file;
tree_editor->absent_file = absent_file;
tree_editor->close_edit = close_edit;
/* Fiddle with the type system. */
inner_editor = tree_editor;
inner_baton = eb;
if (!depth_is_sticky
&& depth != svn_depth_unknown
&& svn_depth_empty <= depth && depth < svn_depth_infinity
&& fetch_dirents_func)
{
/* We are asked to perform an update at a depth less than the ambient
depth. In this case the update won't describe additions that would
have been reported if we updated at the ambient depth. */
svn_error_t *err;
svn_node_kind_t dir_kind;
svn_wc__db_status_t dir_status;
const char *dir_repos_relpath;
svn_depth_t dir_depth;
/* we have to do this on the target of the update, not the anchor */
err = svn_wc__db_base_get_info(&dir_status, &dir_kind, NULL,
&dir_repos_relpath, NULL, NULL, NULL,
NULL, NULL, &dir_depth, NULL, NULL, NULL,
NULL, NULL, NULL,
db, eb->target_abspath,
scratch_pool, scratch_pool);
if (!err
&& dir_kind == svn_node_dir
&& dir_status == svn_wc__db_status_normal)
{
if (dir_depth > depth)
{
apr_hash_t *dirents;
/* If we switch, we should look at the new relpath */
if (eb->switch_relpath)
dir_repos_relpath = eb->switch_relpath;
SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents,
repos_root, dir_repos_relpath,
edit_pool, scratch_pool));
if (dirents != NULL && apr_hash_count(dirents))
svn_hash_sets(eb->dir_dirents,
apr_pstrdup(edit_pool, dir_repos_relpath),
dirents);
}
if (depth == svn_depth_immediates)
{
/* Worst case scenario of issue #3569 fix: We have to do the
same for all existing subdirs, but then we check for
svn_depth_empty. */
const apr_array_header_t *children;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
int i;
SVN_ERR(svn_wc__db_base_get_children(&children, db,
eb->target_abspath,
scratch_pool,
iterpool));
for (i = 0; i < children->nelts; i++)
{
const char *child_abspath;
const char *child_name;
svn_pool_clear(iterpool);
child_name = APR_ARRAY_IDX(children, i, const char *);
child_abspath = svn_dirent_join(eb->target_abspath,
child_name, iterpool);
SVN_ERR(svn_wc__db_base_get_info(&dir_status, &dir_kind,
NULL, &dir_repos_relpath,
NULL, NULL, NULL, NULL,
NULL, &dir_depth, NULL,
NULL, NULL, NULL, NULL,
NULL,
db, child_abspath,
iterpool, iterpool));
if (dir_kind == svn_node_dir
&& dir_status == svn_wc__db_status_normal
&& dir_depth > svn_depth_empty)
{
apr_hash_t *dirents;
/* If we switch, we should look at the new relpath */
if (eb->switch_relpath)
dir_repos_relpath = svn_relpath_join(
eb->switch_relpath,
child_name, iterpool);
SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents,
repos_root, dir_repos_relpath,
edit_pool, iterpool));
if (dirents != NULL && apr_hash_count(dirents))
svn_hash_sets(eb->dir_dirents,
apr_pstrdup(edit_pool,
dir_repos_relpath),
dirents);
}
}
}
}
else if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
svn_error_clear(err);
else
SVN_ERR(err);
}
/* We need to limit the scope of our operation to the ambient depths
present in the working copy already, but only if the requested
depth is not sticky. If a depth was explicitly requested,
libsvn_delta/depth_filter_editor.c will ensure that we never see
editor calls that extend beyond the scope of the requested depth.
But even what we do so might extend beyond the scope of our
ambient depth. So we use another filtering editor to avoid
modifying the ambient working copy depth when not asked to do so.
(This can also be skipped if the server understands depth.) */
if (!server_performs_filtering
&& !depth_is_sticky)
SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor,
&inner_baton,
db,
anchor_abspath,
target_basename,
inner_editor,
inner_baton,
result_pool));
SVN_ERR(svn_delta_get_cancellation_editor(cancel_func,
cancel_baton,
inner_editor,
inner_baton,
editor,
edit_baton,
result_pool));
sfb = apr_palloc(result_pool, sizeof(*sfb));
sfb->db = db;
sfb->base_abspath = eb->anchor_abspath;
sfb->fetch_base = TRUE;
shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func;
shim_callbacks->fetch_props_func = svn_wc__fetch_props_func;
shim_callbacks->fetch_base_func = svn_wc__fetch_base_func;
shim_callbacks->fetch_baton = sfb;
SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
NULL, NULL, shim_callbacks,
result_pool, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__get_update_editor(const svn_delta_editor_t **editor,
void **edit_baton,
svn_revnum_t *target_revision,
svn_wc_context_t *wc_ctx,
const char *anchor_abspath,
const char *target_basename,
apr_hash_t *wcroot_iprops,
svn_boolean_t use_commit_times,
svn_depth_t depth,
svn_boolean_t depth_is_sticky,
svn_boolean_t allow_unver_obstructions,
svn_boolean_t adds_as_modification,
svn_boolean_t server_performs_filtering,
svn_boolean_t clean_checkout,
const char *diff3_cmd,
const apr_array_header_t *preserved_exts,
svn_wc_dirents_func_t fetch_dirents_func,
void *fetch_dirents_baton,
svn_wc_conflict_resolver_func2_t conflict_func,
void *conflict_baton,
svn_wc_external_update_t external_func,
void *external_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
return make_editor(target_revision, wc_ctx->db, anchor_abspath,
target_basename, wcroot_iprops, use_commit_times,
NULL, depth, depth_is_sticky, allow_unver_obstructions,
adds_as_modification, server_performs_filtering,
clean_checkout,
notify_func, notify_baton,
cancel_func, cancel_baton,
fetch_dirents_func, fetch_dirents_baton,
conflict_func, conflict_baton,
external_func, external_baton,
diff3_cmd, preserved_exts, editor, edit_baton,
result_pool, scratch_pool);
}
svn_error_t *
svn_wc__get_switch_editor(const svn_delta_editor_t **editor,
void **edit_baton,
svn_revnum_t *target_revision,
svn_wc_context_t *wc_ctx,
const char *anchor_abspath,
const char *target_basename,
const char *switch_url,
apr_hash_t *wcroot_iprops,
svn_boolean_t use_commit_times,
svn_depth_t depth,
svn_boolean_t depth_is_sticky,
svn_boolean_t allow_unver_obstructions,
svn_boolean_t server_performs_filtering,
const char *diff3_cmd,
const apr_array_header_t *preserved_exts,
svn_wc_dirents_func_t fetch_dirents_func,
void *fetch_dirents_baton,
svn_wc_conflict_resolver_func2_t conflict_func,
void *conflict_baton,
svn_wc_external_update_t external_func,
void *external_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
SVN_ERR_ASSERT(switch_url && svn_uri_is_canonical(switch_url, scratch_pool));
return make_editor(target_revision, wc_ctx->db, anchor_abspath,
target_basename, wcroot_iprops, use_commit_times,
switch_url,
depth, depth_is_sticky, allow_unver_obstructions,
FALSE /* adds_as_modification */,
server_performs_filtering,
FALSE /* clean_checkout */,
notify_func, notify_baton,
cancel_func, cancel_baton,
fetch_dirents_func, fetch_dirents_baton,
conflict_func, conflict_baton,
external_func, external_baton,
diff3_cmd, preserved_exts,
editor, edit_baton,
result_pool, scratch_pool);
}
/* ### Note that this function is completely different from the rest of the
update editor in what it updates. The update editor changes only BASE
and ACTUAL and this function just changes WORKING and ACTUAL.
In the entries world this function shared a lot of code with the
update editor but in the wonderful new WC-NG world it will probably
do more and more by itself and would be more logically grouped with
the add/copy functionality in adm_ops.c and copy.c. */
svn_error_t *
svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx,
const char *local_abspath,
svn_stream_t *new_base_contents,
svn_stream_t *new_contents,
apr_hash_t *new_base_props,
apr_hash_t *new_props,
const char *copyfrom_url,
svn_revnum_t copyfrom_rev,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
svn_wc__db_t *db = wc_ctx->db;
const char *dir_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
svn_wc__db_status_t status;
svn_node_kind_t kind;
const char *tmp_text_base_abspath;
svn_checksum_t *new_text_base_md5_checksum;
svn_checksum_t *new_text_base_sha1_checksum;
const char *source_abspath = NULL;
svn_skel_t *all_work_items = NULL;
svn_skel_t *work_item;
const char *repos_root_url;
const char *repos_uuid;
const char *original_repos_relpath;
svn_revnum_t changed_rev;
apr_time_t changed_date;
const char *changed_author;
svn_error_t *err;
apr_pool_t *pool = scratch_pool;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR_ASSERT(new_base_contents != NULL);
SVN_ERR_ASSERT(new_base_props != NULL);
/* We should have a write lock on this file's parent directory. */
SVN_ERR(svn_wc__write_check(db, dir_abspath, pool));
err = svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL,
db, local_abspath, scratch_pool, scratch_pool);
if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
else if(err)
svn_error_clear(err);
else
switch (status)
{
case svn_wc__db_status_not_present:
case svn_wc__db_status_deleted:
break;
default:
return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,
_("Node '%s' exists."),
svn_dirent_local_style(local_abspath,
scratch_pool));
}
SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, &repos_root_url,
&repos_uuid, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
db, dir_abspath, scratch_pool, scratch_pool));
switch (status)
{
case svn_wc__db_status_normal:
case svn_wc__db_status_added:
break;
case svn_wc__db_status_deleted:
return
svn_error_createf(SVN_ERR_WC_SCHEDULE_CONFLICT, NULL,
_("Can't add '%s' to a parent directory"
" scheduled for deletion"),
svn_dirent_local_style(local_abspath,
scratch_pool));
default:
return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, err,
_("Can't find parent directory's node while"
" trying to add '%s'"),
svn_dirent_local_style(local_abspath,
scratch_pool));
}
if (kind != svn_node_dir)
return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
_("Can't schedule an addition of '%s'"
" below a not-directory node"),
svn_dirent_local_style(local_abspath,
scratch_pool));
/* Fabricate the anticipated new URL of the target and check the
copyfrom URL to be in the same repository. */
if (copyfrom_url != NULL)
{
/* Find the repository_root via the parent directory, which
is always versioned before this function is called */
if (!repos_root_url)
{
/* The parent is an addition, scan upwards to find the right info */
SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL,
&repos_root_url, &repos_uuid,
NULL, NULL, NULL, NULL,
wc_ctx->db, dir_abspath,
scratch_pool, scratch_pool));
}
SVN_ERR_ASSERT(repos_root_url);
original_repos_relpath =
svn_uri_skip_ancestor(repos_root_url, copyfrom_url, scratch_pool);
if (!original_repos_relpath)
return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Copyfrom-url '%s' has different repository"
" root than '%s'"),
copyfrom_url, repos_root_url);
}
else
{
original_repos_relpath = NULL;
copyfrom_rev = SVN_INVALID_REVNUM; /* Just to be sure. */
}
/* Set CHANGED_* to reflect the entry props in NEW_BASE_PROPS, and
filter NEW_BASE_PROPS so it contains only regular props. */
{
apr_array_header_t *regular_props;
apr_array_header_t *entry_props;
SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_base_props, pool),
&entry_props, NULL, &regular_props,
pool));
/* Put regular props back into a hash table. */
new_base_props = svn_prop_array_to_hash(regular_props, pool);
/* Get the change_* info from the entry props. */
SVN_ERR(accumulate_last_change(&changed_rev,
&changed_date,
&changed_author,
entry_props, pool, pool));
}
/* Copy NEW_BASE_CONTENTS into a temporary file so our log can refer to
it, and set TMP_TEXT_BASE_ABSPATH to its path. Compute its
NEW_TEXT_BASE_MD5_CHECKSUM and NEW_TEXT_BASE_SHA1_CHECKSUM as we copy. */
{
svn_stream_t *tmp_base_contents;
SVN_ERR(svn_wc__open_writable_base(&tmp_base_contents,
&tmp_text_base_abspath,
&new_text_base_md5_checksum,
&new_text_base_sha1_checksum,
wc_ctx->db, local_abspath,
pool, pool));
SVN_ERR(svn_stream_copy3(new_base_contents, tmp_base_contents,
cancel_func, cancel_baton, pool));
}
/* If the caller gave us a new working file, copy it to a safe (temporary)
location and set SOURCE_ABSPATH to that path. We'll then translate/copy
that into place after the node's state has been created. */
if (new_contents)
{
const char *temp_dir_abspath;
svn_stream_t *tmp_contents;
SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db,
local_abspath, pool, pool));
SVN_ERR(svn_stream_open_unique(&tmp_contents, &source_abspath,
temp_dir_abspath, svn_io_file_del_none,
pool, pool));
SVN_ERR(svn_stream_copy3(new_contents, tmp_contents,
cancel_func, cancel_baton, pool));
}
/* Install new text base for copied files. Added files do NOT have a
text base. */
if (copyfrom_url != NULL)
{
SVN_ERR(svn_wc__db_pristine_install(db, tmp_text_base_abspath,
new_text_base_sha1_checksum,
new_text_base_md5_checksum, pool));
}
else
{
/* ### There's something wrong around here. Sometimes (merge from a
foreign repository, at least) we are called with copyfrom_url =
NULL and an empty new_base_contents (and an empty set of
new_base_props). Why an empty "new base"?
That happens in merge_tests.py 54,87,88,89,143.
In that case, having been given this supposed "new base" file, we
copy it and calculate its checksum but do not install it. Why?
That must be wrong.
To crudely work around one issue with this, that we shouldn't
record a checksum in the database if we haven't installed the
corresponding pristine text, for now we'll just set the checksum
to NULL.
The proper solution is probably more like: the caller should pass
NULL for the missing information, and this function should learn to
handle that. */
new_text_base_sha1_checksum = NULL;
new_text_base_md5_checksum = NULL;
}
/* For added files without NEW_CONTENTS, then generate the working file
from the provided "pristine" contents. */
if (new_contents == NULL && copyfrom_url == NULL)
source_abspath = tmp_text_base_abspath;
{
svn_boolean_t record_fileinfo;
/* If new contents were provided, then we do NOT want to record the
file information. We assume the new contents do not match the
"proper" values for RECORDED_SIZE and RECORDED_TIME. */
record_fileinfo = (new_contents == NULL);
/* Install the working copy file (with appropriate translation) from
the appropriate source. SOURCE_ABSPATH will be NULL, indicating an
installation from the pristine (available for copied/moved files),
or it will specify a temporary file where we placed a "pristine"
(for an added file) or a detranslated local-mods file. */
SVN_ERR(svn_wc__wq_build_file_install(&work_item,
db, local_abspath,
source_abspath,
FALSE /* use_commit_times */,
record_fileinfo,
pool, pool));
all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
/* If we installed from somewhere besides the official pristine, then
it is a temporary file, which needs to be removed. */
if (source_abspath != NULL)
{
SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, local_abspath,
source_abspath,
pool, pool));
all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
}
}
/* ### ideally, we would have a single DB operation, and queue the work
### items on that. for now, we'll queue them with the second call. */
SVN_ERR(svn_wc__db_op_copy_file(db, local_abspath,
new_base_props,
changed_rev,
changed_date,
changed_author,
original_repos_relpath,
original_repos_relpath ? repos_root_url
: NULL,
original_repos_relpath ? repos_uuid : NULL,
copyfrom_rev,
new_text_base_sha1_checksum,
TRUE,
new_props,
FALSE /* is_move */,
NULL /* conflict */,
all_work_items,
pool));
return svn_error_trace(svn_wc__wq_run(db, dir_abspath,
cancel_func, cancel_baton,
pool));
}
svn_error_t *
svn_wc__complete_directory_add(svn_wc_context_t *wc_ctx,
const char *local_abspath,
apr_hash_t *new_original_props,
const char *copyfrom_url,
svn_revnum_t copyfrom_rev,
apr_pool_t *scratch_pool)
{
svn_wc__db_status_t status;
svn_node_kind_t kind;
const char *original_repos_relpath;
const char *original_root_url;
const char *original_uuid;
svn_boolean_t had_props;
svn_boolean_t props_mod;
svn_revnum_t original_revision;
svn_revnum_t changed_rev;
apr_time_t changed_date;
const char *changed_author;
SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
&original_repos_relpath, &original_root_url,
&original_uuid, &original_revision, NULL, NULL,
NULL, NULL, NULL, NULL, &had_props, &props_mod,
NULL, NULL, NULL,
wc_ctx->db, local_abspath,
scratch_pool, scratch_pool));
if (status != svn_wc__db_status_added
|| kind != svn_node_dir
|| had_props
|| props_mod
|| !original_repos_relpath)
{
return svn_error_createf(
SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
_("'%s' is not an unmodified copied directory"),
svn_dirent_local_style(local_abspath, scratch_pool));
}
if (original_revision != copyfrom_rev
|| strcmp(copyfrom_url,
svn_path_url_add_component2(original_root_url,
original_repos_relpath,
scratch_pool)))
{
return svn_error_createf(
SVN_ERR_WC_COPYFROM_PATH_NOT_FOUND, NULL,
_("Copyfrom '%s' doesn't match original location of '%s'"),
copyfrom_url,
svn_dirent_local_style(local_abspath, scratch_pool));
}
{
apr_array_header_t *regular_props;
apr_array_header_t *entry_props;
SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_original_props,
scratch_pool),
&entry_props, NULL, &regular_props,
scratch_pool));
/* Put regular props back into a hash table. */
new_original_props = svn_prop_array_to_hash(regular_props, scratch_pool);
/* Get the change_* info from the entry props. */
SVN_ERR(accumulate_last_change(&changed_rev,
&changed_date,
&changed_author,
entry_props, scratch_pool, scratch_pool));
}
return svn_error_trace(
svn_wc__db_op_copy_dir(wc_ctx->db, local_abspath,
new_original_props,
changed_rev, changed_date, changed_author,
original_repos_relpath, original_root_url,
original_uuid, original_revision,
NULL /* children */,
- FALSE /* is_move */,
svn_depth_infinity,
+ FALSE /* is_move */,
NULL /* conflict */,
NULL /* work_items */,
scratch_pool));
}
Index: vendor/subversion/dist/subversion/libsvn_wc/wc-checks.h
===================================================================
--- vendor/subversion/dist/subversion/libsvn_wc/wc-checks.h (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_wc/wc-checks.h (revision 286501)
@@ -1,55 +1,55 @@
-/* This file is automatically generated from wc-checks.sql and .dist_sandbox/subversion-1.8.10/subversion/libsvn_wc/token-map.h.
+/* This file is automatically generated from wc-checks.sql and .dist_sandbox/subversion-1.8.14/subversion/libsvn_wc/token-map.h.
* Do not edit this file -- edit the source and rerun gen-make.py */
#define STMT_VERIFICATION_TRIGGERS 0
#define STMT_0_INFO {"STMT_VERIFICATION_TRIGGERS", NULL}
#define STMT_0 \
"CREATE TEMPORARY TRIGGER no_repository_updates BEFORE UPDATE ON repository " \
"BEGIN " \
" SELECT RAISE(FAIL, 'Updates to REPOSITORY are not allowed.'); " \
"END; " \
"CREATE TEMPORARY TRIGGER validation_01 BEFORE INSERT ON nodes " \
"WHEN NOT ((new.local_relpath = '' AND new.parent_relpath IS NULL) " \
" OR (relpath_depth(new.local_relpath) " \
" = relpath_depth(new.parent_relpath) + 1)) " \
"BEGIN " \
" SELECT RAISE(FAIL, 'WC DB validity check 01 failed'); " \
"END; " \
"CREATE TEMPORARY TRIGGER validation_02 BEFORE INSERT ON nodes " \
"WHEN NOT new.op_depth <= relpath_depth(new.local_relpath) " \
"BEGIN " \
" SELECT RAISE(FAIL, 'WC DB validity check 02 failed'); " \
"END; " \
"CREATE TEMPORARY TRIGGER validation_03 BEFORE INSERT ON nodes " \
"WHEN NOT ( " \
" (new.op_depth = relpath_depth(new.local_relpath)) " \
" OR " \
" (EXISTS (SELECT 1 FROM nodes " \
" WHERE wc_id = new.wc_id AND op_depth = new.op_depth " \
" AND local_relpath = new.parent_relpath)) " \
" ) " \
" AND NOT (new.file_external IS NOT NULL AND new.op_depth = 0) " \
"BEGIN " \
" SELECT RAISE(FAIL, 'WC DB validity check 03 failed'); " \
"END; " \
"CREATE TEMPORARY TRIGGER validation_04 BEFORE INSERT ON actual_node " \
"WHEN NOT (new.local_relpath = '' " \
" OR EXISTS (SELECT 1 FROM nodes " \
" WHERE wc_id = new.wc_id " \
" AND local_relpath = new.parent_relpath)) " \
"BEGIN " \
" SELECT RAISE(FAIL, 'WC DB validity check 04 failed'); " \
"END; " \
""
#define WC_CHECKS_SQL_DECLARE_STATEMENTS(varname) \
static const char * const varname[] = { \
STMT_0, \
NULL \
}
#define WC_CHECKS_SQL_DECLARE_STATEMENT_INFO(varname) \
static const char * const varname[][2] = { \
STMT_0_INFO, \
{NULL, NULL} \
}
Index: vendor/subversion/dist/subversion/libsvn_wc/wc-metadata.h
===================================================================
--- vendor/subversion/dist/subversion/libsvn_wc/wc-metadata.h (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_wc/wc-metadata.h (revision 286501)
@@ -1,541 +1,545 @@
-/* This file is automatically generated from wc-metadata.sql and .dist_sandbox/subversion-1.8.10/subversion/libsvn_wc/token-map.h.
+/* This file is automatically generated from wc-metadata.sql and .dist_sandbox/subversion-1.8.14/subversion/libsvn_wc/token-map.h.
* Do not edit this file -- edit the source and rerun gen-make.py */
#define STMT_CREATE_SCHEMA 0
#define STMT_0_INFO {"STMT_CREATE_SCHEMA", NULL}
#define STMT_0 \
"CREATE TABLE REPOSITORY ( " \
" id INTEGER PRIMARY KEY AUTOINCREMENT, " \
" root TEXT UNIQUE NOT NULL, " \
" uuid TEXT NOT NULL " \
" ); " \
"CREATE INDEX I_UUID ON REPOSITORY (uuid); " \
"CREATE INDEX I_ROOT ON REPOSITORY (root); " \
"CREATE TABLE WCROOT ( " \
" id INTEGER PRIMARY KEY AUTOINCREMENT, " \
" local_abspath TEXT UNIQUE " \
" ); " \
"CREATE UNIQUE INDEX I_LOCAL_ABSPATH ON WCROOT (local_abspath); " \
"CREATE TABLE PRISTINE ( " \
" checksum TEXT NOT NULL PRIMARY KEY, " \
" compression INTEGER, " \
" size INTEGER NOT NULL, " \
" refcount INTEGER NOT NULL, " \
" md5_checksum TEXT NOT NULL " \
" ); " \
"CREATE INDEX I_PRISTINE_MD5 ON PRISTINE (md5_checksum); " \
"CREATE TABLE ACTUAL_NODE ( " \
" wc_id INTEGER NOT NULL REFERENCES WCROOT (id), " \
" local_relpath TEXT NOT NULL, " \
" parent_relpath TEXT, " \
" properties BLOB, " \
" conflict_old TEXT, " \
" conflict_new TEXT, " \
" conflict_working TEXT, " \
" prop_reject TEXT, " \
" changelist TEXT, " \
" text_mod TEXT, " \
" tree_conflict_data TEXT, " \
" conflict_data BLOB, " \
" older_checksum TEXT REFERENCES PRISTINE (checksum), " \
" left_checksum TEXT REFERENCES PRISTINE (checksum), " \
" right_checksum TEXT REFERENCES PRISTINE (checksum), " \
" PRIMARY KEY (wc_id, local_relpath) " \
" ); " \
"CREATE UNIQUE INDEX I_ACTUAL_PARENT ON ACTUAL_NODE (wc_id, parent_relpath, " \
" local_relpath); " \
"CREATE TABLE LOCK ( " \
" repos_id INTEGER NOT NULL REFERENCES REPOSITORY (id), " \
" repos_relpath TEXT NOT NULL, " \
" lock_token TEXT NOT NULL, " \
" lock_owner TEXT, " \
" lock_comment TEXT, " \
" lock_date INTEGER, " \
" PRIMARY KEY (repos_id, repos_relpath) " \
" ); " \
"CREATE TABLE WORK_QUEUE ( " \
" id INTEGER PRIMARY KEY AUTOINCREMENT, " \
" work BLOB NOT NULL " \
" ); " \
"CREATE TABLE WC_LOCK ( " \
" wc_id INTEGER NOT NULL REFERENCES WCROOT (id), " \
" local_dir_relpath TEXT NOT NULL, " \
" locked_levels INTEGER NOT NULL DEFAULT -1, " \
" PRIMARY KEY (wc_id, local_dir_relpath) " \
" ); " \
"PRAGMA user_version = " \
APR_STRINGIFY(SVN_WC__VERSION) \
"; " \
""
#define STMT_CREATE_NODES 1
#define STMT_1_INFO {"STMT_CREATE_NODES", NULL}
#define STMT_1 \
"CREATE TABLE NODES ( " \
" wc_id INTEGER NOT NULL REFERENCES WCROOT (id), " \
" local_relpath TEXT NOT NULL, " \
" op_depth INTEGER NOT NULL, " \
" parent_relpath TEXT, " \
" repos_id INTEGER REFERENCES REPOSITORY (id), " \
" repos_path TEXT, " \
" revision INTEGER, " \
" presence TEXT NOT NULL, " \
" moved_here INTEGER, " \
" moved_to TEXT, " \
" kind TEXT NOT NULL, " \
" properties BLOB, " \
" depth TEXT, " \
" checksum TEXT REFERENCES PRISTINE (checksum), " \
" symlink_target TEXT, " \
" changed_revision INTEGER, " \
" changed_date INTEGER, " \
" changed_author TEXT, " \
" translated_size INTEGER, " \
" last_mod_time INTEGER, " \
" dav_cache BLOB, " \
" file_external INTEGER, " \
" inherited_props BLOB, " \
" PRIMARY KEY (wc_id, local_relpath, op_depth) " \
" ); " \
"CREATE UNIQUE INDEX I_NODES_PARENT ON NODES (wc_id, parent_relpath, " \
" local_relpath, op_depth); " \
"CREATE UNIQUE INDEX I_NODES_MOVED ON NODES (wc_id, moved_to, op_depth); " \
"CREATE VIEW NODES_CURRENT AS " \
" SELECT * FROM nodes AS n " \
" WHERE op_depth = (SELECT MAX(op_depth) FROM nodes AS n2 " \
" WHERE n2.wc_id = n.wc_id " \
" AND n2.local_relpath = n.local_relpath); " \
"CREATE VIEW NODES_BASE AS " \
" SELECT * FROM nodes " \
" WHERE op_depth = 0; " \
""
#define STMT_CREATE_NODES_TRIGGERS 2
#define STMT_2_INFO {"STMT_CREATE_NODES_TRIGGERS", NULL}
#define STMT_2 \
"CREATE TRIGGER nodes_insert_trigger " \
"AFTER INSERT ON nodes " \
"WHEN NEW.checksum IS NOT NULL " \
"BEGIN " \
" UPDATE pristine SET refcount = refcount + 1 " \
" WHERE checksum = NEW.checksum; " \
"END; " \
"CREATE TRIGGER nodes_delete_trigger " \
"AFTER DELETE ON nodes " \
"WHEN OLD.checksum IS NOT NULL " \
"BEGIN " \
" UPDATE pristine SET refcount = refcount - 1 " \
" WHERE checksum = OLD.checksum; " \
"END; " \
"CREATE TRIGGER nodes_update_checksum_trigger " \
"AFTER UPDATE OF checksum ON nodes " \
"WHEN NEW.checksum IS NOT OLD.checksum " \
"BEGIN " \
" UPDATE pristine SET refcount = refcount + 1 " \
" WHERE checksum = NEW.checksum; " \
" UPDATE pristine SET refcount = refcount - 1 " \
" WHERE checksum = OLD.checksum; " \
"END; " \
""
#define STMT_CREATE_EXTERNALS 3
#define STMT_3_INFO {"STMT_CREATE_EXTERNALS", NULL}
#define STMT_3 \
"CREATE TABLE EXTERNALS ( " \
" wc_id INTEGER NOT NULL REFERENCES WCROOT (id), " \
" local_relpath TEXT NOT NULL, " \
" parent_relpath TEXT NOT NULL, " \
" repos_id INTEGER NOT NULL REFERENCES REPOSITORY (id), " \
" presence TEXT NOT NULL, " \
" kind TEXT NOT NULL, " \
" def_local_relpath TEXT NOT NULL, " \
" def_repos_relpath TEXT NOT NULL, " \
" def_operational_revision TEXT, " \
" def_revision TEXT, " \
" PRIMARY KEY (wc_id, local_relpath) " \
"); " \
"CREATE UNIQUE INDEX I_EXTERNALS_DEFINED ON EXTERNALS (wc_id, " \
" def_local_relpath, " \
" local_relpath); " \
""
#define STMT_INSTALL_SCHEMA_STATISTICS 4
#define STMT_4_INFO {"STMT_INSTALL_SCHEMA_STATISTICS", NULL}
#define STMT_4 \
"ANALYZE sqlite_master; " \
"DELETE FROM sqlite_stat1 " \
- "WHERE tbl in ('NODES', 'ACTUAL_NODE', 'LOCK', 'WC_LOCK'); " \
- "INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES " \
+ "WHERE tbl in ('NODES', 'ACTUAL_NODE', 'LOCK', 'WC_LOCK', 'EXTERNALS'); " \
+ "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \
" ('NODES', 'sqlite_autoindex_NODES_1', '8000 8000 2 1'); " \
- "INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES " \
+ "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \
" ('NODES', 'I_NODES_PARENT', '8000 8000 10 2 1'); " \
- "INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES " \
+ "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \
" ('NODES', 'I_NODES_MOVED', '8000 8000 1 1'); " \
- "INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES " \
+ "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \
" ('ACTUAL_NODE', 'sqlite_autoindex_ACTUAL_NODE_1', '8000 8000 1'); " \
- "INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES " \
+ "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \
" ('ACTUAL_NODE', 'I_ACTUAL_PARENT', '8000 8000 10 1'); " \
- "INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES " \
+ "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \
" ('LOCK', 'sqlite_autoindex_LOCK_1', '100 100 1'); " \
- "INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES " \
+ "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \
" ('WC_LOCK', 'sqlite_autoindex_WC_LOCK_1', '100 100 1'); " \
+ "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \
+ " ('EXTERNALS','sqlite_autoindex_EXTERNALS_1', '100 100 1'); " \
+ "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \
+ " ('EXTERNALS','I_EXTERNALS_DEFINED', '100 100 3 1'); " \
"ANALYZE sqlite_master; " \
""
#define STMT_UPGRADE_TO_20 5
#define STMT_5_INFO {"STMT_UPGRADE_TO_20", NULL}
#define STMT_5 \
"UPDATE BASE_NODE SET checksum = (SELECT checksum FROM pristine " \
" WHERE md5_checksum = BASE_NODE.checksum) " \
"WHERE EXISTS (SELECT 1 FROM pristine WHERE md5_checksum = BASE_NODE.checksum); " \
"UPDATE WORKING_NODE SET checksum = (SELECT checksum FROM pristine " \
" WHERE md5_checksum = WORKING_NODE.checksum) " \
"WHERE EXISTS (SELECT 1 FROM pristine " \
" WHERE md5_checksum = WORKING_NODE.checksum); " \
"INSERT INTO NODES ( " \
" wc_id, local_relpath, op_depth, parent_relpath, " \
" repos_id, repos_path, revision, " \
" presence, depth, moved_here, moved_to, kind, " \
" changed_revision, changed_date, changed_author, " \
" checksum, properties, translated_size, last_mod_time, " \
" dav_cache, symlink_target, file_external ) " \
"SELECT wc_id, local_relpath, 0 , parent_relpath, " \
" repos_id, repos_relpath, revnum, " \
" presence, depth, NULL , NULL , kind, " \
" changed_rev, changed_date, changed_author, " \
" checksum, properties, translated_size, last_mod_time, " \
" dav_cache, symlink_target, file_external " \
"FROM BASE_NODE; " \
"INSERT INTO NODES ( " \
" wc_id, local_relpath, op_depth, parent_relpath, " \
" repos_id, repos_path, revision, " \
" presence, depth, moved_here, moved_to, kind, " \
" changed_revision, changed_date, changed_author, " \
" checksum, properties, translated_size, last_mod_time, " \
" dav_cache, symlink_target, file_external ) " \
"SELECT wc_id, local_relpath, 2 , parent_relpath, " \
" copyfrom_repos_id, copyfrom_repos_path, copyfrom_revnum, " \
" presence, depth, NULL , NULL , kind, " \
" changed_rev, changed_date, changed_author, " \
" checksum, properties, translated_size, last_mod_time, " \
" NULL , symlink_target, NULL " \
"FROM WORKING_NODE; " \
"DROP TABLE BASE_NODE; " \
"DROP TABLE WORKING_NODE; " \
"PRAGMA user_version = 20; " \
""
#define STMT_UPGRADE_TO_21 6
#define STMT_6_INFO {"STMT_UPGRADE_TO_21", NULL}
#define STMT_6 \
"PRAGMA user_version = 21; " \
""
#define STMT_UPGRADE_21_SELECT_OLD_TREE_CONFLICT 7
#define STMT_7_INFO {"STMT_UPGRADE_21_SELECT_OLD_TREE_CONFLICT", NULL}
#define STMT_7 \
"SELECT wc_id, local_relpath, tree_conflict_data " \
"FROM actual_node " \
"WHERE tree_conflict_data IS NOT NULL " \
""
#define STMT_UPGRADE_21_ERASE_OLD_CONFLICTS 8
#define STMT_8_INFO {"STMT_UPGRADE_21_ERASE_OLD_CONFLICTS", NULL}
#define STMT_8 \
"UPDATE actual_node SET tree_conflict_data = NULL " \
""
#define STMT_UPGRADE_TO_22 9
#define STMT_9_INFO {"STMT_UPGRADE_TO_22", NULL}
#define STMT_9 \
"UPDATE actual_node SET tree_conflict_data = conflict_data; " \
"UPDATE actual_node SET conflict_data = NULL; " \
"PRAGMA user_version = 22; " \
""
#define STMT_UPGRADE_TO_23 10
#define STMT_10_INFO {"STMT_UPGRADE_TO_23", NULL}
#define STMT_10 \
"PRAGMA user_version = 23; " \
""
#define STMT_UPGRADE_23_HAS_WORKING_NODES 11
#define STMT_11_INFO {"STMT_UPGRADE_23_HAS_WORKING_NODES", NULL}
#define STMT_11 \
"SELECT 1 FROM nodes WHERE op_depth > 0 " \
"LIMIT 1 " \
""
#define STMT_UPGRADE_TO_24 12
#define STMT_12_INFO {"STMT_UPGRADE_TO_24", NULL}
#define STMT_12 \
"UPDATE pristine SET refcount = " \
" (SELECT COUNT(*) FROM nodes " \
" WHERE checksum = pristine.checksum ); " \
"PRAGMA user_version = 24; " \
""
#define STMT_UPGRADE_TO_25 13
#define STMT_13_INFO {"STMT_UPGRADE_TO_25", NULL}
#define STMT_13 \
"DROP VIEW IF EXISTS NODES_CURRENT; " \
"CREATE VIEW NODES_CURRENT AS " \
" SELECT * FROM nodes " \
" JOIN (SELECT wc_id, local_relpath, MAX(op_depth) AS op_depth FROM nodes " \
" GROUP BY wc_id, local_relpath) AS filter " \
" ON nodes.wc_id = filter.wc_id " \
" AND nodes.local_relpath = filter.local_relpath " \
" AND nodes.op_depth = filter.op_depth; " \
"PRAGMA user_version = 25; " \
""
#define STMT_UPGRADE_TO_26 14
#define STMT_14_INFO {"STMT_UPGRADE_TO_26", NULL}
#define STMT_14 \
"DROP VIEW IF EXISTS NODES_BASE; " \
"CREATE VIEW NODES_BASE AS " \
" SELECT * FROM nodes " \
" WHERE op_depth = 0; " \
"PRAGMA user_version = 26; " \
""
#define STMT_UPGRADE_TO_27 15
#define STMT_15_INFO {"STMT_UPGRADE_TO_27", NULL}
#define STMT_15 \
"PRAGMA user_version = 27; " \
""
#define STMT_UPGRADE_27_HAS_ACTUAL_NODES_CONFLICTS 16
#define STMT_16_INFO {"STMT_UPGRADE_27_HAS_ACTUAL_NODES_CONFLICTS", NULL}
#define STMT_16 \
"SELECT 1 FROM actual_node " \
"WHERE NOT ((prop_reject IS NULL) AND (conflict_old IS NULL) " \
" AND (conflict_new IS NULL) AND (conflict_working IS NULL) " \
" AND (tree_conflict_data IS NULL)) " \
"LIMIT 1 " \
""
#define STMT_UPGRADE_TO_28 17
#define STMT_17_INFO {"STMT_UPGRADE_TO_28", NULL}
#define STMT_17 \
"UPDATE NODES SET checksum = (SELECT checksum FROM pristine " \
" WHERE md5_checksum = nodes.checksum) " \
"WHERE EXISTS (SELECT 1 FROM pristine WHERE md5_checksum = nodes.checksum); " \
"PRAGMA user_version = 28; " \
""
#define STMT_UPGRADE_TO_29 18
#define STMT_18_INFO {"STMT_UPGRADE_TO_29", NULL}
#define STMT_18 \
"DROP TRIGGER IF EXISTS nodes_update_checksum_trigger; " \
"DROP TRIGGER IF EXISTS nodes_insert_trigger; " \
"DROP TRIGGER IF EXISTS nodes_delete_trigger; " \
"CREATE TRIGGER nodes_update_checksum_trigger " \
"AFTER UPDATE OF checksum ON nodes " \
"WHEN NEW.checksum IS NOT OLD.checksum " \
"BEGIN " \
" UPDATE pristine SET refcount = refcount + 1 " \
" WHERE checksum = NEW.checksum; " \
" UPDATE pristine SET refcount = refcount - 1 " \
" WHERE checksum = OLD.checksum; " \
"END; " \
"CREATE TRIGGER nodes_insert_trigger " \
"AFTER INSERT ON nodes " \
"WHEN NEW.checksum IS NOT NULL " \
"BEGIN " \
" UPDATE pristine SET refcount = refcount + 1 " \
" WHERE checksum = NEW.checksum; " \
"END; " \
"CREATE TRIGGER nodes_delete_trigger " \
"AFTER DELETE ON nodes " \
"WHEN OLD.checksum IS NOT NULL " \
"BEGIN " \
" UPDATE pristine SET refcount = refcount - 1 " \
" WHERE checksum = OLD.checksum; " \
"END; " \
"PRAGMA user_version = 29; " \
""
#define STMT_UPGRADE_TO_30 19
#define STMT_19_INFO {"STMT_UPGRADE_TO_30", NULL}
#define STMT_19 \
"CREATE UNIQUE INDEX IF NOT EXISTS I_NODES_MOVED " \
"ON NODES (wc_id, moved_to, op_depth); " \
"CREATE INDEX IF NOT EXISTS I_PRISTINE_MD5 ON PRISTINE (md5_checksum); " \
"UPDATE nodes SET presence = \"server-excluded\" WHERE presence = \"absent\"; " \
"UPDATE nodes SET file_external=1 WHERE file_external IS NOT NULL; " \
""
#define STMT_UPGRADE_30_SELECT_CONFLICT_SEPARATE 20
#define STMT_20_INFO {"STMT_UPGRADE_30_SELECT_CONFLICT_SEPARATE", NULL}
#define STMT_20 \
"SELECT wc_id, local_relpath, " \
" conflict_old, conflict_working, conflict_new, prop_reject, tree_conflict_data " \
"FROM actual_node " \
"WHERE conflict_old IS NOT NULL " \
" OR conflict_working IS NOT NULL " \
" OR conflict_new IS NOT NULL " \
" OR prop_reject IS NOT NULL " \
" OR tree_conflict_data IS NOT NULL " \
"ORDER by wc_id, local_relpath " \
""
#define STMT_UPGRADE_30_SET_CONFLICT 21
#define STMT_21_INFO {"STMT_UPGRADE_30_SET_CONFLICT", NULL}
#define STMT_21 \
"UPDATE actual_node SET conflict_data = ?3, conflict_old = NULL, " \
" conflict_working = NULL, conflict_new = NULL, prop_reject = NULL, " \
" tree_conflict_data = NULL " \
"WHERE wc_id = ?1 and local_relpath = ?2 " \
""
#define STMT_UPGRADE_TO_31_ALTER_TABLE 22
#define STMT_22_INFO {"STMT_UPGRADE_TO_31_ALTER_TABLE", NULL}
#define STMT_22 \
"ALTER TABLE NODES ADD COLUMN inherited_props BLOB; " \
""
#define STMT_UPGRADE_TO_31_FINALIZE 23
#define STMT_23_INFO {"STMT_UPGRADE_TO_31_FINALIZE", NULL}
#define STMT_23 \
"DROP INDEX IF EXISTS I_ACTUAL_CHANGELIST; " \
"DROP INDEX IF EXISTS I_EXTERNALS_PARENT; " \
"DROP INDEX I_NODES_PARENT; " \
"CREATE UNIQUE INDEX I_NODES_PARENT ON NODES (wc_id, parent_relpath, " \
" local_relpath, op_depth); " \
"DROP INDEX I_ACTUAL_PARENT; " \
"CREATE UNIQUE INDEX I_ACTUAL_PARENT ON ACTUAL_NODE (wc_id, parent_relpath, " \
" local_relpath); " \
"PRAGMA user_version = 31; " \
""
#define STMT_UPGRADE_31_SELECT_WCROOT_NODES 24
#define STMT_24_INFO {"STMT_UPGRADE_31_SELECT_WCROOT_NODES", NULL}
#define STMT_24 \
"SELECT l.wc_id, l.local_relpath FROM nodes as l " \
"LEFT OUTER JOIN nodes as r " \
"ON l.wc_id = r.wc_id " \
" AND r.local_relpath = l.parent_relpath " \
" AND r.op_depth = 0 " \
"WHERE l.op_depth = 0 " \
" AND l.repos_path != '' " \
" AND ((l.repos_id IS NOT r.repos_id) " \
" OR (l.repos_path IS NOT (CASE WHEN (r.local_relpath) = '' THEN (CASE WHEN (r.repos_path) = '' THEN (l.local_relpath) WHEN (l.local_relpath) = '' THEN (r.repos_path) ELSE (r.repos_path) || '/' || (l.local_relpath) END) WHEN (r.repos_path) = '' THEN (CASE WHEN (r.local_relpath) = '' THEN (l.local_relpath) WHEN SUBSTR((l.local_relpath), 1, LENGTH(r.local_relpath)) = (r.local_relpath) THEN CASE WHEN LENGTH(r.local_relpath) = LENGTH(l.local_relpath) THEN '' WHEN SUBSTR((l.local_relpath), LENGTH(r.local_relpath)+1, 1) = '/' THEN SUBSTR((l.local_relpath), LENGTH(r.local_relpath)+2) END END) WHEN SUBSTR((l.local_relpath), 1, LENGTH(r.local_relpath)) = (r.local_relpath) THEN CASE WHEN LENGTH(r.local_relpath) = LENGTH(l.local_relpath) THEN (r.repos_path) WHEN SUBSTR((l.local_relpath), LENGTH(r.local_relpath)+1, 1) = '/' THEN (r.repos_path) || SUBSTR((l.local_relpath), LENGTH(r.local_relpath)+1) END END))) " \
""
#define STMT_UPGRADE_TO_32 25
#define STMT_25_INFO {"STMT_UPGRADE_TO_32", NULL}
#define STMT_25 \
"DROP INDEX IF EXISTS I_ACTUAL_CHANGELIST; " \
"DROP INDEX IF EXISTS I_EXTERNALS_PARENT; " \
"CREATE INDEX I_EXTERNALS_PARENT ON EXTERNALS (wc_id, parent_relpath); " \
"DROP INDEX I_NODES_PARENT; " \
"CREATE UNIQUE INDEX I_NODES_PARENT ON NODES (wc_id, parent_relpath, " \
" local_relpath, op_depth); " \
"DROP INDEX I_ACTUAL_PARENT; " \
"CREATE UNIQUE INDEX I_ACTUAL_PARENT ON ACTUAL_NODE (wc_id, parent_relpath, " \
" local_relpath); " \
"-- format: YYY " \
""
#define WC_METADATA_SQL_99 \
"CREATE TABLE ACTUAL_NODE_BACKUP ( " \
" wc_id INTEGER NOT NULL, " \
" local_relpath TEXT NOT NULL, " \
" parent_relpath TEXT, " \
" properties BLOB, " \
" conflict_old TEXT, " \
" conflict_new TEXT, " \
" conflict_working TEXT, " \
" prop_reject TEXT, " \
" changelist TEXT, " \
" text_mod TEXT " \
" ); " \
"INSERT INTO ACTUAL_NODE_BACKUP SELECT " \
" wc_id, local_relpath, parent_relpath, properties, conflict_old, " \
" conflict_new, conflict_working, prop_reject, changelist, text_mod " \
"FROM ACTUAL_NODE; " \
"DROP TABLE ACTUAL_NODE; " \
"CREATE TABLE ACTUAL_NODE ( " \
" wc_id INTEGER NOT NULL REFERENCES WCROOT (id), " \
" local_relpath TEXT NOT NULL, " \
" parent_relpath TEXT, " \
" properties BLOB, " \
" conflict_old TEXT, " \
" conflict_new TEXT, " \
" conflict_working TEXT, " \
" prop_reject TEXT, " \
" changelist TEXT, " \
" text_mod TEXT, " \
" PRIMARY KEY (wc_id, local_relpath) " \
" ); " \
"CREATE UNIQUE INDEX I_ACTUAL_PARENT ON ACTUAL_NODE (wc_id, parent_relpath, " \
" local_relpath); " \
"INSERT INTO ACTUAL_NODE SELECT " \
" wc_id, local_relpath, parent_relpath, properties, conflict_old, " \
" conflict_new, conflict_working, prop_reject, changelist, text_mod " \
"FROM ACTUAL_NODE_BACKUP; " \
"DROP TABLE ACTUAL_NODE_BACKUP; " \
""
#define WC_METADATA_SQL_DECLARE_STATEMENTS(varname) \
static const char * const varname[] = { \
STMT_0, \
STMT_1, \
STMT_2, \
STMT_3, \
STMT_4, \
STMT_5, \
STMT_6, \
STMT_7, \
STMT_8, \
STMT_9, \
STMT_10, \
STMT_11, \
STMT_12, \
STMT_13, \
STMT_14, \
STMT_15, \
STMT_16, \
STMT_17, \
STMT_18, \
STMT_19, \
STMT_20, \
STMT_21, \
STMT_22, \
STMT_23, \
STMT_24, \
STMT_25, \
NULL \
}
#define WC_METADATA_SQL_DECLARE_STATEMENT_INFO(varname) \
static const char * const varname[][2] = { \
STMT_0_INFO, \
STMT_1_INFO, \
STMT_2_INFO, \
STMT_3_INFO, \
STMT_4_INFO, \
STMT_5_INFO, \
STMT_6_INFO, \
STMT_7_INFO, \
STMT_8_INFO, \
STMT_9_INFO, \
STMT_10_INFO, \
STMT_11_INFO, \
STMT_12_INFO, \
STMT_13_INFO, \
STMT_14_INFO, \
STMT_15_INFO, \
STMT_16_INFO, \
STMT_17_INFO, \
STMT_18_INFO, \
STMT_19_INFO, \
STMT_20_INFO, \
STMT_21_INFO, \
STMT_22_INFO, \
STMT_23_INFO, \
STMT_24_INFO, \
STMT_25_INFO, \
{NULL, NULL} \
}
Index: vendor/subversion/dist/subversion/libsvn_wc/wc-metadata.sql
===================================================================
--- vendor/subversion/dist/subversion/libsvn_wc/wc-metadata.sql (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_wc/wc-metadata.sql (revision 286501)
@@ -1,1002 +1,1007 @@
/* wc-metadata.sql -- schema used in the wc-metadata SQLite database
* This is intended for use with SQLite 3
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
/*
* the KIND column in these tables has one of the following values
* (documented in the corresponding C type #svn_kind_t):
* "file"
* "dir"
* "symlink"
* "unknown"
*
* the PRESENCE column in these tables has one of the following values
* (see also the C type #svn_wc__db_status_t):
* "normal"
* "server-excluded" -- server has declared it excluded (ie. authz failure)
* "excluded" -- administratively excluded (ie. sparse WC)
* "not-present" -- node not present at this REV
* "incomplete" -- state hasn't been filled in
* "base-deleted" -- node represents a delete of a BASE node
*/
/* One big list of statements to create our (current) schema. */
-- STMT_CREATE_SCHEMA
/* ------------------------------------------------------------------------- */
CREATE TABLE REPOSITORY (
id INTEGER PRIMARY KEY AUTOINCREMENT,
/* The root URL of the repository. This value is URI-encoded. */
root TEXT UNIQUE NOT NULL,
/* the UUID of the repository */
uuid TEXT NOT NULL
);
/* Note: a repository (identified by its UUID) may appear at multiple URLs.
For example, http://example.com/repos/ and https://example.com/repos/. */
CREATE INDEX I_UUID ON REPOSITORY (uuid);
CREATE INDEX I_ROOT ON REPOSITORY (root);
/* ------------------------------------------------------------------------- */
CREATE TABLE WCROOT (
id INTEGER PRIMARY KEY AUTOINCREMENT,
/* absolute path in the local filesystem. NULL if storing metadata in
the wcroot itself. */
local_abspath TEXT UNIQUE
);
CREATE UNIQUE INDEX I_LOCAL_ABSPATH ON WCROOT (local_abspath);
/* ------------------------------------------------------------------------- */
/* The PRISTINE table keeps track of pristine texts. Each row describes a
single pristine text. The text itself is stored in a file whose name is
derived from the 'checksum' column. Each pristine text is referenced by
any number of rows in the NODES and ACTUAL_NODE tables.
In future, the pristine text file may be compressed.
*/
CREATE TABLE PRISTINE (
/* The SHA-1 checksum of the pristine text. This is a unique key. The
SHA-1 checksum of a pristine text is assumed to be unique among all
pristine texts referenced from this database. */
checksum TEXT NOT NULL PRIMARY KEY,
/* Enumerated values specifying type of compression. The only value
supported so far is NULL, meaning that no compression has been applied
and the pristine text is stored verbatim in the file. */
compression INTEGER,
/* The size in bytes of the file in which the pristine text is stored.
Used to verify the pristine file is "proper". */
size INTEGER NOT NULL,
/* The number of rows in the NODES table that have a 'checksum' column
value that refers to this row. (References in other places, such as
in the ACTUAL_NODE table, are not counted.) */
refcount INTEGER NOT NULL,
/* Alternative MD5 checksum used for communicating with older
repositories. Not strictly guaranteed to be unique among table rows. */
md5_checksum TEXT NOT NULL
);
CREATE INDEX I_PRISTINE_MD5 ON PRISTINE (md5_checksum);
/* ------------------------------------------------------------------------- */
/* The ACTUAL_NODE table describes text changes and property changes
on each node in the WC, relative to the NODES table row for the
same path. (A NODES row must exist if this node exists, but an
ACTUAL_NODE row can exist on its own if it is just recording info
on a non-present node - a tree conflict or a changelist, for
example.)
The ACTUAL_NODE table row for a given path exists if the node at that
path is known to have text or property changes relative to its
NODES row. ("Is known" because a text change on disk may not yet
have been discovered and recorded here.)
The ACTUAL_NODE table row for a given path may also exist in other cases,
including if the "changelist" or any of the conflict columns have a
non-null value.
*/
CREATE TABLE ACTUAL_NODE (
/* specifies the location of this node in the local filesystem */
wc_id INTEGER NOT NULL REFERENCES WCROOT (id),
local_relpath TEXT NOT NULL,
/* parent's local_relpath for aggregating children of a given parent.
this will be "" if the parent is the wcroot. NULL if this is the
wcroot node. */
parent_relpath TEXT,
/* serialized skel of this node's properties. NULL implies no change to
the properties, relative to WORKING/BASE as appropriate. */
properties BLOB,
/* relpaths of the conflict files. */
/* ### These columns will eventually be merged into conflict_data below. */
conflict_old TEXT,
conflict_new TEXT,
conflict_working TEXT,
prop_reject TEXT,
/* if not NULL, this node is part of a changelist. */
changelist TEXT,
/* ### need to determine values. "unknown" (no info), "admin" (they
### used something like 'svn edit'), "noticed" (saw a mod while
### scanning the filesystem). */
text_mod TEXT,
/* if a directory, serialized data for all of tree conflicts therein.
### This column will eventually be merged into the conflict_data column,
### but within the ACTUAL node of the tree conflict victim itself, rather
### than the node of the tree conflict victim's parent directory. */
tree_conflict_data TEXT,
/* A skel containing the conflict details. */
conflict_data BLOB,
/* Three columns containing the checksums of older, left and right conflict
texts. Stored in a column to allow storing them in the pristine store */
/* stsp: This is meant for text conflicts, right? What about property
conflicts? Why do we need these in a column to refer to the
pristine store? Can't we just parse the checksums from
conflict_data as well?
rhuijben: Because that won't allow triggers to handle refcounts.
We would have to scan all conflict skels before cleaning up the
a single file from the pristine stor */
older_checksum TEXT REFERENCES PRISTINE (checksum),
left_checksum TEXT REFERENCES PRISTINE (checksum),
right_checksum TEXT REFERENCES PRISTINE (checksum),
PRIMARY KEY (wc_id, local_relpath)
);
CREATE UNIQUE INDEX I_ACTUAL_PARENT ON ACTUAL_NODE (wc_id, parent_relpath,
local_relpath);
/* ------------------------------------------------------------------------- */
/* This table is a cache of information about repository locks. */
CREATE TABLE LOCK (
/* what repository location is locked */
repos_id INTEGER NOT NULL REFERENCES REPOSITORY (id),
repos_relpath TEXT NOT NULL,
/* Information about the lock. Note: these values are just caches from
the server, and are not authoritative. */
lock_token TEXT NOT NULL,
/* ### make the following fields NOT NULL ? */
lock_owner TEXT,
lock_comment TEXT,
lock_date INTEGER, /* an APR date/time (usec since 1970) */
PRIMARY KEY (repos_id, repos_relpath)
);
/* ------------------------------------------------------------------------- */
CREATE TABLE WORK_QUEUE (
/* Work items are identified by this value. */
id INTEGER PRIMARY KEY AUTOINCREMENT,
/* A serialized skel specifying the work item. */
work BLOB NOT NULL
);
/* ------------------------------------------------------------------------- */
CREATE TABLE WC_LOCK (
/* specifies the location of this node in the local filesystem */
wc_id INTEGER NOT NULL REFERENCES WCROOT (id),
local_dir_relpath TEXT NOT NULL,
locked_levels INTEGER NOT NULL DEFAULT -1,
PRIMARY KEY (wc_id, local_dir_relpath)
);
PRAGMA user_version =
-- define: SVN_WC__VERSION
;
/* ------------------------------------------------------------------------- */
/* The NODES table describes the way WORKING nodes are layered on top of
BASE nodes and on top of other WORKING nodes, due to nested tree structure
changes. The layers are modelled using the "op_depth" column.
An 'operation depth' refers to the number of directory levels down from
the WC root at which a tree-change operation (delete, add?, copy, move)
was performed. A row's 'op_depth' does NOT refer to the depth of its own
'local_relpath', but rather to the depth of the nearest tree change that
affects that node.
The row with op_depth=0 for any given local relpath represents the "base"
node that is created and updated by checkout, update, switch and commit
post-processing. The row with the highest op_depth for a particular
local_relpath represents the working version. Any rows with intermediate
op_depth values are not normally visible to the user but may become
visible after reverting local changes.
This table contains full node descriptions for nodes in either the BASE
or WORKING trees as described in notes/wc-ng/design. Fields relate
both to BASE and WORKING trees, unless documented otherwise.
For illustration, with a scenario like this:
# (0)
svn rm foo
svn cp ^/moo foo # (1)
svn rm foo/bar
touch foo/bar
svn add foo/bar # (2)
, these are the NODES table rows for the path foo/bar:
(0) "BASE" ---> NODES (op_depth == 0)
(1) NODES (op_depth == 1)
(2) NODES (op_depth == 2)
0 is the original data for foo/bar before 'svn rm foo' (if it existed).
1 is the data for foo/bar copied in from ^/moo/bar.
2 is the to-be-committed data for foo/bar, created by 'svn add foo/bar'.
An 'svn revert foo/bar' would remove the NODES of (2).
*/
-- STMT_CREATE_NODES
CREATE TABLE NODES (
/* Working copy location related fields */
wc_id INTEGER NOT NULL REFERENCES WCROOT (id),
local_relpath TEXT NOT NULL,
/* Contains the depth (= number of path segments) of the operation
modifying the working copy tree structure. All nodes below the root
of the operation (aka operation root, aka oproot) affected by the
operation will be assigned the same op_depth.
op_depth == 0 designates the initial checkout; the BASE tree.
*/
op_depth INTEGER NOT NULL,
/* parent's local_relpath for aggregating children of a given parent.
this will be "" if the parent is the wcroot. Since a wcroot will
never have a WORKING node the parent_relpath will never be null,
except when op_depth == 0 and the node is a wcroot. */
parent_relpath TEXT,
/* Repository location fields */
/* When op_depth == 0, these fields refer to the repository location of the
BASE node, the location of the initial checkout.
When op_depth != 0, they indicate where this node was copied/moved from.
In this case, the fields are set for the root of the operation and for all
children. */
repos_id INTEGER REFERENCES REPOSITORY (id),
repos_path TEXT,
revision INTEGER,
/* WC state fields */
/* The tree state of the node.
In case 'op_depth' is equal to 0, this node is part of the 'BASE'
tree. The 'BASE' represents pristine nodes that are in the
repository; it is obtained and modified by commands such as
checkout/update/switch.
In case 'op_depth' is greater than 0, this node is part of a
layer of working nodes. The 'WORKING' tree is obtained and
modified by commands like delete/copy/revert.
The 'BASE' and 'WORKING' trees use the same literal values for
the 'presence' but the meaning of each value can vary depending
on the tree.
normal: in the 'BASE' tree this is an ordinary node for which we
have full information. In the 'WORKING' tree it's an added or
copied node for which we have full information.
not-present: in the 'BASE' tree this is a node that is implied to
exist by the parent node, but is not present in the working
copy. Typically obtained by delete/commit, or by update to
revision in which the node does not exist. In the 'WORKING'
tree this is a copy of a 'not-present' node from the 'BASE'
tree, and it will be deleted on commit. Such a node cannot be
copied directly, but can be copied as a descendant.
incomplete: in the 'BASE' tree this is an ordinary node for which
we do not have full information. Only the name is guaranteed;
we may not have all its children, we may not have its checksum,
etc. In the 'WORKING' tree this is a copied node for which we
do not have the full information. This state is generally
obtained when an operation was interrupted.
base-deleted: not valid in 'BASE' tree. In the 'WORKING' tree
this represents a node that is deleted from the tree below the
current 'op_depth'. This state is badly named, it should be
something like 'deleted'.
server-excluded: in the 'BASE' tree this is a node that is excluded by
authz. The name of the node is known from the parent, but no
other information is available. Not valid in the 'WORKING'
tree as there is no way to commit such a node.
excluded: in the 'BASE' tree this node is administratively
excluded by the user (sparse WC). In the 'WORKING' tree this
is a copy of an excluded node from the 'BASE' tree. Such a
node cannot be copied directly but can be copied as a
descendant. */
presence TEXT NOT NULL,
/* ### JF: For an old-style move, "copyfrom" info stores its source, but a
new WC-NG "move" is intended to be a "true rename" so its copyfrom
revision is implicit, being in effect (new head - 1) at commit time.
For a (new) move, we need to store or deduce the copyfrom local-relpath;
perhaps add a column called "moved_from". */
/* Boolean value, specifying if this node was moved here (rather than just
copied). This is set on all the nodes in the moved tree. The source of
the move is implied by a different node with a moved_to column pointing
at the root node of the moved tree. */
moved_here INTEGER,
/* If the underlying node was moved away (rather than just deleted), this
specifies the local_relpath of where the node was moved to.
This is set only on the root of a move, and is NULL for all children.
The op-depth of the moved-to node is not recorded. A moved_to path
always points at a node within the highest op-depth layer at the
destination. This invariant must be maintained by operations which
change existing move information. */
moved_to TEXT,
/* Content fields */
/* the kind of the new node. may be "unknown" if the node is not present. */
kind TEXT NOT NULL,
/* serialized skel of this node's properties (when presence is 'normal' or
'incomplete'); an empty skel or NULL indicates no properties. NULL if
we have no information about the properties (any other presence).
TODO: Choose & require a single representation for 'no properties'.
*/
properties BLOB,
/* NULL depth means "default" (typically svn_depth_infinity) */
/* ### depth on WORKING? seems this is a BASE-only concept. how do
### you do "files" on an added-directory? can't really ignore
### the subdirs! */
/* ### maybe a WC-to-WC copy can retain a depth? */
depth TEXT,
/* The SHA-1 checksum of the pristine text, if this node is a file and was
moved here or copied here, else NULL. */
checksum TEXT REFERENCES PRISTINE (checksum),
/* for kind==symlink, this specifies the target. */
symlink_target TEXT,
/* Last-Change fields */
/* If this node was moved here or copied here, then the following fields may
have information about their source node. changed_rev must be not-null
if this node has presence=="normal". changed_date and changed_author may
be null if the corresponding revprops are missing.
For an added or not-present node, these are null. */
changed_revision INTEGER,
changed_date INTEGER, /* an APR date/time (usec since 1970) */
changed_author TEXT,
/* Various cache fields */
/* The size in bytes of the working file when it had no local text
modifications. This means the size of the text when translated from
repository-normal format to working copy format with EOL style
translated and keywords expanded according to the properties in the
"properties" column of this row.
NULL if this node is not a file or if the size has not (yet) been
computed. */
translated_size INTEGER,
/* The mod-time of the working file when it was last determined to be
logically unmodified relative to its base, taking account of keywords
and EOL style. This value is used in the change detection heuristic
used by the status command.
NULL if this node is not a file or if this info has not yet been
determined.
*/
last_mod_time INTEGER, /* an APR date/time (usec since 1970) */
/* serialized skel of this node's dav-cache. could be NULL if the
node does not have any dav-cache. */
dav_cache BLOB,
/* Is there a file external in this location. NULL if there
is no file external, otherwise '1' */
/* ### Originally we had a wc-1.0 like skel in this place, so we
### check for NULL.
### In Subversion 1.7 we defined this column as TEXT, but Sqlite
### only uses this information for deciding how to optimize
### anyway. */
file_external INTEGER,
/* serialized skel of this node's inherited properties. NULL if this
is not the BASE of a WC root node. */
inherited_props BLOB,
PRIMARY KEY (wc_id, local_relpath, op_depth)
);
CREATE UNIQUE INDEX I_NODES_PARENT ON NODES (wc_id, parent_relpath,
local_relpath, op_depth);
/* I_NODES_MOVED is introduced in format 30 */
CREATE UNIQUE INDEX I_NODES_MOVED ON NODES (wc_id, moved_to, op_depth);
/* Many queries have to filter the nodes table to pick only that version
of each node with the highest (most "current") op_depth. This view
does the heavy lifting for such queries.
Note that this view includes a row for each and every path that is known
in the WC, including, for example, paths that were children of a base- or
lower-op-depth directory that has been replaced by something else in the
current view.
*/
CREATE VIEW NODES_CURRENT AS
SELECT * FROM nodes AS n
WHERE op_depth = (SELECT MAX(op_depth) FROM nodes AS n2
WHERE n2.wc_id = n.wc_id
AND n2.local_relpath = n.local_relpath);
/* Many queries have to filter the nodes table to pick only that version
of each node with the BASE ("as checked out") op_depth. This view
does the heavy lifting for such queries. */
CREATE VIEW NODES_BASE AS
SELECT * FROM nodes
WHERE op_depth = 0;
-- STMT_CREATE_NODES_TRIGGERS
CREATE TRIGGER nodes_insert_trigger
AFTER INSERT ON nodes
WHEN NEW.checksum IS NOT NULL
BEGIN
UPDATE pristine SET refcount = refcount + 1
WHERE checksum = NEW.checksum;
END;
CREATE TRIGGER nodes_delete_trigger
AFTER DELETE ON nodes
WHEN OLD.checksum IS NOT NULL
BEGIN
UPDATE pristine SET refcount = refcount - 1
WHERE checksum = OLD.checksum;
END;
CREATE TRIGGER nodes_update_checksum_trigger
AFTER UPDATE OF checksum ON nodes
WHEN NEW.checksum IS NOT OLD.checksum
/* AND (NEW.checksum IS NOT NULL OR OLD.checksum IS NOT NULL) */
BEGIN
UPDATE pristine SET refcount = refcount + 1
WHERE checksum = NEW.checksum;
UPDATE pristine SET refcount = refcount - 1
WHERE checksum = OLD.checksum;
END;
-- STMT_CREATE_EXTERNALS
CREATE TABLE EXTERNALS (
/* Working copy location related fields (like NODES)*/
wc_id INTEGER NOT NULL REFERENCES WCROOT (id),
local_relpath TEXT NOT NULL,
/* The working copy root can't be recorded as an external in itself
so this will never be NULL. ### ATM only inserted, never queried */
parent_relpath TEXT NOT NULL,
/* Repository location fields */
repos_id INTEGER NOT NULL REFERENCES REPOSITORY (id),
/* Either MAP_NORMAL or MAP_EXCLUDED */
presence TEXT NOT NULL,
/* the kind of the external. */
kind TEXT NOT NULL,
/* The local relpath of the directory NODE defining this external
(Defaults to the parent directory of the file external after upgrade) */
def_local_relpath TEXT NOT NULL,
/* The url of the external as used in the definition */
def_repos_relpath TEXT NOT NULL,
/* The operational (peg) and node revision if this is a revision fixed
external; otherwise NULL. (Usually these will both have the same value) */
def_operational_revision TEXT,
def_revision TEXT,
PRIMARY KEY (wc_id, local_relpath)
);
CREATE UNIQUE INDEX I_EXTERNALS_DEFINED ON EXTERNALS (wc_id,
def_local_relpath,
local_relpath);
/* ------------------------------------------------------------------------- */
/* This statement provides SQLite with the necessary information about our
indexes to make better decisions in the query planner.
For every interesting index this contains a number of rows where the
statistics ar calculated for and then for every column in the index the
average number of rows with the same value in all columns left of this
column including the column itself.
See http://www.sqlite.org/fileformat2.html#stat1tab for more details.
The important thing here is that this tells Sqlite that the wc_id column
of the NODES and ACTUAL_NODE table is usually a single value, so queries
should use more than one column for index usage.
The current hints describe NODES+ACTUAL_NODE as a working copy with
8000 nodes in 1 a single working copy(=wc_id), 10 nodes per directory
and an average of 2 op-depth layers per node.
The number of integers must be number of index columns + 1, which is
verified via the test_schema_statistics() test.
*/
-- STMT_INSTALL_SCHEMA_STATISTICS
ANALYZE sqlite_master; /* Creates empty sqlite_stat1 if necessary */
DELETE FROM sqlite_stat1
-WHERE tbl in ('NODES', 'ACTUAL_NODE', 'LOCK', 'WC_LOCK');
+WHERE tbl in ('NODES', 'ACTUAL_NODE', 'LOCK', 'WC_LOCK', 'EXTERNALS');
-INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES
+INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES
('NODES', 'sqlite_autoindex_NODES_1', '8000 8000 2 1');
-INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES
+INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES
('NODES', 'I_NODES_PARENT', '8000 8000 10 2 1');
/* Tell a lie: We ignore that 99.9% of all moved_to values are NULL */
-INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES
+INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES
('NODES', 'I_NODES_MOVED', '8000 8000 1 1');
-INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES
+INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES
('ACTUAL_NODE', 'sqlite_autoindex_ACTUAL_NODE_1', '8000 8000 1');
-INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES
+INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES
('ACTUAL_NODE', 'I_ACTUAL_PARENT', '8000 8000 10 1');
-INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES
+INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES
('LOCK', 'sqlite_autoindex_LOCK_1', '100 100 1');
-INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES
+INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES
('WC_LOCK', 'sqlite_autoindex_WC_LOCK_1', '100 100 1');
+
+INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES
+ ('EXTERNALS','sqlite_autoindex_EXTERNALS_1', '100 100 1');
+INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES
+ ('EXTERNALS','I_EXTERNALS_DEFINED', '100 100 3 1');
/* sqlite_autoindex_WORK_QUEUE_1 doesn't exist because WORK_QUEUE is
a INTEGER PRIMARY KEY AUTOINCREMENT table */
ANALYZE sqlite_master; /* Loads sqlite_stat1 data for query optimizer */
/* ------------------------------------------------------------------------- */
/* Format 20 introduces NODES and removes BASE_NODE and WORKING_NODE */
-- STMT_UPGRADE_TO_20
UPDATE BASE_NODE SET checksum = (SELECT checksum FROM pristine
WHERE md5_checksum = BASE_NODE.checksum)
WHERE EXISTS (SELECT 1 FROM pristine WHERE md5_checksum = BASE_NODE.checksum);
UPDATE WORKING_NODE SET checksum = (SELECT checksum FROM pristine
WHERE md5_checksum = WORKING_NODE.checksum)
WHERE EXISTS (SELECT 1 FROM pristine
WHERE md5_checksum = WORKING_NODE.checksum);
INSERT INTO NODES (
wc_id, local_relpath, op_depth, parent_relpath,
repos_id, repos_path, revision,
presence, depth, moved_here, moved_to, kind,
changed_revision, changed_date, changed_author,
checksum, properties, translated_size, last_mod_time,
dav_cache, symlink_target, file_external )
SELECT wc_id, local_relpath, 0 /*op_depth*/, parent_relpath,
repos_id, repos_relpath, revnum,
presence, depth, NULL /*moved_here*/, NULL /*moved_to*/, kind,
changed_rev, changed_date, changed_author,
checksum, properties, translated_size, last_mod_time,
dav_cache, symlink_target, file_external
FROM BASE_NODE;
INSERT INTO NODES (
wc_id, local_relpath, op_depth, parent_relpath,
repos_id, repos_path, revision,
presence, depth, moved_here, moved_to, kind,
changed_revision, changed_date, changed_author,
checksum, properties, translated_size, last_mod_time,
dav_cache, symlink_target, file_external )
SELECT wc_id, local_relpath, 2 /*op_depth*/, parent_relpath,
copyfrom_repos_id, copyfrom_repos_path, copyfrom_revnum,
presence, depth, NULL /*moved_here*/, NULL /*moved_to*/, kind,
changed_rev, changed_date, changed_author,
checksum, properties, translated_size, last_mod_time,
NULL /*dav_cache*/, symlink_target, NULL /*file_external*/
FROM WORKING_NODE;
DROP TABLE BASE_NODE;
DROP TABLE WORKING_NODE;
PRAGMA user_version = 20;
/* ------------------------------------------------------------------------- */
/* Format 21 involves no schema changes, it moves the tree conflict victim
information to victime nodes, rather than parents. */
-- STMT_UPGRADE_TO_21
PRAGMA user_version = 21;
/* For format 21 bump code */
-- STMT_UPGRADE_21_SELECT_OLD_TREE_CONFLICT
SELECT wc_id, local_relpath, tree_conflict_data
FROM actual_node
WHERE tree_conflict_data IS NOT NULL
/* For format 21 bump code */
-- STMT_UPGRADE_21_ERASE_OLD_CONFLICTS
UPDATE actual_node SET tree_conflict_data = NULL
/* ------------------------------------------------------------------------- */
/* Format 22 simply moves the tree conflict information from the conflict_data
column to the tree_conflict_data column. */
-- STMT_UPGRADE_TO_22
UPDATE actual_node SET tree_conflict_data = conflict_data;
UPDATE actual_node SET conflict_data = NULL;
PRAGMA user_version = 22;
/* ------------------------------------------------------------------------- */
/* Format 23 involves no schema changes, it introduces multi-layer
op-depth processing for NODES. */
-- STMT_UPGRADE_TO_23
PRAGMA user_version = 23;
-- STMT_UPGRADE_23_HAS_WORKING_NODES
SELECT 1 FROM nodes WHERE op_depth > 0
LIMIT 1
/* ------------------------------------------------------------------------- */
/* Format 24 involves no schema changes; it starts using the pristine
table's refcount column correctly. */
-- STMT_UPGRADE_TO_24
UPDATE pristine SET refcount =
(SELECT COUNT(*) FROM nodes
WHERE checksum = pristine.checksum /*OR checksum = pristine.md5_checksum*/);
PRAGMA user_version = 24;
/* ------------------------------------------------------------------------- */
/* Format 25 introduces the NODES_CURRENT view. */
-- STMT_UPGRADE_TO_25
DROP VIEW IF EXISTS NODES_CURRENT;
CREATE VIEW NODES_CURRENT AS
SELECT * FROM nodes
JOIN (SELECT wc_id, local_relpath, MAX(op_depth) AS op_depth FROM nodes
GROUP BY wc_id, local_relpath) AS filter
ON nodes.wc_id = filter.wc_id
AND nodes.local_relpath = filter.local_relpath
AND nodes.op_depth = filter.op_depth;
PRAGMA user_version = 25;
/* ------------------------------------------------------------------------- */
/* Format 26 introduces the NODES_BASE view. */
-- STMT_UPGRADE_TO_26
DROP VIEW IF EXISTS NODES_BASE;
CREATE VIEW NODES_BASE AS
SELECT * FROM nodes
WHERE op_depth = 0;
PRAGMA user_version = 26;
/* ------------------------------------------------------------------------- */
/* Format 27 involves no schema changes, it introduces stores
conflict files as relpaths rather than names in ACTUAL_NODE. */
-- STMT_UPGRADE_TO_27
PRAGMA user_version = 27;
/* For format 27 bump code */
-- STMT_UPGRADE_27_HAS_ACTUAL_NODES_CONFLICTS
SELECT 1 FROM actual_node
WHERE NOT ((prop_reject IS NULL) AND (conflict_old IS NULL)
AND (conflict_new IS NULL) AND (conflict_working IS NULL)
AND (tree_conflict_data IS NULL))
LIMIT 1
/* ------------------------------------------------------------------------- */
/* Format 28 involves no schema changes, it only converts MD5 pristine
references to SHA1. */
-- STMT_UPGRADE_TO_28
UPDATE NODES SET checksum = (SELECT checksum FROM pristine
WHERE md5_checksum = nodes.checksum)
WHERE EXISTS (SELECT 1 FROM pristine WHERE md5_checksum = nodes.checksum);
PRAGMA user_version = 28;
/* ------------------------------------------------------------------------- */
/* Format 29 introduces the EXTERNALS table (See STMT_CREATE_TRIGGERS) and
optimizes a few trigger definitions. ... */
-- STMT_UPGRADE_TO_29
DROP TRIGGER IF EXISTS nodes_update_checksum_trigger;
DROP TRIGGER IF EXISTS nodes_insert_trigger;
DROP TRIGGER IF EXISTS nodes_delete_trigger;
CREATE TRIGGER nodes_update_checksum_trigger
AFTER UPDATE OF checksum ON nodes
WHEN NEW.checksum IS NOT OLD.checksum
/* AND (NEW.checksum IS NOT NULL OR OLD.checksum IS NOT NULL) */
BEGIN
UPDATE pristine SET refcount = refcount + 1
WHERE checksum = NEW.checksum;
UPDATE pristine SET refcount = refcount - 1
WHERE checksum = OLD.checksum;
END;
CREATE TRIGGER nodes_insert_trigger
AFTER INSERT ON nodes
WHEN NEW.checksum IS NOT NULL
BEGIN
UPDATE pristine SET refcount = refcount + 1
WHERE checksum = NEW.checksum;
END;
CREATE TRIGGER nodes_delete_trigger
AFTER DELETE ON nodes
WHEN OLD.checksum IS NOT NULL
BEGIN
UPDATE pristine SET refcount = refcount - 1
WHERE checksum = OLD.checksum;
END;
PRAGMA user_version = 29;
/* ------------------------------------------------------------------------- */
/* Format 30 creates a new NODES index for move information, and a new
PRISTINE index for the md5_checksum column. It also activates use of
skel-based conflict storage -- see notes/wc-ng/conflict-storage-2.0.
It also renames the "absent" presence to "server-excluded". */
-- STMT_UPGRADE_TO_30
CREATE UNIQUE INDEX IF NOT EXISTS I_NODES_MOVED
ON NODES (wc_id, moved_to, op_depth);
CREATE INDEX IF NOT EXISTS I_PRISTINE_MD5 ON PRISTINE (md5_checksum);
UPDATE nodes SET presence = "server-excluded" WHERE presence = "absent";
/* Just to be sure clear out file external skels from pre 1.7.0 development
working copies that were never updated by 1.7.0+ style clients */
UPDATE nodes SET file_external=1 WHERE file_external IS NOT NULL;
-- STMT_UPGRADE_30_SELECT_CONFLICT_SEPARATE
SELECT wc_id, local_relpath,
conflict_old, conflict_working, conflict_new, prop_reject, tree_conflict_data
FROM actual_node
WHERE conflict_old IS NOT NULL
OR conflict_working IS NOT NULL
OR conflict_new IS NOT NULL
OR prop_reject IS NOT NULL
OR tree_conflict_data IS NOT NULL
ORDER by wc_id, local_relpath
-- STMT_UPGRADE_30_SET_CONFLICT
UPDATE actual_node SET conflict_data = ?3, conflict_old = NULL,
conflict_working = NULL, conflict_new = NULL, prop_reject = NULL,
tree_conflict_data = NULL
WHERE wc_id = ?1 and local_relpath = ?2
/* ------------------------------------------------------------------------- */
/* Format 31 adds the inherited_props column to the NODES table. C code then
initializes the update/switch roots to make sure future updates fetch the
inherited properties */
-- STMT_UPGRADE_TO_31_ALTER_TABLE
ALTER TABLE NODES ADD COLUMN inherited_props BLOB;
-- STMT_UPGRADE_TO_31_FINALIZE
DROP INDEX IF EXISTS I_ACTUAL_CHANGELIST;
DROP INDEX IF EXISTS I_EXTERNALS_PARENT;
DROP INDEX I_NODES_PARENT;
CREATE UNIQUE INDEX I_NODES_PARENT ON NODES (wc_id, parent_relpath,
local_relpath, op_depth);
DROP INDEX I_ACTUAL_PARENT;
CREATE UNIQUE INDEX I_ACTUAL_PARENT ON ACTUAL_NODE (wc_id, parent_relpath,
local_relpath);
PRAGMA user_version = 31;
-- STMT_UPGRADE_31_SELECT_WCROOT_NODES
/* Select all base nodes which are the root of a WC, including
switched subtrees, but excluding those which map to the root
of the repos.
### IPROPS: Is this query horribly inefficient? Quite likely,
### but it only runs during an upgrade, so do we care? */
SELECT l.wc_id, l.local_relpath FROM nodes as l
LEFT OUTER JOIN nodes as r
ON l.wc_id = r.wc_id
AND r.local_relpath = l.parent_relpath
AND r.op_depth = 0
WHERE l.op_depth = 0
AND l.repos_path != ''
AND ((l.repos_id IS NOT r.repos_id)
OR (l.repos_path IS NOT RELPATH_SKIP_JOIN(r.local_relpath, r.repos_path, l.local_relpath)))
/* ------------------------------------------------------------------------- */
/* Format 32 .... */
-- STMT_UPGRADE_TO_32
/* Drop old index. ### Remove this part from the upgrade to 31 once bumped */
DROP INDEX IF EXISTS I_ACTUAL_CHANGELIST;
DROP INDEX IF EXISTS I_EXTERNALS_PARENT;
CREATE INDEX I_EXTERNALS_PARENT ON EXTERNALS (wc_id, parent_relpath);
DROP INDEX I_NODES_PARENT;
CREATE UNIQUE INDEX I_NODES_PARENT ON NODES (wc_id, parent_relpath,
local_relpath, op_depth);
DROP INDEX I_ACTUAL_PARENT;
CREATE UNIQUE INDEX I_ACTUAL_PARENT ON ACTUAL_NODE (wc_id, parent_relpath,
local_relpath);
/* ------------------------------------------------------------------------- */
/* Format YYY introduces new handling for conflict information. */
-- format: YYY
/* ------------------------------------------------------------------------- */
/* Format 99 drops all columns not needed due to previous format upgrades.
Before we release 1.7, these statements will be pulled into a format bump
and all the tables will be cleaned up. We don't know what that format
number will be, however, so we're just marking it as 99 for now. */
-- format: 99
/* TODO: Un-confuse *_revision column names in the EXTERNALS table to
"-r<operative> foo@<peg>", as suggested by the patch attached to
http://svn.haxx.se/dev/archive-2011-09/0478.shtml */
/* TODO: Remove column parent_relpath from EXTERNALS. We're not using it and
never will. It's not interesting like in the NODES table: the external's
parent path may be *anything*: unversioned, "behind" a another WC... */
/* Now "drop" the tree_conflict_data column from actual_node. */
CREATE TABLE ACTUAL_NODE_BACKUP (
wc_id INTEGER NOT NULL,
local_relpath TEXT NOT NULL,
parent_relpath TEXT,
properties BLOB,
conflict_old TEXT,
conflict_new TEXT,
conflict_working TEXT,
prop_reject TEXT,
changelist TEXT,
text_mod TEXT
);
INSERT INTO ACTUAL_NODE_BACKUP SELECT
wc_id, local_relpath, parent_relpath, properties, conflict_old,
conflict_new, conflict_working, prop_reject, changelist, text_mod
FROM ACTUAL_NODE;
DROP TABLE ACTUAL_NODE;
CREATE TABLE ACTUAL_NODE (
wc_id INTEGER NOT NULL REFERENCES WCROOT (id),
local_relpath TEXT NOT NULL,
parent_relpath TEXT,
properties BLOB,
conflict_old TEXT,
conflict_new TEXT,
conflict_working TEXT,
prop_reject TEXT,
changelist TEXT,
text_mod TEXT,
PRIMARY KEY (wc_id, local_relpath)
);
CREATE UNIQUE INDEX I_ACTUAL_PARENT ON ACTUAL_NODE (wc_id, parent_relpath,
local_relpath);
INSERT INTO ACTUAL_NODE SELECT
wc_id, local_relpath, parent_relpath, properties, conflict_old,
conflict_new, conflict_working, prop_reject, changelist, text_mod
FROM ACTUAL_NODE_BACKUP;
DROP TABLE ACTUAL_NODE_BACKUP;
/* Note: Other differences between the schemas of an upgraded and a
* fresh WC.
*
* While format 22 was current, "NOT NULL" was added to the
* columns PRISTINE.size and PRISTINE.md5_checksum. The format was not
* bumped because it is a forward- and backward-compatible change.
*
* While format 23 was current, "REFERENCES PRISTINE" was added to the
* columns ACTUAL_NODE.older_checksum, ACTUAL_NODE.left_checksum,
* ACTUAL_NODE.right_checksum, NODES.checksum.
*
* The "NODES_BASE" view was originally implemented with a more complex (but
* functionally equivalent) statement using a 'JOIN'. WCs that were created
* at or upgraded to format 26 before it was changed will still have the old
* version.
*/
Index: vendor/subversion/dist/subversion/libsvn_wc/wc-queries.h
===================================================================
--- vendor/subversion/dist/subversion/libsvn_wc/wc-queries.h (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_wc/wc-queries.h (revision 286501)
@@ -1,3169 +1,3182 @@
-/* This file is automatically generated from wc-queries.sql and .dist_sandbox/subversion-1.8.10/subversion/libsvn_wc/token-map.h.
+/* This file is automatically generated from wc-queries.sql and .dist_sandbox/subversion-1.8.14/subversion/libsvn_wc/token-map.h.
* Do not edit this file -- edit the source and rerun gen-make.py */
#define STMT_SELECT_NODE_INFO 0
#define STMT_0_INFO {"STMT_SELECT_NODE_INFO", NULL}
#define STMT_0 \
"SELECT op_depth, repos_id, repos_path, presence, kind, revision, checksum, " \
" translated_size, changed_revision, changed_date, changed_author, depth, " \
" symlink_target, last_mod_time, properties, moved_here, inherited_props, " \
" moved_to " \
"FROM nodes " \
"WHERE wc_id = ?1 AND local_relpath = ?2 " \
"ORDER BY op_depth DESC " \
""
#define STMT_SELECT_NODE_INFO_WITH_LOCK 1
#define STMT_1_INFO {"STMT_SELECT_NODE_INFO_WITH_LOCK", NULL}
#define STMT_1 \
"SELECT op_depth, nodes.repos_id, nodes.repos_path, presence, kind, revision, " \
" checksum, translated_size, changed_revision, changed_date, changed_author, " \
" depth, symlink_target, last_mod_time, properties, moved_here, " \
" inherited_props, " \
" lock_token, lock_owner, lock_comment, lock_date " \
"FROM nodes " \
"LEFT OUTER JOIN lock ON nodes.repos_id = lock.repos_id " \
" AND nodes.repos_path = lock.repos_relpath " \
"WHERE wc_id = ?1 AND local_relpath = ?2 " \
"ORDER BY op_depth DESC " \
""
#define STMT_SELECT_BASE_NODE 2
#define STMT_2_INFO {"STMT_SELECT_BASE_NODE", NULL}
#define STMT_2 \
"SELECT repos_id, repos_path, presence, kind, revision, checksum, " \
" translated_size, changed_revision, changed_date, changed_author, depth, " \
" symlink_target, last_mod_time, properties, file_external " \
"FROM nodes " \
"WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 " \
""
#define STMT_SELECT_BASE_NODE_WITH_LOCK 3
#define STMT_3_INFO {"STMT_SELECT_BASE_NODE_WITH_LOCK", NULL}
#define STMT_3 \
"SELECT nodes.repos_id, nodes.repos_path, presence, kind, revision, " \
" checksum, translated_size, changed_revision, changed_date, changed_author, " \
" depth, symlink_target, last_mod_time, properties, file_external, " \
" lock_token, lock_owner, lock_comment, lock_date " \
"FROM nodes " \
"LEFT OUTER JOIN lock ON nodes.repos_id = lock.repos_id " \
" AND nodes.repos_path = lock.repos_relpath " \
"WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 " \
""
#define STMT_SELECT_BASE_CHILDREN_INFO 4
#define STMT_4_INFO {"STMT_SELECT_BASE_CHILDREN_INFO", NULL}
#define STMT_4 \
"SELECT local_relpath, nodes.repos_id, nodes.repos_path, presence, kind, " \
" revision, depth, file_external, " \
" lock_token, lock_owner, lock_comment, lock_date " \
"FROM nodes " \
"LEFT OUTER JOIN lock ON nodes.repos_id = lock.repos_id " \
" AND nodes.repos_path = lock.repos_relpath " \
"WHERE wc_id = ?1 AND parent_relpath = ?2 AND op_depth = 0 " \
""
#define STMT_SELECT_WORKING_NODE 5
#define STMT_5_INFO {"STMT_SELECT_WORKING_NODE", NULL}
#define STMT_5 \
"SELECT op_depth, presence, kind, checksum, translated_size, " \
" changed_revision, changed_date, changed_author, depth, symlink_target, " \
" repos_id, repos_path, revision, " \
" moved_here, moved_to, last_mod_time, properties " \
"FROM nodes " \
"WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > 0 " \
"ORDER BY op_depth DESC " \
"LIMIT 1 " \
""
#define STMT_SELECT_DEPTH_NODE 6
#define STMT_6_INFO {"STMT_SELECT_DEPTH_NODE", NULL}
#define STMT_6 \
"SELECT repos_id, repos_path, presence, kind, revision, checksum, " \
" translated_size, changed_revision, changed_date, changed_author, depth, " \
" symlink_target, last_mod_time, properties " \
"FROM nodes " \
"WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3 " \
""
#define STMT_SELECT_LOWEST_WORKING_NODE 7
#define STMT_7_INFO {"STMT_SELECT_LOWEST_WORKING_NODE", NULL}
#define STMT_7 \
"SELECT op_depth, presence, kind, moved_to " \
"FROM nodes " \
"WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > ?3 " \
"ORDER BY op_depth " \
"LIMIT 1 " \
""
#define STMT_SELECT_HIGHEST_WORKING_NODE 8
#define STMT_8_INFO {"STMT_SELECT_HIGHEST_WORKING_NODE", NULL}
#define STMT_8 \
"SELECT op_depth " \
"FROM nodes " \
"WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth < ?3 " \
"ORDER BY op_depth DESC " \
"LIMIT 1 " \
""
#define STMT_SELECT_ACTUAL_NODE 9
#define STMT_9_INFO {"STMT_SELECT_ACTUAL_NODE", NULL}
#define STMT_9 \
"SELECT changelist, properties, conflict_data " \
"FROM actual_node " \
"WHERE wc_id = ?1 AND local_relpath = ?2 " \
""
#define STMT_SELECT_NODE_CHILDREN_INFO 10
#define STMT_10_INFO {"STMT_SELECT_NODE_CHILDREN_INFO", NULL}
#define STMT_10 \
"SELECT op_depth, nodes.repos_id, nodes.repos_path, presence, kind, revision, " \
" checksum, translated_size, changed_revision, changed_date, changed_author, " \
" depth, symlink_target, last_mod_time, properties, lock_token, lock_owner, " \
" lock_comment, lock_date, local_relpath, moved_here, moved_to, file_external " \
"FROM nodes " \
"LEFT OUTER JOIN lock ON nodes.repos_id = lock.repos_id " \
" AND nodes.repos_path = lock.repos_relpath AND op_depth = 0 " \
"WHERE wc_id = ?1 AND parent_relpath = ?2 " \
""
#define STMT_SELECT_NODE_CHILDREN_WALKER_INFO 11
#define STMT_11_INFO {"STMT_SELECT_NODE_CHILDREN_WALKER_INFO", NULL}
#define STMT_11 \
"SELECT local_relpath, op_depth, presence, kind " \
"FROM nodes_current " \
"WHERE wc_id = ?1 AND parent_relpath = ?2 " \
""
#define STMT_SELECT_ACTUAL_CHILDREN_INFO 12
#define STMT_12_INFO {"STMT_SELECT_ACTUAL_CHILDREN_INFO", NULL}
#define STMT_12 \
"SELECT local_relpath, changelist, properties, conflict_data " \
"FROM actual_node " \
"WHERE wc_id = ?1 AND parent_relpath = ?2 " \
""
#define STMT_SELECT_REPOSITORY_BY_ID 13
#define STMT_13_INFO {"STMT_SELECT_REPOSITORY_BY_ID", NULL}
#define STMT_13 \
"SELECT root, uuid FROM repository WHERE id = ?1 " \
""
#define STMT_SELECT_WCROOT_NULL 14
#define STMT_14_INFO {"STMT_SELECT_WCROOT_NULL", NULL}
#define STMT_14 \
"SELECT id FROM wcroot WHERE local_abspath IS NULL " \
""
#define STMT_SELECT_REPOSITORY 15
#define STMT_15_INFO {"STMT_SELECT_REPOSITORY", NULL}
#define STMT_15 \
"SELECT id FROM repository WHERE root = ?1 " \
""
#define STMT_INSERT_REPOSITORY 16
#define STMT_16_INFO {"STMT_INSERT_REPOSITORY", NULL}
#define STMT_16 \
"INSERT INTO repository (root, uuid) VALUES (?1, ?2) " \
""
#define STMT_INSERT_NODE 17
#define STMT_17_INFO {"STMT_INSERT_NODE", NULL}
#define STMT_17 \
"INSERT OR REPLACE INTO nodes ( " \
" wc_id, local_relpath, op_depth, parent_relpath, repos_id, repos_path, " \
" revision, presence, depth, kind, changed_revision, changed_date, " \
" changed_author, checksum, properties, translated_size, last_mod_time, " \
" dav_cache, symlink_target, file_external, moved_to, moved_here, " \
" inherited_props) " \
"VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, " \
" ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23) " \
""
#define STMT_SELECT_BASE_PRESENT 18
#define STMT_18_INFO {"STMT_SELECT_BASE_PRESENT", NULL}
#define STMT_18 \
"SELECT local_relpath, kind FROM nodes n " \
"WHERE wc_id = ?1 AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND op_depth = 0 " \
" AND presence in ('normal', 'incomplete') " \
" AND NOT EXISTS(SELECT 1 FROM NODES w " \
" WHERE w.wc_id = ?1 AND w.local_relpath = n.local_relpath " \
" AND op_depth > 0) " \
"ORDER BY local_relpath DESC " \
""
#define STMT_SELECT_WORKING_PRESENT 19
#define STMT_19_INFO {"STMT_SELECT_WORKING_PRESENT", NULL}
#define STMT_19 \
"SELECT local_relpath, kind, checksum, translated_size, last_mod_time " \
"FROM nodes n " \
"WHERE wc_id = ?1 " \
" AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND presence in ('normal', 'incomplete') " \
" AND op_depth = (SELECT MAX(op_depth) " \
" FROM NODES w " \
" WHERE w.wc_id = ?1 " \
" AND w.local_relpath = n.local_relpath) " \
"ORDER BY local_relpath DESC " \
""
#define STMT_DELETE_NODE_RECURSIVE 20
#define STMT_20_INFO {"STMT_DELETE_NODE_RECURSIVE", NULL}
#define STMT_20 \
"DELETE FROM NODES " \
"WHERE wc_id = ?1 " \
" AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
""
#define STMT_DELETE_NODE 21
#define STMT_21_INFO {"STMT_DELETE_NODE", NULL}
#define STMT_21 \
"DELETE " \
"FROM NODES " \
"WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3 " \
""
#define STMT_DELETE_ACTUAL_FOR_BASE_RECURSIVE 22
#define STMT_22_INFO {"STMT_DELETE_ACTUAL_FOR_BASE_RECURSIVE", NULL}
#define STMT_22 \
"DELETE FROM actual_node " \
"WHERE wc_id = ?1 AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND EXISTS(SELECT 1 FROM NODES b " \
" WHERE b.wc_id = ?1 " \
" AND b.local_relpath = actual_node.local_relpath " \
" AND op_depth = 0) " \
" AND NOT EXISTS(SELECT 1 FROM NODES w " \
" WHERE w.wc_id = ?1 " \
" AND w.local_relpath = actual_node.local_relpath " \
" AND op_depth > 0 " \
" AND presence in ('normal', 'incomplete', 'not-present')) " \
""
#define STMT_DELETE_WORKING_BASE_DELETE 23
#define STMT_23_INFO {"STMT_DELETE_WORKING_BASE_DELETE", NULL}
#define STMT_23 \
"DELETE FROM nodes " \
"WHERE wc_id = ?1 AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND presence = 'base-deleted' " \
" AND op_depth > 0 " \
" AND op_depth = (SELECT MIN(op_depth) FROM nodes n " \
" WHERE n.wc_id = ?1 " \
" AND n.local_relpath = nodes.local_relpath " \
" AND op_depth > 0) " \
""
#define STMT_DELETE_WORKING_RECURSIVE 24
#define STMT_24_INFO {"STMT_DELETE_WORKING_RECURSIVE", NULL}
#define STMT_24 \
"DELETE FROM nodes " \
"WHERE wc_id = ?1 AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND op_depth > 0 " \
""
#define STMT_DELETE_BASE_RECURSIVE 25
#define STMT_25_INFO {"STMT_DELETE_BASE_RECURSIVE", NULL}
#define STMT_25 \
"DELETE FROM nodes " \
"WHERE wc_id = ?1 AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND op_depth = 0 " \
""
#define STMT_DELETE_WORKING_OP_DEPTH 26
#define STMT_26_INFO {"STMT_DELETE_WORKING_OP_DEPTH", NULL}
#define STMT_26 \
"DELETE FROM nodes " \
"WHERE wc_id = ?1 " \
" AND (local_relpath = ?2 OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
" AND op_depth = ?3 " \
""
#define STMT_DELETE_WORKING_OP_DEPTH_ABOVE 27
#define STMT_27_INFO {"STMT_DELETE_WORKING_OP_DEPTH_ABOVE", NULL}
#define STMT_27 \
"DELETE FROM nodes " \
"WHERE wc_id = ?1 " \
" AND (local_relpath = ?2 OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
" AND op_depth > ?3 " \
""
#define STMT_SELECT_LOCAL_RELPATH_OP_DEPTH 28
#define STMT_28_INFO {"STMT_SELECT_LOCAL_RELPATH_OP_DEPTH", NULL}
#define STMT_28 \
"SELECT local_relpath " \
"FROM nodes " \
"WHERE wc_id = ?1 " \
" AND (local_relpath = ?2 OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
" AND op_depth = ?3 " \
""
#define STMT_SELECT_CHILDREN_OP_DEPTH 29
#define STMT_29_INFO {"STMT_SELECT_CHILDREN_OP_DEPTH", NULL}
#define STMT_29 \
"SELECT local_relpath, kind " \
"FROM nodes " \
"WHERE wc_id = ?1 " \
" AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND op_depth = ?3 " \
"ORDER BY local_relpath DESC " \
""
#define STMT_COPY_NODE_MOVE 30
#define STMT_30_INFO {"STMT_COPY_NODE_MOVE", NULL}
#define STMT_30 \
"INSERT OR REPLACE INTO nodes ( " \
" wc_id, local_relpath, op_depth, parent_relpath, repos_id, repos_path, " \
" revision, presence, depth, kind, changed_revision, changed_date, " \
" changed_author, checksum, properties, translated_size, last_mod_time, " \
" symlink_target, moved_here, moved_to ) " \
"SELECT " \
" wc_id, ?4 , ?5 , ?6 , " \
" repos_id, " \
" repos_path, revision, presence, depth, kind, changed_revision, " \
" changed_date, changed_author, checksum, properties, translated_size, " \
" last_mod_time, symlink_target, 1, " \
" (SELECT dst.moved_to FROM nodes AS dst " \
" WHERE dst.wc_id = ?1 " \
" AND dst.local_relpath = ?4 " \
" AND dst.op_depth = ?5) " \
"FROM nodes " \
"WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3 " \
""
#define STMT_SELECT_OP_DEPTH_CHILDREN 31
#define STMT_31_INFO {"STMT_SELECT_OP_DEPTH_CHILDREN", NULL}
#define STMT_31 \
"SELECT local_relpath, kind FROM nodes " \
"WHERE wc_id = ?1 " \
" AND parent_relpath = ?2 " \
" AND op_depth = ?3 " \
" AND presence != 'base-deleted' " \
" AND file_external is NULL " \
""
#define STMT_SELECT_GE_OP_DEPTH_CHILDREN 32
#define STMT_32_INFO {"STMT_SELECT_GE_OP_DEPTH_CHILDREN", NULL}
#define STMT_32 \
"SELECT 1 FROM nodes " \
"WHERE wc_id = ?1 AND parent_relpath = ?2 " \
" AND (op_depth > ?3 OR (op_depth = ?3 AND presence != 'base-deleted')) " \
"UNION ALL " \
"SELECT 1 FROM ACTUAL_NODE a " \
"WHERE wc_id = ?1 AND parent_relpath = ?2 " \
" AND NOT EXISTS (SELECT 1 FROM nodes n " \
" WHERE wc_id = ?1 AND n.local_relpath = a.local_relpath) " \
""
#define STMT_DELETE_SHADOWED_RECURSIVE 33
#define STMT_33_INFO {"STMT_DELETE_SHADOWED_RECURSIVE", NULL}
#define STMT_33 \
"DELETE FROM nodes " \
"WHERE wc_id = ?1 " \
" AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND (op_depth < ?3 " \
" OR (op_depth = ?3 AND presence = 'base-deleted')) " \
""
#define STMT_CLEAR_MOVED_TO_FROM_DEST 34
#define STMT_34_INFO {"STMT_CLEAR_MOVED_TO_FROM_DEST", NULL}
#define STMT_34 \
"UPDATE NODES SET moved_to = NULL " \
"WHERE wc_id = ?1 " \
" AND moved_to = ?2 " \
""
#define STMT_SELECT_NOT_PRESENT_DESCENDANTS 35
#define STMT_35_INFO {"STMT_SELECT_NOT_PRESENT_DESCENDANTS", NULL}
#define STMT_35 \
"SELECT local_relpath FROM nodes " \
"WHERE wc_id = ?1 AND op_depth = ?3 " \
" AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND presence = 'not-present' " \
""
#define STMT_COMMIT_DESCENDANTS_TO_BASE 36
#define STMT_36_INFO {"STMT_COMMIT_DESCENDANTS_TO_BASE", NULL}
#define STMT_36 \
"UPDATE NODES SET op_depth = 0, " \
" repos_id = ?4, " \
" repos_path = ?5 || SUBSTR(local_relpath, LENGTH(?2)+1), " \
" revision = ?6, " \
" dav_cache = NULL, " \
" moved_here = NULL, " \
" presence = CASE presence " \
" WHEN 'normal' THEN 'normal' " \
" WHEN 'excluded' THEN 'excluded' " \
" ELSE 'not-present' " \
" END " \
"WHERE wc_id = ?1 " \
" AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND op_depth = ?3 " \
""
#define STMT_SELECT_NODE_CHILDREN 37
#define STMT_37_INFO {"STMT_SELECT_NODE_CHILDREN", NULL}
#define STMT_37 \
"SELECT local_relpath FROM nodes " \
"WHERE wc_id = ?1 AND parent_relpath = ?2 " \
""
#define STMT_SELECT_WORKING_CHILDREN 38
#define STMT_38_INFO {"STMT_SELECT_WORKING_CHILDREN", NULL}
#define STMT_38 \
"SELECT local_relpath FROM nodes " \
"WHERE wc_id = ?1 AND parent_relpath = ?2 " \
" AND (op_depth > (SELECT MAX(op_depth) FROM nodes " \
" WHERE wc_id = ?1 AND local_relpath = ?2) " \
" OR " \
" (op_depth = (SELECT MAX(op_depth) FROM nodes " \
" WHERE wc_id = ?1 AND local_relpath = ?2) " \
" AND presence != 'base-deleted')) " \
""
#define STMT_SELECT_NODE_PROPS 39
#define STMT_39_INFO {"STMT_SELECT_NODE_PROPS", NULL}
#define STMT_39 \
"SELECT properties, presence FROM nodes " \
"WHERE wc_id = ?1 AND local_relpath = ?2 " \
"ORDER BY op_depth DESC " \
""
#define STMT_SELECT_ACTUAL_PROPS 40
#define STMT_40_INFO {"STMT_SELECT_ACTUAL_PROPS", NULL}
#define STMT_40 \
"SELECT properties FROM actual_node " \
"WHERE wc_id = ?1 AND local_relpath = ?2 " \
""
#define STMT_UPDATE_ACTUAL_PROPS 41
#define STMT_41_INFO {"STMT_UPDATE_ACTUAL_PROPS", NULL}
#define STMT_41 \
"UPDATE actual_node SET properties = ?3 " \
"WHERE wc_id = ?1 AND local_relpath = ?2 " \
""
#define STMT_INSERT_ACTUAL_PROPS 42
#define STMT_42_INFO {"STMT_INSERT_ACTUAL_PROPS", NULL}
#define STMT_42 \
"INSERT INTO actual_node (wc_id, local_relpath, parent_relpath, properties) " \
"VALUES (?1, ?2, ?3, ?4) " \
""
#define STMT_INSERT_LOCK 43
#define STMT_43_INFO {"STMT_INSERT_LOCK", NULL}
#define STMT_43 \
"INSERT OR REPLACE INTO lock " \
"(repos_id, repos_relpath, lock_token, lock_owner, lock_comment, " \
" lock_date) " \
"VALUES (?1, ?2, ?3, ?4, ?5, ?6) " \
""
#define STMT_SELECT_BASE_NODE_LOCK_TOKENS_RECURSIVE 44
#define STMT_44_INFO {"STMT_SELECT_BASE_NODE_LOCK_TOKENS_RECURSIVE", NULL}
#define STMT_44 \
"SELECT nodes.repos_id, nodes.repos_path, lock_token " \
"FROM nodes " \
"LEFT JOIN lock ON nodes.repos_id = lock.repos_id " \
" AND nodes.repos_path = lock.repos_relpath " \
"WHERE wc_id = ?1 AND op_depth = 0 " \
" AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
""
#define STMT_INSERT_WCROOT 45
#define STMT_45_INFO {"STMT_INSERT_WCROOT", NULL}
#define STMT_45 \
"INSERT INTO wcroot (local_abspath) " \
"VALUES (?1) " \
""
#define STMT_UPDATE_BASE_NODE_DAV_CACHE 46
#define STMT_46_INFO {"STMT_UPDATE_BASE_NODE_DAV_CACHE", NULL}
#define STMT_46 \
"UPDATE nodes SET dav_cache = ?3 " \
"WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 " \
""
#define STMT_SELECT_BASE_DAV_CACHE 47
#define STMT_47_INFO {"STMT_SELECT_BASE_DAV_CACHE", NULL}
#define STMT_47 \
"SELECT dav_cache FROM nodes " \
"WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 " \
""
#define STMT_SELECT_DELETION_INFO 48
#define STMT_48_INFO {"STMT_SELECT_DELETION_INFO", NULL}
#define STMT_48 \
"SELECT (SELECT b.presence FROM nodes AS b " \
" WHERE b.wc_id = ?1 AND b.local_relpath = ?2 AND b.op_depth = 0), " \
" work.presence, work.op_depth " \
"FROM nodes_current AS work " \
"WHERE work.wc_id = ?1 AND work.local_relpath = ?2 AND work.op_depth > 0 " \
"LIMIT 1 " \
""
#define STMT_SELECT_DELETION_INFO_SCAN 49
#define STMT_49_INFO {"STMT_SELECT_DELETION_INFO_SCAN", NULL}
#define STMT_49 \
"SELECT (SELECT b.presence FROM nodes AS b " \
" WHERE b.wc_id = ?1 AND b.local_relpath = ?2 AND b.op_depth = 0), " \
" work.presence, work.op_depth, moved.moved_to " \
"FROM nodes_current AS work " \
"LEFT OUTER JOIN nodes AS moved " \
" ON moved.wc_id = work.wc_id " \
" AND moved.local_relpath = work.local_relpath " \
" AND moved.moved_to IS NOT NULL " \
"WHERE work.wc_id = ?1 AND work.local_relpath = ?2 AND work.op_depth > 0 " \
"LIMIT 1 " \
""
#define STMT_SELECT_MOVED_TO_NODE 50
#define STMT_50_INFO {"STMT_SELECT_MOVED_TO_NODE", NULL}
#define STMT_50 \
"SELECT op_depth, moved_to " \
"FROM nodes " \
"WHERE wc_id = ?1 AND local_relpath = ?2 AND moved_to IS NOT NULL " \
"ORDER BY op_depth DESC " \
""
#define STMT_SELECT_OP_DEPTH_MOVED_TO 51
#define STMT_51_INFO {"STMT_SELECT_OP_DEPTH_MOVED_TO", NULL}
#define STMT_51 \
"SELECT op_depth, moved_to, repos_path, revision " \
"FROM nodes " \
"WHERE wc_id = ?1 AND local_relpath = ?2 " \
" AND op_depth <= (SELECT MIN(op_depth) FROM nodes " \
" WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > ?3) " \
"ORDER BY op_depth DESC " \
""
#define STMT_SELECT_MOVED_TO 52
#define STMT_52_INFO {"STMT_SELECT_MOVED_TO", NULL}
#define STMT_52 \
"SELECT moved_to " \
"FROM nodes " \
"WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3 " \
""
#define STMT_SELECT_MOVED_HERE 53
#define STMT_53_INFO {"STMT_SELECT_MOVED_HERE", NULL}
#define STMT_53 \
"SELECT moved_here, presence, repos_path, revision " \
"FROM nodes " \
"WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth >= ?3 " \
"ORDER BY op_depth " \
""
#define STMT_SELECT_MOVED_BACK 54
#define STMT_54_INFO {"STMT_SELECT_MOVED_BACK", NULL}
#define STMT_54 \
"SELECT u.local_relpath, " \
" u.presence, u.repos_id, u.repos_path, u.revision, " \
" l.presence, l.repos_id, l.repos_path, l.revision, " \
" u.moved_here, u.moved_to " \
"FROM nodes u " \
"LEFT OUTER JOIN nodes l ON l.wc_id = ?1 " \
" AND l.local_relpath = u.local_relpath " \
" AND l.op_depth = ?3 " \
"WHERE u.wc_id = ?1 " \
" AND u.local_relpath = ?2 " \
" AND u.op_depth = ?4 " \
"UNION ALL " \
"SELECT u.local_relpath, " \
" u.presence, u.repos_id, u.repos_path, u.revision, " \
" l.presence, l.repos_id, l.repos_path, l.revision, " \
" u.moved_here, NULL " \
"FROM nodes u " \
"LEFT OUTER JOIN nodes l ON l.wc_id=?1 " \
" AND l.local_relpath=u.local_relpath " \
" AND l.op_depth=?3 " \
"WHERE u.wc_id = ?1 " \
" AND (((u.local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((u.local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND u.op_depth = ?4 " \
""
#define STMT_DELETE_MOVED_BACK 55
#define STMT_55_INFO {"STMT_DELETE_MOVED_BACK", NULL}
#define STMT_55 \
"DELETE FROM nodes " \
"WHERE wc_id = ?1 " \
" AND (local_relpath = ?2 " \
" OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
" AND op_depth = ?3 " \
""
#define STMT_DELETE_LOCK 56
#define STMT_56_INFO {"STMT_DELETE_LOCK", NULL}
#define STMT_56 \
"DELETE FROM lock " \
"WHERE repos_id = ?1 AND repos_relpath = ?2 " \
""
#define STMT_DELETE_LOCK_RECURSIVELY 57
#define STMT_57_INFO {"STMT_DELETE_LOCK_RECURSIVELY", NULL}
#define STMT_57 \
"DELETE FROM lock " \
"WHERE repos_id = ?1 AND (repos_relpath = ?2 OR (((repos_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((repos_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
""
#define STMT_CLEAR_BASE_NODE_RECURSIVE_DAV_CACHE 58
#define STMT_58_INFO {"STMT_CLEAR_BASE_NODE_RECURSIVE_DAV_CACHE", NULL}
#define STMT_58 \
"UPDATE nodes SET dav_cache = NULL " \
"WHERE dav_cache IS NOT NULL AND wc_id = ?1 AND op_depth = 0 " \
" AND (local_relpath = ?2 " \
" OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
""
#define STMT_RECURSIVE_UPDATE_NODE_REPO 59
#define STMT_59_INFO {"STMT_RECURSIVE_UPDATE_NODE_REPO", NULL}
#define STMT_59 \
"UPDATE nodes SET repos_id = ?4, dav_cache = NULL " \
"WHERE (wc_id = ?1 AND local_relpath = ?2 AND repos_id = ?3) " \
" OR (wc_id = ?1 AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND repos_id = ?3) " \
""
#define STMT_UPDATE_LOCK_REPOS_ID 60
#define STMT_60_INFO {"STMT_UPDATE_LOCK_REPOS_ID", NULL}
#define STMT_60 \
"UPDATE lock SET repos_id = ?2 " \
"WHERE repos_id = ?1 " \
""
#define STMT_UPDATE_NODE_FILEINFO 61
#define STMT_61_INFO {"STMT_UPDATE_NODE_FILEINFO", NULL}
#define STMT_61 \
"UPDATE nodes SET translated_size = ?3, last_mod_time = ?4 " \
"WHERE wc_id = ?1 AND local_relpath = ?2 " \
" AND op_depth = (SELECT MAX(op_depth) FROM nodes " \
" WHERE wc_id = ?1 AND local_relpath = ?2) " \
""
#define STMT_INSERT_ACTUAL_CONFLICT 62
#define STMT_62_INFO {"STMT_INSERT_ACTUAL_CONFLICT", NULL}
#define STMT_62 \
"INSERT INTO actual_node (wc_id, local_relpath, conflict_data, parent_relpath) " \
"VALUES (?1, ?2, ?3, ?4) " \
""
#define STMT_UPDATE_ACTUAL_CONFLICT 63
#define STMT_63_INFO {"STMT_UPDATE_ACTUAL_CONFLICT", NULL}
#define STMT_63 \
"UPDATE actual_node SET conflict_data = ?3 " \
"WHERE wc_id = ?1 AND local_relpath = ?2 " \
""
#define STMT_UPDATE_ACTUAL_CHANGELISTS 64
#define STMT_64_INFO {"STMT_UPDATE_ACTUAL_CHANGELISTS", NULL}
#define STMT_64 \
"UPDATE actual_node SET changelist = ?3 " \
"WHERE wc_id = ?1 " \
" AND (local_relpath = ?2 OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
" AND local_relpath = (SELECT local_relpath FROM targets_list AS t " \
" WHERE wc_id = ?1 " \
" AND t.local_relpath = actual_node.local_relpath " \
" AND kind = 'file') " \
""
#define STMT_UPDATE_ACTUAL_CLEAR_CHANGELIST 65
#define STMT_65_INFO {"STMT_UPDATE_ACTUAL_CLEAR_CHANGELIST", NULL}
#define STMT_65 \
"UPDATE actual_node SET changelist = NULL " \
" WHERE wc_id = ?1 AND local_relpath = ?2 " \
""
#define STMT_MARK_SKIPPED_CHANGELIST_DIRS 66
#define STMT_66_INFO {"STMT_MARK_SKIPPED_CHANGELIST_DIRS", NULL}
#define STMT_66 \
"INSERT INTO changelist_list (wc_id, local_relpath, notify, changelist) " \
"SELECT wc_id, local_relpath, 7, ?3 " \
"FROM targets_list " \
"WHERE wc_id = ?1 " \
" AND (local_relpath = ?2 OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
" AND kind = 'dir' " \
""
#define STMT_RESET_ACTUAL_WITH_CHANGELIST 67
#define STMT_67_INFO {"STMT_RESET_ACTUAL_WITH_CHANGELIST", NULL}
#define STMT_67 \
"REPLACE INTO actual_node ( " \
" wc_id, local_relpath, parent_relpath, changelist) " \
"VALUES (?1, ?2, ?3, ?4) " \
""
#define STMT_CREATE_CHANGELIST_LIST 68
#define STMT_68_INFO {"STMT_CREATE_CHANGELIST_LIST", NULL}
#define STMT_68 \
"DROP TABLE IF EXISTS changelist_list; " \
"CREATE TEMPORARY TABLE changelist_list ( " \
" wc_id INTEGER NOT NULL, " \
" local_relpath TEXT NOT NULL, " \
" notify INTEGER NOT NULL, " \
" changelist TEXT NOT NULL, " \
" PRIMARY KEY (wc_id, local_relpath, notify DESC) " \
") " \
""
#define STMT_CREATE_CHANGELIST_TRIGGER 69
#define STMT_69_INFO {"STMT_CREATE_CHANGELIST_TRIGGER", NULL}
#define STMT_69 \
"DROP TRIGGER IF EXISTS trigger_changelist_list_change; " \
"CREATE TEMPORARY TRIGGER trigger_changelist_list_change " \
"BEFORE UPDATE ON actual_node " \
"WHEN old.changelist IS NOT new.changelist " \
"BEGIN " \
" INSERT INTO changelist_list(wc_id, local_relpath, notify, changelist) " \
" SELECT old.wc_id, old.local_relpath, 27, old.changelist " \
" WHERE old.changelist is NOT NULL; " \
" INSERT INTO changelist_list(wc_id, local_relpath, notify, changelist) " \
" SELECT new.wc_id, new.local_relpath, 26, new.changelist " \
" WHERE new.changelist IS NOT NULL; " \
"END " \
""
#define STMT_FINALIZE_CHANGELIST 70
#define STMT_70_INFO {"STMT_FINALIZE_CHANGELIST", NULL}
#define STMT_70 \
"DROP TRIGGER trigger_changelist_list_change; " \
"DROP TABLE changelist_list; " \
"DROP TABLE targets_list " \
""
#define STMT_SELECT_CHANGELIST_LIST 71
#define STMT_71_INFO {"STMT_SELECT_CHANGELIST_LIST", NULL}
#define STMT_71 \
"SELECT wc_id, local_relpath, notify, changelist " \
"FROM changelist_list " \
"ORDER BY wc_id, local_relpath ASC, notify DESC " \
""
#define STMT_CREATE_TARGETS_LIST 72
#define STMT_72_INFO {"STMT_CREATE_TARGETS_LIST", NULL}
#define STMT_72 \
"DROP TABLE IF EXISTS targets_list; " \
"CREATE TEMPORARY TABLE targets_list ( " \
" wc_id INTEGER NOT NULL, " \
" local_relpath TEXT NOT NULL, " \
" parent_relpath TEXT, " \
" kind TEXT NOT NULL, " \
" PRIMARY KEY (wc_id, local_relpath) " \
" ); " \
""
#define STMT_DROP_TARGETS_LIST 73
#define STMT_73_INFO {"STMT_DROP_TARGETS_LIST", NULL}
#define STMT_73 \
"DROP TABLE targets_list " \
""
#define STMT_INSERT_TARGET 74
#define STMT_74_INFO {"STMT_INSERT_TARGET", NULL}
#define STMT_74 \
"INSERT INTO targets_list(wc_id, local_relpath, parent_relpath, kind) " \
"SELECT wc_id, local_relpath, parent_relpath, kind " \
"FROM nodes_current " \
"WHERE wc_id = ?1 " \
" AND local_relpath = ?2 " \
""
#define STMT_INSERT_TARGET_DEPTH_FILES 75
#define STMT_75_INFO {"STMT_INSERT_TARGET_DEPTH_FILES", NULL}
#define STMT_75 \
"INSERT INTO targets_list(wc_id, local_relpath, parent_relpath, kind) " \
"SELECT wc_id, local_relpath, parent_relpath, kind " \
"FROM nodes_current " \
"WHERE wc_id = ?1 " \
" AND parent_relpath = ?2 " \
" AND kind = 'file' " \
""
#define STMT_INSERT_TARGET_DEPTH_IMMEDIATES 76
#define STMT_76_INFO {"STMT_INSERT_TARGET_DEPTH_IMMEDIATES", NULL}
#define STMT_76 \
"INSERT INTO targets_list(wc_id, local_relpath, parent_relpath, kind) " \
"SELECT wc_id, local_relpath, parent_relpath, kind " \
"FROM nodes_current " \
"WHERE wc_id = ?1 " \
" AND parent_relpath = ?2 " \
""
#define STMT_INSERT_TARGET_DEPTH_INFINITY 77
#define STMT_77_INFO {"STMT_INSERT_TARGET_DEPTH_INFINITY", NULL}
#define STMT_77 \
"INSERT INTO targets_list(wc_id, local_relpath, parent_relpath, kind) " \
"SELECT wc_id, local_relpath, parent_relpath, kind " \
"FROM nodes_current " \
"WHERE wc_id = ?1 " \
" AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
""
#define STMT_INSERT_TARGET_WITH_CHANGELIST 78
#define STMT_78_INFO {"STMT_INSERT_TARGET_WITH_CHANGELIST", NULL}
#define STMT_78 \
"INSERT INTO targets_list(wc_id, local_relpath, parent_relpath, kind) " \
"SELECT N.wc_id, N.local_relpath, N.parent_relpath, N.kind " \
" FROM actual_node AS A JOIN nodes_current AS N " \
" ON A.wc_id = N.wc_id AND A.local_relpath = N.local_relpath " \
" WHERE N.wc_id = ?1 " \
" AND N.local_relpath = ?2 " \
" AND A.changelist = ?3 " \
""
#define STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_FILES 79
#define STMT_79_INFO {"STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_FILES", NULL}
#define STMT_79 \
"INSERT INTO targets_list(wc_id, local_relpath, parent_relpath, kind) " \
"SELECT N.wc_id, N.local_relpath, N.parent_relpath, N.kind " \
" FROM actual_node AS A JOIN nodes_current AS N " \
" ON A.wc_id = N.wc_id AND A.local_relpath = N.local_relpath " \
" WHERE N.wc_id = ?1 " \
" AND N.parent_relpath = ?2 " \
" AND kind = 'file' " \
" AND A.changelist = ?3 " \
""
#define STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_IMMEDIATES 80
#define STMT_80_INFO {"STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_IMMEDIATES", NULL}
#define STMT_80 \
"INSERT INTO targets_list(wc_id, local_relpath, parent_relpath, kind) " \
"SELECT N.wc_id, N.local_relpath, N.parent_relpath, N.kind " \
" FROM actual_node AS A JOIN nodes_current AS N " \
" ON A.wc_id = N.wc_id AND A.local_relpath = N.local_relpath " \
" WHERE N.wc_id = ?1 " \
" AND N.parent_relpath = ?2 " \
" AND A.changelist = ?3 " \
""
#define STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_INFINITY 81
#define STMT_81_INFO {"STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_INFINITY", NULL}
#define STMT_81 \
"INSERT INTO targets_list(wc_id, local_relpath, parent_relpath, kind) " \
"SELECT N.wc_id, N.local_relpath, N.parent_relpath, N.kind " \
" FROM actual_node AS A JOIN nodes_current AS N " \
" ON A.wc_id = N.wc_id AND A.local_relpath = N.local_relpath " \
" WHERE N.wc_id = ?1 " \
" AND (((N.local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((N.local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND A.changelist = ?3 " \
""
#define STMT_INSERT_ACTUAL_EMPTIES 82
#define STMT_82_INFO {"STMT_INSERT_ACTUAL_EMPTIES", NULL}
#define STMT_82 \
"INSERT OR IGNORE INTO actual_node ( " \
" wc_id, local_relpath, parent_relpath) " \
"SELECT wc_id, local_relpath, parent_relpath " \
"FROM targets_list " \
""
#define STMT_DELETE_ACTUAL_EMPTY 83
#define STMT_83_INFO {"STMT_DELETE_ACTUAL_EMPTY", NULL}
#define STMT_83 \
"DELETE FROM actual_node " \
"WHERE wc_id = ?1 AND local_relpath = ?2 " \
" AND properties IS NULL " \
" AND conflict_data IS NULL " \
" AND changelist IS NULL " \
" AND text_mod IS NULL " \
" AND older_checksum IS NULL " \
" AND right_checksum IS NULL " \
" AND left_checksum IS NULL " \
""
#define STMT_DELETE_ACTUAL_EMPTIES 84
#define STMT_84_INFO {"STMT_DELETE_ACTUAL_EMPTIES", NULL}
#define STMT_84 \
"DELETE FROM actual_node " \
"WHERE wc_id = ?1 " \
" AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND properties IS NULL " \
" AND conflict_data IS NULL " \
" AND changelist IS NULL " \
" AND text_mod IS NULL " \
" AND older_checksum IS NULL " \
" AND right_checksum IS NULL " \
" AND left_checksum IS NULL " \
""
#define STMT_DELETE_BASE_NODE 85
#define STMT_85_INFO {"STMT_DELETE_BASE_NODE", NULL}
#define STMT_85 \
"DELETE FROM nodes " \
"WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 " \
""
#define STMT_DELETE_WORKING_NODE 86
#define STMT_86_INFO {"STMT_DELETE_WORKING_NODE", NULL}
#define STMT_86 \
"DELETE FROM nodes " \
"WHERE wc_id = ?1 AND local_relpath = ?2 " \
" AND op_depth = (SELECT MAX(op_depth) FROM nodes " \
" WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > 0) " \
""
#define STMT_DELETE_LOWEST_WORKING_NODE 87
#define STMT_87_INFO {"STMT_DELETE_LOWEST_WORKING_NODE", NULL}
#define STMT_87 \
"DELETE FROM nodes " \
"WHERE wc_id = ?1 AND local_relpath = ?2 " \
" AND op_depth = (SELECT MIN(op_depth) FROM nodes " \
" WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > ?3) " \
" AND presence = 'base-deleted' " \
""
#define STMT_DELETE_NODE_ALL_LAYERS 88
#define STMT_88_INFO {"STMT_DELETE_NODE_ALL_LAYERS", NULL}
#define STMT_88 \
"DELETE FROM nodes " \
"WHERE wc_id = ?1 AND local_relpath = ?2 " \
""
#define STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE 89
#define STMT_89_INFO {"STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE", NULL}
#define STMT_89 \
"DELETE FROM nodes " \
"WHERE wc_id = ?1 " \
" AND (local_relpath = ?2 " \
" OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
" AND op_depth >= ?3 " \
""
#define STMT_DELETE_ACTUAL_NODE 90
#define STMT_90_INFO {"STMT_DELETE_ACTUAL_NODE", NULL}
#define STMT_90 \
"DELETE FROM actual_node " \
"WHERE wc_id = ?1 AND local_relpath = ?2 " \
""
#define STMT_DELETE_ACTUAL_NODE_RECURSIVE 91
#define STMT_91_INFO {"STMT_DELETE_ACTUAL_NODE_RECURSIVE", NULL}
#define STMT_91 \
"DELETE FROM actual_node " \
"WHERE wc_id = ?1 " \
" AND (local_relpath = ?2 " \
" OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
""
#define STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST 92
#define STMT_92_INFO {"STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST", NULL}
#define STMT_92 \
"DELETE FROM actual_node " \
"WHERE wc_id = ?1 " \
" AND local_relpath = ?2 " \
" AND (changelist IS NULL " \
" OR NOT EXISTS (SELECT 1 FROM nodes_current c " \
" WHERE c.wc_id = ?1 AND c.local_relpath = ?2 " \
" AND c.kind = 'file')) " \
""
#define STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE 93
#define STMT_93_INFO {"STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE", NULL}
#define STMT_93 \
"DELETE FROM actual_node " \
"WHERE wc_id = ?1 " \
" AND (local_relpath = ?2 " \
" OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
" AND (changelist IS NULL " \
" OR NOT EXISTS (SELECT 1 FROM nodes_current c " \
" WHERE c.wc_id = ?1 " \
" AND c.local_relpath = actual_node.local_relpath " \
" AND c.kind = 'file')) " \
""
#define STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST 94
#define STMT_94_INFO {"STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST", NULL}
#define STMT_94 \
"UPDATE actual_node " \
"SET properties = NULL, " \
" text_mod = NULL, " \
" conflict_data = NULL, " \
" tree_conflict_data = NULL, " \
" older_checksum = NULL, " \
" left_checksum = NULL, " \
" right_checksum = NULL " \
"WHERE wc_id = ?1 AND local_relpath = ?2 " \
""
#define STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE 95
#define STMT_95_INFO {"STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE", NULL}
#define STMT_95 \
"UPDATE actual_node " \
"SET properties = NULL, " \
" text_mod = NULL, " \
" conflict_data = NULL, " \
" tree_conflict_data = NULL, " \
" older_checksum = NULL, " \
" left_checksum = NULL, " \
" right_checksum = NULL " \
"WHERE wc_id = ?1 " \
" AND (local_relpath = ?2 " \
" OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
""
#define STMT_UPDATE_NODE_BASE_DEPTH 96
#define STMT_96_INFO {"STMT_UPDATE_NODE_BASE_DEPTH", NULL}
#define STMT_96 \
"UPDATE nodes SET depth = ?3 " \
"WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 " \
" AND kind='dir' " \
""
#define STMT_UPDATE_NODE_BASE_PRESENCE 97
#define STMT_97_INFO {"STMT_UPDATE_NODE_BASE_PRESENCE", NULL}
#define STMT_97 \
"UPDATE nodes SET presence = ?3 " \
"WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 " \
""
#define STMT_UPDATE_BASE_NODE_PRESENCE_REVNUM_AND_REPOS_PATH 98
#define STMT_98_INFO {"STMT_UPDATE_BASE_NODE_PRESENCE_REVNUM_AND_REPOS_PATH", NULL}
#define STMT_98 \
"UPDATE nodes SET presence = ?3, revision = ?4, repos_path = ?5 " \
"WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 " \
""
#define STMT_LOOK_FOR_WORK 99
#define STMT_99_INFO {"STMT_LOOK_FOR_WORK", NULL}
#define STMT_99 \
"SELECT id FROM work_queue LIMIT 1 " \
""
#define STMT_INSERT_WORK_ITEM 100
#define STMT_100_INFO {"STMT_INSERT_WORK_ITEM", NULL}
#define STMT_100 \
"INSERT INTO work_queue (work) VALUES (?1) " \
""
#define STMT_SELECT_WORK_ITEM 101
#define STMT_101_INFO {"STMT_SELECT_WORK_ITEM", NULL}
#define STMT_101 \
"SELECT id, work FROM work_queue ORDER BY id LIMIT 1 " \
""
#define STMT_DELETE_WORK_ITEM 102
#define STMT_102_INFO {"STMT_DELETE_WORK_ITEM", NULL}
#define STMT_102 \
"DELETE FROM work_queue WHERE id = ?1 " \
""
#define STMT_INSERT_OR_IGNORE_PRISTINE 103
#define STMT_103_INFO {"STMT_INSERT_OR_IGNORE_PRISTINE", NULL}
#define STMT_103 \
"INSERT OR IGNORE INTO pristine (checksum, md5_checksum, size, refcount) " \
"VALUES (?1, ?2, ?3, 0) " \
""
#define STMT_INSERT_PRISTINE 104
#define STMT_104_INFO {"STMT_INSERT_PRISTINE", NULL}
#define STMT_104 \
"INSERT INTO pristine (checksum, md5_checksum, size, refcount) " \
"VALUES (?1, ?2, ?3, 0) " \
""
#define STMT_SELECT_PRISTINE 105
#define STMT_105_INFO {"STMT_SELECT_PRISTINE", NULL}
#define STMT_105 \
"SELECT md5_checksum " \
"FROM pristine " \
"WHERE checksum = ?1 " \
""
#define STMT_SELECT_PRISTINE_SIZE 106
#define STMT_106_INFO {"STMT_SELECT_PRISTINE_SIZE", NULL}
#define STMT_106 \
"SELECT size " \
"FROM pristine " \
"WHERE checksum = ?1 LIMIT 1 " \
""
#define STMT_SELECT_PRISTINE_BY_MD5 107
#define STMT_107_INFO {"STMT_SELECT_PRISTINE_BY_MD5", NULL}
#define STMT_107 \
"SELECT checksum " \
"FROM pristine " \
"WHERE md5_checksum = ?1 " \
""
#define STMT_SELECT_UNREFERENCED_PRISTINES 108
#define STMT_108_INFO {"STMT_SELECT_UNREFERENCED_PRISTINES", NULL}
#define STMT_108 \
"SELECT checksum " \
"FROM pristine " \
"WHERE refcount = 0 " \
""
#define STMT_DELETE_PRISTINE_IF_UNREFERENCED 109
#define STMT_109_INFO {"STMT_DELETE_PRISTINE_IF_UNREFERENCED", NULL}
#define STMT_109 \
"DELETE FROM pristine " \
"WHERE checksum = ?1 AND refcount = 0 " \
""
#define STMT_SELECT_COPY_PRISTINES 110
#define STMT_110_INFO {"STMT_SELECT_COPY_PRISTINES", NULL}
#define STMT_110 \
"SELECT n.checksum, md5_checksum, size " \
"FROM nodes_current n " \
"LEFT JOIN pristine p ON n.checksum = p.checksum " \
"WHERE wc_id = ?1 " \
" AND n.local_relpath = ?2 " \
" AND n.checksum IS NOT NULL " \
"UNION ALL " \
"SELECT n.checksum, md5_checksum, size " \
"FROM nodes n " \
"LEFT JOIN pristine p ON n.checksum = p.checksum " \
"WHERE wc_id = ?1 " \
" AND (((n.local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((n.local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND op_depth >= " \
" (SELECT MAX(op_depth) FROM nodes WHERE wc_id = ?1 AND local_relpath = ?2) " \
" AND n.checksum IS NOT NULL " \
""
#define STMT_VACUUM 111
#define STMT_111_INFO {"STMT_VACUUM", NULL}
#define STMT_111 \
"VACUUM " \
""
#define STMT_SELECT_CONFLICT_VICTIMS 112
#define STMT_112_INFO {"STMT_SELECT_CONFLICT_VICTIMS", NULL}
#define STMT_112 \
"SELECT local_relpath, conflict_data " \
"FROM actual_node " \
"WHERE wc_id = ?1 AND parent_relpath = ?2 AND " \
" NOT (conflict_data IS NULL) " \
""
#define STMT_INSERT_WC_LOCK 113
#define STMT_113_INFO {"STMT_INSERT_WC_LOCK", NULL}
#define STMT_113 \
"INSERT INTO wc_lock (wc_id, local_dir_relpath, locked_levels) " \
"VALUES (?1, ?2, ?3) " \
""
#define STMT_SELECT_WC_LOCK 114
#define STMT_114_INFO {"STMT_SELECT_WC_LOCK", NULL}
#define STMT_114 \
"SELECT locked_levels FROM wc_lock " \
"WHERE wc_id = ?1 AND local_dir_relpath = ?2 " \
""
#define STMT_SELECT_ANCESTOR_WCLOCKS 115
#define STMT_115_INFO {"STMT_SELECT_ANCESTOR_WCLOCKS", NULL}
#define STMT_115 \
"SELECT local_dir_relpath, locked_levels FROM wc_lock " \
"WHERE wc_id = ?1 " \
" AND ((local_dir_relpath >= ?3 AND local_dir_relpath <= ?2) " \
" OR local_dir_relpath = '') " \
""
#define STMT_DELETE_WC_LOCK 116
#define STMT_116_INFO {"STMT_DELETE_WC_LOCK", NULL}
#define STMT_116 \
"DELETE FROM wc_lock " \
"WHERE wc_id = ?1 AND local_dir_relpath = ?2 " \
""
#define STMT_FIND_WC_LOCK 117
#define STMT_117_INFO {"STMT_FIND_WC_LOCK", NULL}
#define STMT_117 \
"SELECT local_dir_relpath FROM wc_lock " \
"WHERE wc_id = ?1 " \
" AND (((local_dir_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_dir_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
""
#define STMT_DELETE_WC_LOCK_ORPHAN 118
#define STMT_118_INFO {"STMT_DELETE_WC_LOCK_ORPHAN", NULL}
#define STMT_118 \
"DELETE FROM wc_lock " \
"WHERE wc_id = ?1 AND local_dir_relpath = ?2 " \
"AND NOT EXISTS (SELECT 1 FROM nodes " \
" WHERE nodes.wc_id = ?1 " \
" AND nodes.local_relpath = wc_lock.local_dir_relpath) " \
""
#define STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE 119
#define STMT_119_INFO {"STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE", NULL}
#define STMT_119 \
"DELETE FROM wc_lock " \
"WHERE wc_id = ?1 " \
" AND (local_dir_relpath = ?2 " \
" OR (((local_dir_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_dir_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
" AND NOT EXISTS (SELECT 1 FROM nodes " \
" WHERE nodes.wc_id = ?1 " \
" AND nodes.local_relpath = wc_lock.local_dir_relpath) " \
""
#define STMT_APPLY_CHANGES_TO_BASE_NODE 120
#define STMT_120_INFO {"STMT_APPLY_CHANGES_TO_BASE_NODE", NULL}
#define STMT_120 \
"INSERT OR REPLACE INTO nodes ( " \
" wc_id, local_relpath, op_depth, parent_relpath, repos_id, repos_path, " \
" revision, presence, depth, kind, changed_revision, changed_date, " \
" changed_author, checksum, properties, dav_cache, symlink_target, " \
" inherited_props, file_external ) " \
"VALUES (?1, ?2, 0, " \
" ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, " \
" (SELECT file_external FROM nodes " \
" WHERE wc_id = ?1 " \
" AND local_relpath = ?2 " \
" AND op_depth = 0)) " \
""
#define STMT_INSTALL_WORKING_NODE_FOR_DELETE 121
#define STMT_121_INFO {"STMT_INSTALL_WORKING_NODE_FOR_DELETE", NULL}
#define STMT_121 \
"INSERT OR REPLACE INTO nodes ( " \
" wc_id, local_relpath, op_depth, " \
" parent_relpath, presence, kind) " \
"VALUES(?1, ?2, ?3, ?4, 'base-deleted', ?5) " \
""
#define STMT_DELETE_NO_LOWER_LAYER 122
#define STMT_122_INFO {"STMT_DELETE_NO_LOWER_LAYER", NULL}
#define STMT_122 \
"DELETE FROM nodes " \
" WHERE wc_id = ?1 " \
" AND (local_relpath = ?2 OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
" AND op_depth = ?3 " \
" AND NOT EXISTS (SELECT 1 FROM nodes n " \
" WHERE n.wc_id = ?1 " \
" AND n.local_relpath = nodes.local_relpath " \
" AND n.op_depth = ?4 " \
" AND n.presence IN ('normal', 'incomplete')) " \
""
#define STMT_REPLACE_WITH_BASE_DELETED 123
#define STMT_123_INFO {"STMT_REPLACE_WITH_BASE_DELETED", NULL}
#define STMT_123 \
"INSERT OR REPLACE INTO nodes (wc_id, local_relpath, op_depth, parent_relpath, " \
" kind, moved_to, presence) " \
"SELECT wc_id, local_relpath, op_depth, parent_relpath, " \
" kind, moved_to, 'base-deleted' " \
" FROM nodes " \
" WHERE wc_id = ?1 " \
" AND (local_relpath = ?2 OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
" AND op_depth = ?3 " \
""
#define STMT_INSERT_DELETE_FROM_NODE_RECURSIVE 124
#define STMT_124_INFO {"STMT_INSERT_DELETE_FROM_NODE_RECURSIVE", NULL}
#define STMT_124 \
"INSERT INTO nodes ( " \
" wc_id, local_relpath, op_depth, parent_relpath, presence, kind) " \
"SELECT wc_id, local_relpath, ?4 , parent_relpath, 'base-deleted', " \
" kind " \
"FROM nodes " \
"WHERE wc_id = ?1 " \
" AND (local_relpath = ?2 " \
" OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
" AND op_depth = ?3 " \
" AND presence NOT IN ('base-deleted', 'not-present', 'excluded', 'server-excluded') " \
" AND file_external IS NULL " \
""
#define STMT_INSERT_WORKING_NODE_FROM_BASE_COPY 125
#define STMT_125_INFO {"STMT_INSERT_WORKING_NODE_FROM_BASE_COPY", NULL}
#define STMT_125 \
"INSERT INTO nodes ( " \
" wc_id, local_relpath, op_depth, parent_relpath, repos_id, repos_path, " \
" revision, presence, depth, kind, changed_revision, changed_date, " \
" changed_author, checksum, properties, translated_size, last_mod_time, " \
" symlink_target ) " \
"SELECT wc_id, local_relpath, ?3 , parent_relpath, repos_id, " \
" repos_path, revision, presence, depth, kind, changed_revision, " \
" changed_date, changed_author, checksum, properties, translated_size, " \
" last_mod_time, symlink_target " \
"FROM nodes " \
"WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 " \
""
#define STMT_INSERT_DELETE_FROM_BASE 126
#define STMT_126_INFO {"STMT_INSERT_DELETE_FROM_BASE", NULL}
#define STMT_126 \
"INSERT INTO nodes ( " \
" wc_id, local_relpath, op_depth, parent_relpath, presence, kind) " \
"SELECT wc_id, local_relpath, ?3 , parent_relpath, " \
" 'base-deleted', kind " \
"FROM nodes " \
"WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 " \
""
#define STMT_UPDATE_OP_DEPTH_INCREASE_RECURSIVE 127
#define STMT_127_INFO {"STMT_UPDATE_OP_DEPTH_INCREASE_RECURSIVE", NULL}
#define STMT_127 \
"UPDATE nodes SET op_depth = ?3 + 1 " \
"WHERE wc_id = ?1 " \
" AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND op_depth = ?3 " \
""
#define STMT_UPDATE_OP_DEPTH_RECURSIVE 128
#define STMT_128_INFO {"STMT_UPDATE_OP_DEPTH_RECURSIVE", NULL}
#define STMT_128 \
"UPDATE nodes SET op_depth = ?4, moved_here = NULL " \
"WHERE wc_id = ?1 " \
" AND (local_relpath = ?2 OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
" AND op_depth = ?3 " \
""
#define STMT_DOES_NODE_EXIST 129
#define STMT_129_INFO {"STMT_DOES_NODE_EXIST", NULL}
#define STMT_129 \
"SELECT 1 FROM nodes WHERE wc_id = ?1 AND local_relpath = ?2 " \
"LIMIT 1 " \
""
#define STMT_HAS_SERVER_EXCLUDED_DESCENDANTS 130
#define STMT_130_INFO {"STMT_HAS_SERVER_EXCLUDED_DESCENDANTS", NULL}
#define STMT_130 \
"SELECT local_relpath FROM nodes " \
"WHERE wc_id = ?1 " \
" AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND op_depth = 0 AND presence = 'server-excluded' " \
"LIMIT 1 " \
""
#define STMT_SELECT_ALL_EXCLUDED_DESCENDANTS 131
#define STMT_131_INFO {"STMT_SELECT_ALL_EXCLUDED_DESCENDANTS", NULL}
#define STMT_131 \
"SELECT local_relpath FROM nodes " \
"WHERE wc_id = ?1 " \
" AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND op_depth = 0 " \
" AND (presence = 'server-excluded' OR presence = 'excluded') " \
""
#define STMT_INSERT_WORKING_NODE_COPY_FROM 132
#define STMT_132_INFO {"STMT_INSERT_WORKING_NODE_COPY_FROM", NULL}
#define STMT_132 \
"INSERT OR REPLACE INTO nodes ( " \
" wc_id, local_relpath, op_depth, parent_relpath, repos_id, " \
" repos_path, revision, presence, depth, moved_here, kind, changed_revision, " \
" changed_date, changed_author, checksum, properties, translated_size, " \
" last_mod_time, symlink_target, moved_to ) " \
"SELECT wc_id, ?3 , ?4 , ?5 , " \
" repos_id, repos_path, revision, ?6 , depth, " \
" ?7, kind, changed_revision, changed_date, " \
" changed_author, checksum, properties, translated_size, " \
" last_mod_time, symlink_target, " \
" (SELECT dst.moved_to FROM nodes AS dst " \
" WHERE dst.wc_id = ?1 " \
" AND dst.local_relpath = ?3 " \
" AND dst.op_depth = ?4) " \
"FROM nodes_current " \
"WHERE wc_id = ?1 AND local_relpath = ?2 " \
""
#define STMT_INSERT_WORKING_NODE_COPY_FROM_DEPTH 133
#define STMT_133_INFO {"STMT_INSERT_WORKING_NODE_COPY_FROM_DEPTH", NULL}
#define STMT_133 \
"INSERT OR REPLACE INTO nodes ( " \
" wc_id, local_relpath, op_depth, parent_relpath, repos_id, " \
" repos_path, revision, presence, depth, moved_here, kind, changed_revision, " \
" changed_date, changed_author, checksum, properties, translated_size, " \
" last_mod_time, symlink_target, moved_to ) " \
"SELECT wc_id, ?3 , ?4 , ?5 , " \
" repos_id, repos_path, revision, ?6 , depth, " \
" ?8 , kind, changed_revision, changed_date, " \
" changed_author, checksum, properties, translated_size, " \
" last_mod_time, symlink_target, " \
" (SELECT dst.moved_to FROM nodes AS dst " \
" WHERE dst.wc_id = ?1 " \
" AND dst.local_relpath = ?3 " \
" AND dst.op_depth = ?4) " \
"FROM nodes " \
"WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?7 " \
""
#define STMT_UPDATE_BASE_REVISION 134
#define STMT_134_INFO {"STMT_UPDATE_BASE_REVISION", NULL}
#define STMT_134 \
"UPDATE nodes SET revision = ?3 " \
"WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 " \
""
#define STMT_UPDATE_BASE_REPOS 135
#define STMT_135_INFO {"STMT_UPDATE_BASE_REPOS", NULL}
#define STMT_135 \
"UPDATE nodes SET repos_id = ?3, repos_path = ?4 " \
"WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 " \
""
#define STMT_ACTUAL_HAS_CHILDREN 136
#define STMT_136_INFO {"STMT_ACTUAL_HAS_CHILDREN", NULL}
#define STMT_136 \
"SELECT 1 FROM actual_node " \
"WHERE wc_id = ?1 AND parent_relpath = ?2 " \
"LIMIT 1 " \
""
#define STMT_INSERT_EXTERNAL 137
#define STMT_137_INFO {"STMT_INSERT_EXTERNAL", NULL}
#define STMT_137 \
"INSERT OR REPLACE INTO externals ( " \
" wc_id, local_relpath, parent_relpath, presence, kind, def_local_relpath, " \
" repos_id, def_repos_relpath, def_operational_revision, def_revision) " \
"VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10) " \
""
#define STMT_SELECT_EXTERNAL_INFO 138
#define STMT_138_INFO {"STMT_SELECT_EXTERNAL_INFO", NULL}
#define STMT_138 \
"SELECT presence, kind, def_local_relpath, repos_id, " \
" def_repos_relpath, def_operational_revision, def_revision " \
"FROM externals WHERE wc_id = ?1 AND local_relpath = ?2 " \
"LIMIT 1 " \
""
#define STMT_DELETE_FILE_EXTERNALS 139
#define STMT_139_INFO {"STMT_DELETE_FILE_EXTERNALS", NULL}
#define STMT_139 \
"DELETE FROM nodes " \
"WHERE wc_id = ?1 " \
" AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND op_depth = 0 " \
" AND file_external IS NOT NULL " \
""
#define STMT_DELETE_FILE_EXTERNAL_REGISTATIONS 140
#define STMT_140_INFO {"STMT_DELETE_FILE_EXTERNAL_REGISTATIONS", NULL}
#define STMT_140 \
"DELETE FROM externals " \
"WHERE wc_id = ?1 " \
" AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND kind != 'dir' " \
""
#define STMT_DELETE_EXTERNAL_REGISTATIONS 141
#define STMT_141_INFO {"STMT_DELETE_EXTERNAL_REGISTATIONS", NULL}
#define STMT_141 \
"DELETE FROM externals " \
"WHERE wc_id = ?1 " \
" AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
""
#define STMT_SELECT_COMMITTABLE_EXTERNALS_BELOW 142
#define STMT_142_INFO {"STMT_SELECT_COMMITTABLE_EXTERNALS_BELOW", NULL}
#define STMT_142 \
"SELECT local_relpath, kind, def_repos_relpath, " \
" (SELECT root FROM repository AS r WHERE r.id = e.repos_id) " \
"FROM externals e " \
"WHERE wc_id = ?1 " \
" AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND def_revision IS NULL " \
" AND repos_id = (SELECT repos_id " \
" FROM nodes AS n " \
" WHERE n.wc_id = ?1 " \
" AND n.local_relpath = '' " \
" AND n.op_depth = 0) " \
" AND ((kind='dir') " \
" OR EXISTS (SELECT 1 FROM nodes " \
" WHERE nodes.wc_id = e.wc_id " \
" AND nodes.local_relpath = e.parent_relpath)) " \
""
#define STMT_SELECT_COMMITTABLE_EXTERNALS_IMMEDIATELY_BELOW 143
#define STMT_143_INFO {"STMT_SELECT_COMMITTABLE_EXTERNALS_IMMEDIATELY_BELOW", NULL}
#define STMT_143 \
"SELECT local_relpath, kind, def_repos_relpath, " \
" (SELECT root FROM repository AS r WHERE r.id = e.repos_id) " \
"FROM externals e " \
"WHERE wc_id = ?1 " \
" AND (((e.local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((e.local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND parent_relpath = ?2 " \
" AND def_revision IS NULL " \
" AND repos_id = (SELECT repos_id " \
" FROM nodes AS n " \
" WHERE n.wc_id = ?1 " \
" AND n.local_relpath = '' " \
" AND n.op_depth = 0) " \
" AND ((kind='dir') " \
" OR EXISTS (SELECT 1 FROM nodes " \
" WHERE nodes.wc_id = e.wc_id " \
" AND nodes.local_relpath = e.parent_relpath)) " \
""
#define STMT_SELECT_EXTERNALS_DEFINED 144
#define STMT_144_INFO {"STMT_SELECT_EXTERNALS_DEFINED", NULL}
#define STMT_144 \
"SELECT local_relpath, def_local_relpath " \
"FROM externals " \
"WHERE (wc_id = ?1 AND def_local_relpath = ?2) " \
" OR (wc_id = ?1 AND (((def_local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((def_local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
""
#define STMT_DELETE_EXTERNAL 145
#define STMT_145_INFO {"STMT_DELETE_EXTERNAL", NULL}
#define STMT_145 \
"DELETE FROM externals " \
"WHERE wc_id = ?1 AND local_relpath = ?2 " \
""
#define STMT_SELECT_EXTERNAL_PROPERTIES 146
#define STMT_146_INFO {"STMT_SELECT_EXTERNAL_PROPERTIES", NULL}
#define STMT_146 \
"SELECT IFNULL((SELECT properties FROM actual_node a " \
" WHERE a.wc_id = ?1 AND A.local_relpath = n.local_relpath), " \
" properties), " \
" local_relpath, depth " \
"FROM nodes_current n " \
"WHERE wc_id = ?1 AND local_relpath = ?2 " \
" AND kind = 'dir' AND presence IN ('normal', 'incomplete') " \
"UNION ALL " \
"SELECT IFNULL((SELECT properties FROM actual_node a " \
" WHERE a.wc_id = ?1 AND A.local_relpath = n.local_relpath), " \
" properties), " \
" local_relpath, depth " \
"FROM nodes_current n " \
"WHERE wc_id = ?1 AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND kind = 'dir' AND presence IN ('normal', 'incomplete') " \
""
#define STMT_SELECT_CURRENT_PROPS_RECURSIVE 147
#define STMT_147_INFO {"STMT_SELECT_CURRENT_PROPS_RECURSIVE", NULL}
#define STMT_147 \
"SELECT IFNULL((SELECT properties FROM actual_node a " \
" WHERE a.wc_id = ?1 AND A.local_relpath = n.local_relpath), " \
" properties), " \
" local_relpath " \
"FROM nodes_current n " \
"WHERE (wc_id = ?1 AND local_relpath = ?2) " \
" OR (wc_id = ?1 AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
""
#define STMT_PRAGMA_LOCKING_MODE 148
#define STMT_148_INFO {"STMT_PRAGMA_LOCKING_MODE", NULL}
#define STMT_148 \
"PRAGMA locking_mode = exclusive " \
""
#define STMT_INSERT_ACTUAL_NODE 149
#define STMT_149_INFO {"STMT_INSERT_ACTUAL_NODE", NULL}
#define STMT_149 \
"INSERT OR REPLACE INTO actual_node ( " \
" wc_id, local_relpath, parent_relpath, properties, changelist, conflict_data) " \
"VALUES (?1, ?2, ?3, ?4, ?5, ?6) " \
""
#define STMT_UPDATE_ACTUAL_CONFLICT_DATA 150
#define STMT_150_INFO {"STMT_UPDATE_ACTUAL_CONFLICT_DATA", NULL}
#define STMT_150 \
"UPDATE actual_node SET conflict_data = ?3 " \
"WHERE wc_id = ?1 AND local_relpath = ?2 " \
""
#define STMT_INSERT_ACTUAL_CONFLICT_DATA 151
#define STMT_151_INFO {"STMT_INSERT_ACTUAL_CONFLICT_DATA", NULL}
#define STMT_151 \
"INSERT INTO actual_node (wc_id, local_relpath, conflict_data, parent_relpath) " \
"VALUES (?1, ?2, ?3, ?4) " \
""
#define STMT_SELECT_ALL_FILES 152
#define STMT_152_INFO {"STMT_SELECT_ALL_FILES", NULL}
#define STMT_152 \
"SELECT local_relpath FROM nodes_current " \
"WHERE wc_id = ?1 AND parent_relpath = ?2 AND kind = 'file' " \
""
#define STMT_UPDATE_NODE_PROPS 153
#define STMT_153_INFO {"STMT_UPDATE_NODE_PROPS", NULL}
#define STMT_153 \
"UPDATE nodes SET properties = ?4 " \
"WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3 " \
""
#define STMT_PRAGMA_TABLE_INFO_NODES 154
#define STMT_154_INFO {"STMT_PRAGMA_TABLE_INFO_NODES", NULL}
#define STMT_154 \
"PRAGMA table_info(\"NODES\") " \
""
#define STMT_CREATE_TARGET_PROP_CACHE 155
#define STMT_155_INFO {"STMT_CREATE_TARGET_PROP_CACHE", NULL}
#define STMT_155 \
"DROP TABLE IF EXISTS target_prop_cache; " \
"CREATE TEMPORARY TABLE target_prop_cache ( " \
" local_relpath TEXT NOT NULL PRIMARY KEY, " \
" kind TEXT NOT NULL, " \
" properties BLOB " \
"); " \
""
#define STMT_CACHE_TARGET_PROPS 156
#define STMT_156_INFO {"STMT_CACHE_TARGET_PROPS", NULL}
#define STMT_156 \
"INSERT INTO target_prop_cache(local_relpath, kind, properties) " \
" SELECT n.local_relpath, n.kind, " \
" IFNULL((SELECT properties FROM actual_node AS a " \
" WHERE a.wc_id = n.wc_id " \
" AND a.local_relpath = n.local_relpath), " \
" n.properties) " \
" FROM targets_list AS t " \
" JOIN nodes AS n " \
" ON n.wc_id = ?1 " \
" AND n.local_relpath = t.local_relpath " \
" AND n.op_depth = (SELECT MAX(op_depth) FROM nodes AS n3 " \
" WHERE n3.wc_id = ?1 " \
" AND n3.local_relpath = t.local_relpath) " \
" WHERE t.wc_id = ?1 " \
" AND (presence='normal' OR presence='incomplete') " \
" ORDER BY t.local_relpath " \
""
#define STMT_CACHE_TARGET_PRISTINE_PROPS 157
#define STMT_157_INFO {"STMT_CACHE_TARGET_PRISTINE_PROPS", NULL}
#define STMT_157 \
"INSERT INTO target_prop_cache(local_relpath, kind, properties) " \
" SELECT n.local_relpath, n.kind, " \
" CASE n.presence " \
" WHEN 'base-deleted' " \
" THEN (SELECT properties FROM nodes AS p " \
" WHERE p.wc_id = n.wc_id " \
" AND p.local_relpath = n.local_relpath " \
" AND p.op_depth < n.op_depth " \
" ORDER BY p.op_depth DESC ) " \
" ELSE properties END " \
" FROM targets_list AS t " \
" JOIN nodes AS n " \
" ON n.wc_id = ?1 " \
" AND n.local_relpath = t.local_relpath " \
" AND n.op_depth = (SELECT MAX(op_depth) FROM nodes AS n3 " \
" WHERE n3.wc_id = ?1 " \
" AND n3.local_relpath = t.local_relpath) " \
" WHERE t.wc_id = ?1 " \
" AND (presence = 'normal' " \
" OR presence = 'incomplete' " \
" OR presence = 'base-deleted') " \
" ORDER BY t.local_relpath " \
""
#define STMT_SELECT_ALL_TARGET_PROP_CACHE 158
#define STMT_158_INFO {"STMT_SELECT_ALL_TARGET_PROP_CACHE", NULL}
#define STMT_158 \
"SELECT local_relpath, properties FROM target_prop_cache " \
"ORDER BY local_relpath " \
""
#define STMT_DROP_TARGET_PROP_CACHE 159
#define STMT_159_INFO {"STMT_DROP_TARGET_PROP_CACHE", NULL}
#define STMT_159 \
"DROP TABLE target_prop_cache; " \
""
#define STMT_CREATE_REVERT_LIST 160
#define STMT_160_INFO {"STMT_CREATE_REVERT_LIST", NULL}
#define STMT_160 \
"DROP TABLE IF EXISTS revert_list; " \
"CREATE TEMPORARY TABLE revert_list ( " \
" local_relpath TEXT NOT NULL, " \
" actual INTEGER NOT NULL, " \
" conflict_data BLOB, " \
" notify INTEGER, " \
" op_depth INTEGER, " \
" repos_id INTEGER, " \
" kind TEXT, " \
" PRIMARY KEY (local_relpath, actual) " \
" ); " \
"DROP TRIGGER IF EXISTS trigger_revert_list_nodes; " \
"CREATE TEMPORARY TRIGGER trigger_revert_list_nodes " \
"BEFORE DELETE ON nodes " \
"BEGIN " \
" INSERT OR REPLACE INTO revert_list(local_relpath, actual, op_depth, " \
" repos_id, kind) " \
" SELECT OLD.local_relpath, 0, OLD.op_depth, OLD.repos_id, OLD.kind; " \
"END; " \
"DROP TRIGGER IF EXISTS trigger_revert_list_actual_delete; " \
"CREATE TEMPORARY TRIGGER trigger_revert_list_actual_delete " \
"BEFORE DELETE ON actual_node " \
"BEGIN " \
" INSERT OR REPLACE INTO revert_list(local_relpath, actual, conflict_data, " \
" notify) " \
" SELECT OLD.local_relpath, 1, OLD.conflict_data, " \
" CASE " \
" WHEN OLD.properties IS NOT NULL " \
" THEN 1 " \
" WHEN NOT EXISTS(SELECT 1 FROM NODES n " \
" WHERE n.wc_id = OLD.wc_id " \
" AND n.local_relpath = OLD.local_relpath) " \
" THEN 1 " \
" ELSE NULL " \
" END; " \
"END; " \
"DROP TRIGGER IF EXISTS trigger_revert_list_actual_update; " \
"CREATE TEMPORARY TRIGGER trigger_revert_list_actual_update " \
"BEFORE UPDATE ON actual_node " \
"BEGIN " \
" INSERT OR REPLACE INTO revert_list(local_relpath, actual, conflict_data, " \
" notify) " \
" SELECT OLD.local_relpath, 1, OLD.conflict_data, " \
" CASE " \
" WHEN OLD.properties IS NOT NULL " \
" THEN 1 " \
" WHEN NOT EXISTS(SELECT 1 FROM NODES n " \
" WHERE n.wc_id = OLD.wc_id " \
" AND n.local_relpath = OLD.local_relpath) " \
" THEN 1 " \
" ELSE NULL " \
" END; " \
"END " \
""
#define STMT_DROP_REVERT_LIST_TRIGGERS 161
#define STMT_161_INFO {"STMT_DROP_REVERT_LIST_TRIGGERS", NULL}
#define STMT_161 \
"DROP TRIGGER trigger_revert_list_nodes; " \
"DROP TRIGGER trigger_revert_list_actual_delete; " \
"DROP TRIGGER trigger_revert_list_actual_update " \
""
#define STMT_SELECT_REVERT_LIST 162
#define STMT_162_INFO {"STMT_SELECT_REVERT_LIST", NULL}
#define STMT_162 \
"SELECT actual, notify, kind, op_depth, repos_id, conflict_data " \
"FROM revert_list " \
"WHERE local_relpath = ?1 " \
"ORDER BY actual DESC " \
""
#define STMT_SELECT_REVERT_LIST_COPIED_CHILDREN 163
#define STMT_163_INFO {"STMT_SELECT_REVERT_LIST_COPIED_CHILDREN", NULL}
#define STMT_163 \
"SELECT local_relpath, kind " \
"FROM revert_list " \
"WHERE (((local_relpath) > (CASE (?1) WHEN '' THEN '' ELSE (?1) || '/' END)) AND ((local_relpath) < CASE (?1) WHEN '' THEN X'FFFF' ELSE (?1) || '0' END)) " \
" AND op_depth >= ?2 " \
" AND repos_id IS NOT NULL " \
"ORDER BY local_relpath " \
""
#define STMT_DELETE_REVERT_LIST 164
#define STMT_164_INFO {"STMT_DELETE_REVERT_LIST", NULL}
#define STMT_164 \
"DELETE FROM revert_list WHERE local_relpath = ?1 " \
""
#define STMT_SELECT_REVERT_LIST_RECURSIVE 165
#define STMT_165_INFO {"STMT_SELECT_REVERT_LIST_RECURSIVE", NULL}
#define STMT_165 \
"SELECT DISTINCT local_relpath " \
"FROM revert_list " \
"WHERE (local_relpath = ?1 " \
" OR (((local_relpath) > (CASE (?1) WHEN '' THEN '' ELSE (?1) || '/' END)) AND ((local_relpath) < CASE (?1) WHEN '' THEN X'FFFF' ELSE (?1) || '0' END))) " \
" AND (notify OR actual = 0) " \
"ORDER BY local_relpath " \
""
#define STMT_DELETE_REVERT_LIST_RECURSIVE 166
#define STMT_166_INFO {"STMT_DELETE_REVERT_LIST_RECURSIVE", NULL}
#define STMT_166 \
"DELETE FROM revert_list " \
"WHERE (local_relpath = ?1 " \
" OR (((local_relpath) > (CASE (?1) WHEN '' THEN '' ELSE (?1) || '/' END)) AND ((local_relpath) < CASE (?1) WHEN '' THEN X'FFFF' ELSE (?1) || '0' END))) " \
""
#define STMT_DROP_REVERT_LIST 167
#define STMT_167_INFO {"STMT_DROP_REVERT_LIST", NULL}
#define STMT_167 \
"DROP TABLE IF EXISTS revert_list " \
""
#define STMT_CREATE_DELETE_LIST 168
#define STMT_168_INFO {"STMT_CREATE_DELETE_LIST", NULL}
#define STMT_168 \
"DROP TABLE IF EXISTS delete_list; " \
"CREATE TEMPORARY TABLE delete_list ( " \
" local_relpath TEXT PRIMARY KEY NOT NULL UNIQUE " \
" ) " \
""
#define STMT_INSERT_DELETE_LIST 169
#define STMT_169_INFO {"STMT_INSERT_DELETE_LIST", NULL}
#define STMT_169 \
"INSERT INTO delete_list(local_relpath) " \
"SELECT local_relpath FROM nodes AS n " \
"WHERE wc_id = ?1 " \
" AND (local_relpath = ?2 " \
" OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
" AND op_depth >= ?3 " \
" AND op_depth = (SELECT MAX(s.op_depth) FROM nodes AS s " \
" WHERE s.wc_id = ?1 " \
" AND s.local_relpath = n.local_relpath) " \
" AND presence NOT IN ('base-deleted', 'not-present', 'excluded', 'server-excluded') " \
" AND file_external IS NULL " \
""
#define STMT_SELECT_DELETE_LIST 170
#define STMT_170_INFO {"STMT_SELECT_DELETE_LIST", NULL}
#define STMT_170 \
"SELECT local_relpath FROM delete_list " \
"ORDER BY local_relpath " \
""
#define STMT_FINALIZE_DELETE 171
#define STMT_171_INFO {"STMT_FINALIZE_DELETE", NULL}
#define STMT_171 \
"DROP TABLE IF EXISTS delete_list " \
""
#define STMT_CREATE_UPDATE_MOVE_LIST 172
#define STMT_172_INFO {"STMT_CREATE_UPDATE_MOVE_LIST", NULL}
#define STMT_172 \
"DROP TABLE IF EXISTS update_move_list; " \
"CREATE TEMPORARY TABLE update_move_list ( " \
" local_relpath TEXT PRIMARY KEY NOT NULL UNIQUE, " \
" action INTEGER NOT NULL, " \
" kind INTEGER NOT NULL, " \
" content_state INTEGER NOT NULL, " \
" prop_state INTEGER NOT NULL " \
" ) " \
""
#define STMT_INSERT_UPDATE_MOVE_LIST 173
#define STMT_173_INFO {"STMT_INSERT_UPDATE_MOVE_LIST", NULL}
#define STMT_173 \
"INSERT INTO update_move_list(local_relpath, action, kind, content_state, " \
" prop_state) " \
"VALUES (?1, ?2, ?3, ?4, ?5) " \
""
#define STMT_SELECT_UPDATE_MOVE_LIST 174
#define STMT_174_INFO {"STMT_SELECT_UPDATE_MOVE_LIST", NULL}
#define STMT_174 \
"SELECT local_relpath, action, kind, content_state, prop_state " \
"FROM update_move_list " \
"ORDER BY local_relpath " \
""
#define STMT_FINALIZE_UPDATE_MOVE 175
#define STMT_175_INFO {"STMT_FINALIZE_UPDATE_MOVE", NULL}
#define STMT_175 \
"DROP TABLE IF EXISTS update_move_list " \
""
#define STMT_SELECT_MIN_MAX_REVISIONS 176
#define STMT_176_INFO {"STMT_SELECT_MIN_MAX_REVISIONS", NULL}
#define STMT_176 \
"SELECT MIN(revision), MAX(revision), " \
" MIN(changed_revision), MAX(changed_revision) FROM nodes " \
" WHERE wc_id = ?1 " \
" AND (local_relpath = ?2 " \
" OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
" AND presence IN ('normal', 'incomplete') " \
" AND file_external IS NULL " \
" AND op_depth = 0 " \
""
#define STMT_HAS_SPARSE_NODES 177
#define STMT_177_INFO {"STMT_HAS_SPARSE_NODES", NULL}
#define STMT_177 \
"SELECT 1 FROM nodes " \
"WHERE wc_id = ?1 " \
" AND (local_relpath = ?2 " \
" OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
" AND op_depth = 0 " \
" AND (presence IN ('server-excluded', 'excluded') " \
" OR depth NOT IN ('infinity', 'unknown')) " \
" AND file_external IS NULL " \
"LIMIT 1 " \
""
#define STMT_SUBTREE_HAS_TREE_MODIFICATIONS 178
#define STMT_178_INFO {"STMT_SUBTREE_HAS_TREE_MODIFICATIONS", NULL}
#define STMT_178 \
"SELECT 1 FROM nodes " \
"WHERE wc_id = ?1 " \
" AND (local_relpath = ?2 " \
" OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
" AND op_depth > 0 " \
"LIMIT 1 " \
""
#define STMT_SUBTREE_HAS_PROP_MODIFICATIONS 179
#define STMT_179_INFO {"STMT_SUBTREE_HAS_PROP_MODIFICATIONS", NULL}
#define STMT_179 \
"SELECT 1 FROM actual_node " \
"WHERE wc_id = ?1 " \
" AND (local_relpath = ?2 " \
" OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
" AND properties IS NOT NULL " \
"LIMIT 1 " \
""
#define STMT_HAS_SWITCHED 180
#define STMT_180_INFO {"STMT_HAS_SWITCHED", NULL}
#define STMT_180 \
"SELECT 1 " \
"FROM nodes " \
"WHERE wc_id = ?1 " \
" AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND op_depth = 0 " \
" AND file_external IS NULL " \
" AND presence IN ('normal', 'incomplete') " \
" AND repos_path IS NOT (CASE WHEN (?2) = '' THEN (CASE WHEN (?3) = '' THEN (local_relpath) WHEN (local_relpath) = '' THEN (?3) ELSE (?3) || '/' || (local_relpath) END) WHEN (?3) = '' THEN (CASE WHEN (?2) = '' THEN (local_relpath) WHEN SUBSTR((local_relpath), 1, LENGTH(?2)) = (?2) THEN CASE WHEN LENGTH(?2) = LENGTH(local_relpath) THEN '' WHEN SUBSTR((local_relpath), LENGTH(?2)+1, 1) = '/' THEN SUBSTR((local_relpath), LENGTH(?2)+2) END END) WHEN SUBSTR((local_relpath), 1, LENGTH(?2)) = (?2) THEN CASE WHEN LENGTH(?2) = LENGTH(local_relpath) THEN (?3) WHEN SUBSTR((local_relpath), LENGTH(?2)+1, 1) = '/' THEN (?3) || SUBSTR((local_relpath), LENGTH(?2)+1) END END) " \
"LIMIT 1 " \
""
#define STMT_SELECT_BASE_FILES_RECURSIVE 181
#define STMT_181_INFO {"STMT_SELECT_BASE_FILES_RECURSIVE", NULL}
#define STMT_181 \
"SELECT local_relpath, translated_size, last_mod_time FROM nodes AS n " \
"WHERE wc_id = ?1 " \
" AND (local_relpath = ?2 " \
" OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
" AND op_depth = 0 " \
" AND kind='file' " \
" AND presence='normal' " \
" AND file_external IS NULL " \
""
#define STMT_SELECT_MOVED_FROM_RELPATH 182
#define STMT_182_INFO {"STMT_SELECT_MOVED_FROM_RELPATH", NULL}
#define STMT_182 \
"SELECT local_relpath, op_depth FROM nodes " \
"WHERE wc_id = ?1 AND moved_to = ?2 AND op_depth > 0 " \
""
#define STMT_UPDATE_MOVED_TO_RELPATH 183
#define STMT_183_INFO {"STMT_UPDATE_MOVED_TO_RELPATH", NULL}
#define STMT_183 \
"UPDATE nodes SET moved_to = ?4 " \
"WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3 " \
""
#define STMT_CLEAR_MOVED_TO_RELPATH 184
#define STMT_184_INFO {"STMT_CLEAR_MOVED_TO_RELPATH", NULL}
#define STMT_184 \
"UPDATE nodes SET moved_to = NULL " \
"WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3 " \
""
#define STMT_CLEAR_MOVED_HERE_RECURSIVE 185
#define STMT_185_INFO {"STMT_CLEAR_MOVED_HERE_RECURSIVE", NULL}
#define STMT_185 \
"UPDATE nodes SET moved_here = NULL " \
"WHERE wc_id = ?1 " \
" AND (local_relpath = ?2 OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
" AND op_depth = ?3 " \
""
#define STMT_SELECT_MOVED_HERE_CHILDREN 186
#define STMT_186_INFO {"STMT_SELECT_MOVED_HERE_CHILDREN", NULL}
#define STMT_186 \
"SELECT moved_to, local_relpath FROM nodes " \
"WHERE wc_id = ?1 AND op_depth > 0 " \
" AND (((moved_to) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((moved_to) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
""
#define STMT_SELECT_MOVED_FOR_DELETE 187
#define STMT_187_INFO {"STMT_SELECT_MOVED_FOR_DELETE", NULL}
#define STMT_187 \
"SELECT local_relpath, moved_to, op_depth, " \
" (SELECT CASE WHEN r.moved_here THEN r.op_depth END FROM nodes r " \
" WHERE r.wc_id = ?1 " \
" AND r.local_relpath = n.local_relpath " \
" AND r.op_depth < n.op_depth " \
" ORDER BY r.op_depth DESC LIMIT 1) AS moved_here_op_depth " \
" FROM nodes n " \
"WHERE wc_id = ?1 " \
" AND (local_relpath = ?2 OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
" AND moved_to IS NOT NULL " \
" AND op_depth >= ?3 " \
""
#define STMT_SELECT_MOVED_FROM_FOR_DELETE 188
#define STMT_188_INFO {"STMT_SELECT_MOVED_FROM_FOR_DELETE", NULL}
#define STMT_188 \
"SELECT local_relpath, op_depth, " \
" (SELECT CASE WHEN r.moved_here THEN r.op_depth END FROM nodes r " \
" WHERE r.wc_id = ?1 " \
" AND r.local_relpath = n.local_relpath " \
" AND r.op_depth < n.op_depth " \
" ORDER BY r.op_depth DESC LIMIT 1) AS moved_here_op_depth " \
" FROM nodes n " \
"WHERE wc_id = ?1 AND moved_to = ?2 AND op_depth > 0 " \
""
#define STMT_UPDATE_MOVED_TO_DESCENDANTS 189
#define STMT_189_INFO {"STMT_UPDATE_MOVED_TO_DESCENDANTS", NULL}
#define STMT_189 \
"UPDATE nodes SET moved_to = (CASE WHEN (?2) = '' THEN (CASE WHEN (?3) = '' THEN (moved_to) WHEN (moved_to) = '' THEN (?3) ELSE (?3) || '/' || (moved_to) END) WHEN (?3) = '' THEN (CASE WHEN (?2) = '' THEN (moved_to) WHEN SUBSTR((moved_to), 1, LENGTH(?2)) = (?2) THEN CASE WHEN LENGTH(?2) = LENGTH(moved_to) THEN '' WHEN SUBSTR((moved_to), LENGTH(?2)+1, 1) = '/' THEN SUBSTR((moved_to), LENGTH(?2)+2) END END) WHEN SUBSTR((moved_to), 1, LENGTH(?2)) = (?2) THEN CASE WHEN LENGTH(?2) = LENGTH(moved_to) THEN (?3) WHEN SUBSTR((moved_to), LENGTH(?2)+1, 1) = '/' THEN (?3) || SUBSTR((moved_to), LENGTH(?2)+1) END END) " \
" WHERE wc_id = ?1 " \
" AND (((moved_to) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((moved_to) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
""
#define STMT_CLEAR_MOVED_TO_DESCENDANTS 190
#define STMT_190_INFO {"STMT_CLEAR_MOVED_TO_DESCENDANTS", NULL}
#define STMT_190 \
"UPDATE nodes SET moved_to = NULL " \
" WHERE wc_id = ?1 " \
" AND (((moved_to) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((moved_to) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
""
#define STMT_SELECT_MOVED_PAIR2 191
#define STMT_191_INFO {"STMT_SELECT_MOVED_PAIR2", NULL}
#define STMT_191 \
"SELECT local_relpath, moved_to, op_depth FROM nodes " \
"WHERE wc_id = ?1 " \
" AND (local_relpath = ?2 OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
" AND moved_to IS NOT NULL " \
" AND NOT (((moved_to) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((moved_to) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND op_depth >= (SELECT MAX(op_depth) FROM nodes o " \
" WHERE o.wc_id = ?1 " \
" AND o.local_relpath = ?2) " \
""
#define STMT_SELECT_MOVED_PAIR3 192
#define STMT_192_INFO {"STMT_SELECT_MOVED_PAIR3", NULL}
#define STMT_192 \
"SELECT local_relpath, moved_to, op_depth, kind FROM nodes " \
"WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > ?3 " \
" AND moved_to IS NOT NULL " \
"UNION ALL " \
"SELECT local_relpath, moved_to, op_depth, kind FROM nodes " \
"WHERE wc_id = ?1 " \
" AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND op_depth > ?3 " \
" AND moved_to IS NOT NULL " \
"ORDER BY local_relpath, op_depth " \
""
#define STMT_SELECT_MOVED_OUTSIDE 193
#define STMT_193_INFO {"STMT_SELECT_MOVED_OUTSIDE", NULL}
#define STMT_193 \
"SELECT local_relpath, moved_to, op_depth FROM nodes " \
"WHERE wc_id = ?1 " \
" AND (local_relpath = ?2 OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
" AND op_depth >= ?3 " \
" AND moved_to IS NOT NULL " \
" AND NOT (((moved_to) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((moved_to) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
""
#define STMT_SELECT_OP_DEPTH_MOVED_PAIR 194
#define STMT_194_INFO {"STMT_SELECT_OP_DEPTH_MOVED_PAIR", NULL}
#define STMT_194 \
"SELECT n.local_relpath, n.moved_to, " \
" (SELECT o.repos_path FROM nodes AS o " \
" WHERE o.wc_id = n.wc_id " \
" AND o.local_relpath = n.local_relpath " \
" AND o.op_depth < ?3 ORDER BY o.op_depth DESC LIMIT 1) " \
"FROM nodes AS n " \
"WHERE n.wc_id = ?1 " \
" AND (((n.local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((n.local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND n.op_depth = ?3 " \
" AND n.moved_to IS NOT NULL " \
""
#define STMT_SELECT_MOVED_DESCENDANTS 195
#define STMT_195_INFO {"STMT_SELECT_MOVED_DESCENDANTS", NULL}
#define STMT_195 \
"SELECT n.local_relpath, h.moved_to " \
"FROM nodes n, nodes h " \
"WHERE n.wc_id = ?1 " \
" AND h.wc_id = ?1 " \
" AND (((n.local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((n.local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND h.local_relpath = n.local_relpath " \
" AND n.op_depth = ?3 " \
" AND h.op_depth = (SELECT MIN(o.op_depth) " \
" FROM nodes o " \
" WHERE o.wc_id = ?1 " \
" AND o.local_relpath = n.local_relpath " \
" AND o.op_depth > ?3) " \
" AND h.moved_to IS NOT NULL " \
""
#define STMT_COMMIT_UPDATE_ORIGIN 196
#define STMT_196_INFO {"STMT_COMMIT_UPDATE_ORIGIN", NULL}
#define STMT_196 \
"UPDATE nodes SET repos_id = ?4, " \
" repos_path = ?5 || SUBSTR(local_relpath, LENGTH(?2)+1), " \
" revision = ?6 " \
"WHERE wc_id = ?1 " \
" AND (local_relpath = ?2 " \
" OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \
" AND op_depth = ?3 " \
""
#define STMT_HAS_LAYER_BETWEEN 197
#define STMT_197_INFO {"STMT_HAS_LAYER_BETWEEN", NULL}
#define STMT_197 \
"SELECT 1 FROM NODES " \
"WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > ?3 AND op_depth < ?4 " \
""
#define STMT_SELECT_REPOS_PATH_REVISION 198
#define STMT_198_INFO {"STMT_SELECT_REPOS_PATH_REVISION", NULL}
#define STMT_198 \
"SELECT local_relpath, repos_path, revision FROM nodes " \
"WHERE wc_id = ?1 " \
" AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND op_depth = 0 " \
"ORDER BY local_relpath " \
""
#define STMT_SELECT_HAS_NON_FILE_CHILDREN 199
#define STMT_199_INFO {"STMT_SELECT_HAS_NON_FILE_CHILDREN", NULL}
#define STMT_199 \
"SELECT 1 FROM nodes " \
"WHERE wc_id = ?1 AND parent_relpath = ?2 AND op_depth = 0 AND kind != 'file' " \
""
#define STMT_SELECT_HAS_GRANDCHILDREN 200
#define STMT_200_INFO {"STMT_SELECT_HAS_GRANDCHILDREN", NULL}
#define STMT_200 \
"SELECT 1 FROM nodes " \
"WHERE wc_id = ?1 " \
" AND (((parent_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((parent_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND op_depth = 0 " \
" AND file_external IS NULL " \
""
#define STMT_SELECT_ALL_NODES 201
#define STMT_201_INFO {"STMT_SELECT_ALL_NODES", NULL}
#define STMT_201 \
"SELECT op_depth, local_relpath, parent_relpath, file_external FROM nodes " \
"WHERE wc_id = ?1 " \
""
#define STMT_SELECT_IPROPS 202
#define STMT_202_INFO {"STMT_SELECT_IPROPS", NULL}
#define STMT_202 \
"SELECT inherited_props FROM nodes " \
"WHERE wc_id = ?1 " \
" AND local_relpath = ?2 " \
" AND op_depth = 0 " \
""
#define STMT_UPDATE_IPROP 203
#define STMT_203_INFO {"STMT_UPDATE_IPROP", NULL}
#define STMT_203 \
"UPDATE nodes " \
"SET inherited_props = ?3 " \
"WHERE (wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0) " \
""
#define STMT_SELECT_IPROPS_NODE 204
#define STMT_204_INFO {"STMT_SELECT_IPROPS_NODE", NULL}
#define STMT_204 \
"SELECT local_relpath, repos_path FROM nodes " \
"WHERE wc_id = ?1 " \
" AND local_relpath = ?2 " \
" AND op_depth = 0 " \
" AND (inherited_props not null) " \
""
#define STMT_SELECT_IPROPS_RECURSIVE 205
#define STMT_205_INFO {"STMT_SELECT_IPROPS_RECURSIVE", NULL}
#define STMT_205 \
"SELECT local_relpath, repos_path FROM nodes " \
"WHERE wc_id = ?1 " \
" AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \
" AND op_depth = 0 " \
" AND (inherited_props not null) " \
""
#define STMT_SELECT_IPROPS_CHILDREN 206
#define STMT_206_INFO {"STMT_SELECT_IPROPS_CHILDREN", NULL}
#define STMT_206 \
"SELECT local_relpath, repos_path FROM nodes " \
"WHERE wc_id = ?1 " \
" AND parent_relpath = ?2 " \
" AND op_depth = 0 " \
" AND (inherited_props not null) " \
""
-#define STMT_CREATE_SCHEMA 207
-#define STMT_207_INFO {"STMT_CREATE_SCHEMA", NULL}
+#define STMT_HAVE_STAT1_TABLE 207
+#define STMT_207_INFO {"STMT_HAVE_STAT1_TABLE", NULL}
#define STMT_207 \
+ "SELECT 1 FROM sqlite_master WHERE name='sqlite_stat1' AND type='table' " \
+ "LIMIT 1 " \
+ ""
+
+#define STMT_CREATE_SCHEMA 208
+#define STMT_208_INFO {"STMT_CREATE_SCHEMA", NULL}
+#define STMT_208 \
"CREATE TABLE REPOSITORY ( " \
" id INTEGER PRIMARY KEY AUTOINCREMENT, " \
" root TEXT UNIQUE NOT NULL, " \
" uuid TEXT NOT NULL " \
" ); " \
"CREATE INDEX I_UUID ON REPOSITORY (uuid); " \
"CREATE INDEX I_ROOT ON REPOSITORY (root); " \
"CREATE TABLE WCROOT ( " \
" id INTEGER PRIMARY KEY AUTOINCREMENT, " \
" local_abspath TEXT UNIQUE " \
" ); " \
"CREATE UNIQUE INDEX I_LOCAL_ABSPATH ON WCROOT (local_abspath); " \
"CREATE TABLE PRISTINE ( " \
" checksum TEXT NOT NULL PRIMARY KEY, " \
" compression INTEGER, " \
" size INTEGER NOT NULL, " \
" refcount INTEGER NOT NULL, " \
" md5_checksum TEXT NOT NULL " \
" ); " \
"CREATE INDEX I_PRISTINE_MD5 ON PRISTINE (md5_checksum); " \
"CREATE TABLE ACTUAL_NODE ( " \
" wc_id INTEGER NOT NULL REFERENCES WCROOT (id), " \
" local_relpath TEXT NOT NULL, " \
" parent_relpath TEXT, " \
" properties BLOB, " \
" conflict_old TEXT, " \
" conflict_new TEXT, " \
" conflict_working TEXT, " \
" prop_reject TEXT, " \
" changelist TEXT, " \
" text_mod TEXT, " \
" tree_conflict_data TEXT, " \
" conflict_data BLOB, " \
" older_checksum TEXT REFERENCES PRISTINE (checksum), " \
" left_checksum TEXT REFERENCES PRISTINE (checksum), " \
" right_checksum TEXT REFERENCES PRISTINE (checksum), " \
" PRIMARY KEY (wc_id, local_relpath) " \
" ); " \
"CREATE UNIQUE INDEX I_ACTUAL_PARENT ON ACTUAL_NODE (wc_id, parent_relpath, " \
" local_relpath); " \
"CREATE TABLE LOCK ( " \
" repos_id INTEGER NOT NULL REFERENCES REPOSITORY (id), " \
" repos_relpath TEXT NOT NULL, " \
" lock_token TEXT NOT NULL, " \
" lock_owner TEXT, " \
" lock_comment TEXT, " \
" lock_date INTEGER, " \
" PRIMARY KEY (repos_id, repos_relpath) " \
" ); " \
"CREATE TABLE WORK_QUEUE ( " \
" id INTEGER PRIMARY KEY AUTOINCREMENT, " \
" work BLOB NOT NULL " \
" ); " \
"CREATE TABLE WC_LOCK ( " \
" wc_id INTEGER NOT NULL REFERENCES WCROOT (id), " \
" local_dir_relpath TEXT NOT NULL, " \
" locked_levels INTEGER NOT NULL DEFAULT -1, " \
" PRIMARY KEY (wc_id, local_dir_relpath) " \
" ); " \
"PRAGMA user_version = " \
APR_STRINGIFY(SVN_WC__VERSION) \
"; " \
""
-#define STMT_CREATE_NODES 208
-#define STMT_208_INFO {"STMT_CREATE_NODES", NULL}
-#define STMT_208 \
+#define STMT_CREATE_NODES 209
+#define STMT_209_INFO {"STMT_CREATE_NODES", NULL}
+#define STMT_209 \
"CREATE TABLE NODES ( " \
" wc_id INTEGER NOT NULL REFERENCES WCROOT (id), " \
" local_relpath TEXT NOT NULL, " \
" op_depth INTEGER NOT NULL, " \
" parent_relpath TEXT, " \
" repos_id INTEGER REFERENCES REPOSITORY (id), " \
" repos_path TEXT, " \
" revision INTEGER, " \
" presence TEXT NOT NULL, " \
" moved_here INTEGER, " \
" moved_to TEXT, " \
" kind TEXT NOT NULL, " \
" properties BLOB, " \
" depth TEXT, " \
" checksum TEXT REFERENCES PRISTINE (checksum), " \
" symlink_target TEXT, " \
" changed_revision INTEGER, " \
" changed_date INTEGER, " \
" changed_author TEXT, " \
" translated_size INTEGER, " \
" last_mod_time INTEGER, " \
" dav_cache BLOB, " \
" file_external INTEGER, " \
" inherited_props BLOB, " \
" PRIMARY KEY (wc_id, local_relpath, op_depth) " \
" ); " \
"CREATE UNIQUE INDEX I_NODES_PARENT ON NODES (wc_id, parent_relpath, " \
" local_relpath, op_depth); " \
"CREATE UNIQUE INDEX I_NODES_MOVED ON NODES (wc_id, moved_to, op_depth); " \
"CREATE VIEW NODES_CURRENT AS " \
" SELECT * FROM nodes AS n " \
" WHERE op_depth = (SELECT MAX(op_depth) FROM nodes AS n2 " \
" WHERE n2.wc_id = n.wc_id " \
" AND n2.local_relpath = n.local_relpath); " \
"CREATE VIEW NODES_BASE AS " \
" SELECT * FROM nodes " \
" WHERE op_depth = 0; " \
""
-#define STMT_CREATE_NODES_TRIGGERS 209
-#define STMT_209_INFO {"STMT_CREATE_NODES_TRIGGERS", NULL}
-#define STMT_209 \
+#define STMT_CREATE_NODES_TRIGGERS 210
+#define STMT_210_INFO {"STMT_CREATE_NODES_TRIGGERS", NULL}
+#define STMT_210 \
"CREATE TRIGGER nodes_insert_trigger " \
"AFTER INSERT ON nodes " \
"WHEN NEW.checksum IS NOT NULL " \
"BEGIN " \
" UPDATE pristine SET refcount = refcount + 1 " \
" WHERE checksum = NEW.checksum; " \
"END; " \
"CREATE TRIGGER nodes_delete_trigger " \
"AFTER DELETE ON nodes " \
"WHEN OLD.checksum IS NOT NULL " \
"BEGIN " \
" UPDATE pristine SET refcount = refcount - 1 " \
" WHERE checksum = OLD.checksum; " \
"END; " \
"CREATE TRIGGER nodes_update_checksum_trigger " \
"AFTER UPDATE OF checksum ON nodes " \
"WHEN NEW.checksum IS NOT OLD.checksum " \
"BEGIN " \
" UPDATE pristine SET refcount = refcount + 1 " \
" WHERE checksum = NEW.checksum; " \
" UPDATE pristine SET refcount = refcount - 1 " \
" WHERE checksum = OLD.checksum; " \
"END; " \
""
-#define STMT_CREATE_EXTERNALS 210
-#define STMT_210_INFO {"STMT_CREATE_EXTERNALS", NULL}
-#define STMT_210 \
+#define STMT_CREATE_EXTERNALS 211
+#define STMT_211_INFO {"STMT_CREATE_EXTERNALS", NULL}
+#define STMT_211 \
"CREATE TABLE EXTERNALS ( " \
" wc_id INTEGER NOT NULL REFERENCES WCROOT (id), " \
" local_relpath TEXT NOT NULL, " \
" parent_relpath TEXT NOT NULL, " \
" repos_id INTEGER NOT NULL REFERENCES REPOSITORY (id), " \
" presence TEXT NOT NULL, " \
" kind TEXT NOT NULL, " \
" def_local_relpath TEXT NOT NULL, " \
" def_repos_relpath TEXT NOT NULL, " \
" def_operational_revision TEXT, " \
" def_revision TEXT, " \
" PRIMARY KEY (wc_id, local_relpath) " \
"); " \
"CREATE UNIQUE INDEX I_EXTERNALS_DEFINED ON EXTERNALS (wc_id, " \
" def_local_relpath, " \
" local_relpath); " \
""
-#define STMT_INSTALL_SCHEMA_STATISTICS 211
-#define STMT_211_INFO {"STMT_INSTALL_SCHEMA_STATISTICS", NULL}
-#define STMT_211 \
+#define STMT_INSTALL_SCHEMA_STATISTICS 212
+#define STMT_212_INFO {"STMT_INSTALL_SCHEMA_STATISTICS", NULL}
+#define STMT_212 \
"ANALYZE sqlite_master; " \
"DELETE FROM sqlite_stat1 " \
- "WHERE tbl in ('NODES', 'ACTUAL_NODE', 'LOCK', 'WC_LOCK'); " \
- "INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES " \
+ "WHERE tbl in ('NODES', 'ACTUAL_NODE', 'LOCK', 'WC_LOCK', 'EXTERNALS'); " \
+ "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \
" ('NODES', 'sqlite_autoindex_NODES_1', '8000 8000 2 1'); " \
- "INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES " \
+ "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \
" ('NODES', 'I_NODES_PARENT', '8000 8000 10 2 1'); " \
- "INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES " \
+ "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \
" ('NODES', 'I_NODES_MOVED', '8000 8000 1 1'); " \
- "INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES " \
+ "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \
" ('ACTUAL_NODE', 'sqlite_autoindex_ACTUAL_NODE_1', '8000 8000 1'); " \
- "INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES " \
+ "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \
" ('ACTUAL_NODE', 'I_ACTUAL_PARENT', '8000 8000 10 1'); " \
- "INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES " \
+ "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \
" ('LOCK', 'sqlite_autoindex_LOCK_1', '100 100 1'); " \
- "INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES " \
+ "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \
" ('WC_LOCK', 'sqlite_autoindex_WC_LOCK_1', '100 100 1'); " \
+ "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \
+ " ('EXTERNALS','sqlite_autoindex_EXTERNALS_1', '100 100 1'); " \
+ "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \
+ " ('EXTERNALS','I_EXTERNALS_DEFINED', '100 100 3 1'); " \
"ANALYZE sqlite_master; " \
""
-#define STMT_UPGRADE_TO_20 212
-#define STMT_212_INFO {"STMT_UPGRADE_TO_20", NULL}
-#define STMT_212 \
+#define STMT_UPGRADE_TO_20 213
+#define STMT_213_INFO {"STMT_UPGRADE_TO_20", NULL}
+#define STMT_213 \
"UPDATE BASE_NODE SET checksum = (SELECT checksum FROM pristine " \
" WHERE md5_checksum = BASE_NODE.checksum) " \
"WHERE EXISTS (SELECT 1 FROM pristine WHERE md5_checksum = BASE_NODE.checksum); " \
"UPDATE WORKING_NODE SET checksum = (SELECT checksum FROM pristine " \
" WHERE md5_checksum = WORKING_NODE.checksum) " \
"WHERE EXISTS (SELECT 1 FROM pristine " \
" WHERE md5_checksum = WORKING_NODE.checksum); " \
"INSERT INTO NODES ( " \
" wc_id, local_relpath, op_depth, parent_relpath, " \
" repos_id, repos_path, revision, " \
" presence, depth, moved_here, moved_to, kind, " \
" changed_revision, changed_date, changed_author, " \
" checksum, properties, translated_size, last_mod_time, " \
" dav_cache, symlink_target, file_external ) " \
"SELECT wc_id, local_relpath, 0 , parent_relpath, " \
" repos_id, repos_relpath, revnum, " \
" presence, depth, NULL , NULL , kind, " \
" changed_rev, changed_date, changed_author, " \
" checksum, properties, translated_size, last_mod_time, " \
" dav_cache, symlink_target, file_external " \
"FROM BASE_NODE; " \
"INSERT INTO NODES ( " \
" wc_id, local_relpath, op_depth, parent_relpath, " \
" repos_id, repos_path, revision, " \
" presence, depth, moved_here, moved_to, kind, " \
" changed_revision, changed_date, changed_author, " \
" checksum, properties, translated_size, last_mod_time, " \
" dav_cache, symlink_target, file_external ) " \
"SELECT wc_id, local_relpath, 2 , parent_relpath, " \
" copyfrom_repos_id, copyfrom_repos_path, copyfrom_revnum, " \
" presence, depth, NULL , NULL , kind, " \
" changed_rev, changed_date, changed_author, " \
" checksum, properties, translated_size, last_mod_time, " \
" NULL , symlink_target, NULL " \
"FROM WORKING_NODE; " \
"DROP TABLE BASE_NODE; " \
"DROP TABLE WORKING_NODE; " \
"PRAGMA user_version = 20; " \
""
-#define STMT_UPGRADE_TO_21 213
-#define STMT_213_INFO {"STMT_UPGRADE_TO_21", NULL}
-#define STMT_213 \
+#define STMT_UPGRADE_TO_21 214
+#define STMT_214_INFO {"STMT_UPGRADE_TO_21", NULL}
+#define STMT_214 \
"PRAGMA user_version = 21; " \
""
-#define STMT_UPGRADE_21_SELECT_OLD_TREE_CONFLICT 214
-#define STMT_214_INFO {"STMT_UPGRADE_21_SELECT_OLD_TREE_CONFLICT", NULL}
-#define STMT_214 \
+#define STMT_UPGRADE_21_SELECT_OLD_TREE_CONFLICT 215
+#define STMT_215_INFO {"STMT_UPGRADE_21_SELECT_OLD_TREE_CONFLICT", NULL}
+#define STMT_215 \
"SELECT wc_id, local_relpath, tree_conflict_data " \
"FROM actual_node " \
"WHERE tree_conflict_data IS NOT NULL " \
""
-#define STMT_UPGRADE_21_ERASE_OLD_CONFLICTS 215
-#define STMT_215_INFO {"STMT_UPGRADE_21_ERASE_OLD_CONFLICTS", NULL}
-#define STMT_215 \
+#define STMT_UPGRADE_21_ERASE_OLD_CONFLICTS 216
+#define STMT_216_INFO {"STMT_UPGRADE_21_ERASE_OLD_CONFLICTS", NULL}
+#define STMT_216 \
"UPDATE actual_node SET tree_conflict_data = NULL " \
""
-#define STMT_UPGRADE_TO_22 216
-#define STMT_216_INFO {"STMT_UPGRADE_TO_22", NULL}
-#define STMT_216 \
+#define STMT_UPGRADE_TO_22 217
+#define STMT_217_INFO {"STMT_UPGRADE_TO_22", NULL}
+#define STMT_217 \
"UPDATE actual_node SET tree_conflict_data = conflict_data; " \
"UPDATE actual_node SET conflict_data = NULL; " \
"PRAGMA user_version = 22; " \
""
-#define STMT_UPGRADE_TO_23 217
-#define STMT_217_INFO {"STMT_UPGRADE_TO_23", NULL}
-#define STMT_217 \
+#define STMT_UPGRADE_TO_23 218
+#define STMT_218_INFO {"STMT_UPGRADE_TO_23", NULL}
+#define STMT_218 \
"PRAGMA user_version = 23; " \
""
-#define STMT_UPGRADE_23_HAS_WORKING_NODES 218
-#define STMT_218_INFO {"STMT_UPGRADE_23_HAS_WORKING_NODES", NULL}
-#define STMT_218 \
+#define STMT_UPGRADE_23_HAS_WORKING_NODES 219
+#define STMT_219_INFO {"STMT_UPGRADE_23_HAS_WORKING_NODES", NULL}
+#define STMT_219 \
"SELECT 1 FROM nodes WHERE op_depth > 0 " \
"LIMIT 1 " \
""
-#define STMT_UPGRADE_TO_24 219
-#define STMT_219_INFO {"STMT_UPGRADE_TO_24", NULL}
-#define STMT_219 \
+#define STMT_UPGRADE_TO_24 220
+#define STMT_220_INFO {"STMT_UPGRADE_TO_24", NULL}
+#define STMT_220 \
"UPDATE pristine SET refcount = " \
" (SELECT COUNT(*) FROM nodes " \
" WHERE checksum = pristine.checksum ); " \
"PRAGMA user_version = 24; " \
""
-#define STMT_UPGRADE_TO_25 220
-#define STMT_220_INFO {"STMT_UPGRADE_TO_25", NULL}
-#define STMT_220 \
+#define STMT_UPGRADE_TO_25 221
+#define STMT_221_INFO {"STMT_UPGRADE_TO_25", NULL}
+#define STMT_221 \
"DROP VIEW IF EXISTS NODES_CURRENT; " \
"CREATE VIEW NODES_CURRENT AS " \
" SELECT * FROM nodes " \
" JOIN (SELECT wc_id, local_relpath, MAX(op_depth) AS op_depth FROM nodes " \
" GROUP BY wc_id, local_relpath) AS filter " \
" ON nodes.wc_id = filter.wc_id " \
" AND nodes.local_relpath = filter.local_relpath " \
" AND nodes.op_depth = filter.op_depth; " \
"PRAGMA user_version = 25; " \
""
-#define STMT_UPGRADE_TO_26 221
-#define STMT_221_INFO {"STMT_UPGRADE_TO_26", NULL}
-#define STMT_221 \
+#define STMT_UPGRADE_TO_26 222
+#define STMT_222_INFO {"STMT_UPGRADE_TO_26", NULL}
+#define STMT_222 \
"DROP VIEW IF EXISTS NODES_BASE; " \
"CREATE VIEW NODES_BASE AS " \
" SELECT * FROM nodes " \
" WHERE op_depth = 0; " \
"PRAGMA user_version = 26; " \
""
-#define STMT_UPGRADE_TO_27 222
-#define STMT_222_INFO {"STMT_UPGRADE_TO_27", NULL}
-#define STMT_222 \
+#define STMT_UPGRADE_TO_27 223
+#define STMT_223_INFO {"STMT_UPGRADE_TO_27", NULL}
+#define STMT_223 \
"PRAGMA user_version = 27; " \
""
-#define STMT_UPGRADE_27_HAS_ACTUAL_NODES_CONFLICTS 223
-#define STMT_223_INFO {"STMT_UPGRADE_27_HAS_ACTUAL_NODES_CONFLICTS", NULL}
-#define STMT_223 \
+#define STMT_UPGRADE_27_HAS_ACTUAL_NODES_CONFLICTS 224
+#define STMT_224_INFO {"STMT_UPGRADE_27_HAS_ACTUAL_NODES_CONFLICTS", NULL}
+#define STMT_224 \
"SELECT 1 FROM actual_node " \
"WHERE NOT ((prop_reject IS NULL) AND (conflict_old IS NULL) " \
" AND (conflict_new IS NULL) AND (conflict_working IS NULL) " \
" AND (tree_conflict_data IS NULL)) " \
"LIMIT 1 " \
""
-#define STMT_UPGRADE_TO_28 224
-#define STMT_224_INFO {"STMT_UPGRADE_TO_28", NULL}
-#define STMT_224 \
+#define STMT_UPGRADE_TO_28 225
+#define STMT_225_INFO {"STMT_UPGRADE_TO_28", NULL}
+#define STMT_225 \
"UPDATE NODES SET checksum = (SELECT checksum FROM pristine " \
" WHERE md5_checksum = nodes.checksum) " \
"WHERE EXISTS (SELECT 1 FROM pristine WHERE md5_checksum = nodes.checksum); " \
"PRAGMA user_version = 28; " \
""
-#define STMT_UPGRADE_TO_29 225
-#define STMT_225_INFO {"STMT_UPGRADE_TO_29", NULL}
-#define STMT_225 \
+#define STMT_UPGRADE_TO_29 226
+#define STMT_226_INFO {"STMT_UPGRADE_TO_29", NULL}
+#define STMT_226 \
"DROP TRIGGER IF EXISTS nodes_update_checksum_trigger; " \
"DROP TRIGGER IF EXISTS nodes_insert_trigger; " \
"DROP TRIGGER IF EXISTS nodes_delete_trigger; " \
"CREATE TRIGGER nodes_update_checksum_trigger " \
"AFTER UPDATE OF checksum ON nodes " \
"WHEN NEW.checksum IS NOT OLD.checksum " \
"BEGIN " \
" UPDATE pristine SET refcount = refcount + 1 " \
" WHERE checksum = NEW.checksum; " \
" UPDATE pristine SET refcount = refcount - 1 " \
" WHERE checksum = OLD.checksum; " \
"END; " \
"CREATE TRIGGER nodes_insert_trigger " \
"AFTER INSERT ON nodes " \
"WHEN NEW.checksum IS NOT NULL " \
"BEGIN " \
" UPDATE pristine SET refcount = refcount + 1 " \
" WHERE checksum = NEW.checksum; " \
"END; " \
"CREATE TRIGGER nodes_delete_trigger " \
"AFTER DELETE ON nodes " \
"WHEN OLD.checksum IS NOT NULL " \
"BEGIN " \
" UPDATE pristine SET refcount = refcount - 1 " \
" WHERE checksum = OLD.checksum; " \
"END; " \
"PRAGMA user_version = 29; " \
""
-#define STMT_UPGRADE_TO_30 226
-#define STMT_226_INFO {"STMT_UPGRADE_TO_30", NULL}
-#define STMT_226 \
+#define STMT_UPGRADE_TO_30 227
+#define STMT_227_INFO {"STMT_UPGRADE_TO_30", NULL}
+#define STMT_227 \
"CREATE UNIQUE INDEX IF NOT EXISTS I_NODES_MOVED " \
"ON NODES (wc_id, moved_to, op_depth); " \
"CREATE INDEX IF NOT EXISTS I_PRISTINE_MD5 ON PRISTINE (md5_checksum); " \
"UPDATE nodes SET presence = \"server-excluded\" WHERE presence = \"absent\"; " \
"UPDATE nodes SET file_external=1 WHERE file_external IS NOT NULL; " \
""
-#define STMT_UPGRADE_30_SELECT_CONFLICT_SEPARATE 227
-#define STMT_227_INFO {"STMT_UPGRADE_30_SELECT_CONFLICT_SEPARATE", NULL}
-#define STMT_227 \
+#define STMT_UPGRADE_30_SELECT_CONFLICT_SEPARATE 228
+#define STMT_228_INFO {"STMT_UPGRADE_30_SELECT_CONFLICT_SEPARATE", NULL}
+#define STMT_228 \
"SELECT wc_id, local_relpath, " \
" conflict_old, conflict_working, conflict_new, prop_reject, tree_conflict_data " \
"FROM actual_node " \
"WHERE conflict_old IS NOT NULL " \
" OR conflict_working IS NOT NULL " \
" OR conflict_new IS NOT NULL " \
" OR prop_reject IS NOT NULL " \
" OR tree_conflict_data IS NOT NULL " \
"ORDER by wc_id, local_relpath " \
""
-#define STMT_UPGRADE_30_SET_CONFLICT 228
-#define STMT_228_INFO {"STMT_UPGRADE_30_SET_CONFLICT", NULL}
-#define STMT_228 \
+#define STMT_UPGRADE_30_SET_CONFLICT 229
+#define STMT_229_INFO {"STMT_UPGRADE_30_SET_CONFLICT", NULL}
+#define STMT_229 \
"UPDATE actual_node SET conflict_data = ?3, conflict_old = NULL, " \
" conflict_working = NULL, conflict_new = NULL, prop_reject = NULL, " \
" tree_conflict_data = NULL " \
"WHERE wc_id = ?1 and local_relpath = ?2 " \
""
-#define STMT_UPGRADE_TO_31_ALTER_TABLE 229
-#define STMT_229_INFO {"STMT_UPGRADE_TO_31_ALTER_TABLE", NULL}
-#define STMT_229 \
+#define STMT_UPGRADE_TO_31_ALTER_TABLE 230
+#define STMT_230_INFO {"STMT_UPGRADE_TO_31_ALTER_TABLE", NULL}
+#define STMT_230 \
"ALTER TABLE NODES ADD COLUMN inherited_props BLOB; " \
""
-#define STMT_UPGRADE_TO_31_FINALIZE 230
-#define STMT_230_INFO {"STMT_UPGRADE_TO_31_FINALIZE", NULL}
-#define STMT_230 \
+#define STMT_UPGRADE_TO_31_FINALIZE 231
+#define STMT_231_INFO {"STMT_UPGRADE_TO_31_FINALIZE", NULL}
+#define STMT_231 \
"DROP INDEX IF EXISTS I_ACTUAL_CHANGELIST; " \
"DROP INDEX IF EXISTS I_EXTERNALS_PARENT; " \
"DROP INDEX I_NODES_PARENT; " \
"CREATE UNIQUE INDEX I_NODES_PARENT ON NODES (wc_id, parent_relpath, " \
" local_relpath, op_depth); " \
"DROP INDEX I_ACTUAL_PARENT; " \
"CREATE UNIQUE INDEX I_ACTUAL_PARENT ON ACTUAL_NODE (wc_id, parent_relpath, " \
" local_relpath); " \
"PRAGMA user_version = 31; " \
""
-#define STMT_UPGRADE_31_SELECT_WCROOT_NODES 231
-#define STMT_231_INFO {"STMT_UPGRADE_31_SELECT_WCROOT_NODES", NULL}
-#define STMT_231 \
+#define STMT_UPGRADE_31_SELECT_WCROOT_NODES 232
+#define STMT_232_INFO {"STMT_UPGRADE_31_SELECT_WCROOT_NODES", NULL}
+#define STMT_232 \
"SELECT l.wc_id, l.local_relpath FROM nodes as l " \
"LEFT OUTER JOIN nodes as r " \
"ON l.wc_id = r.wc_id " \
" AND r.local_relpath = l.parent_relpath " \
" AND r.op_depth = 0 " \
"WHERE l.op_depth = 0 " \
" AND l.repos_path != '' " \
" AND ((l.repos_id IS NOT r.repos_id) " \
" OR (l.repos_path IS NOT (CASE WHEN (r.local_relpath) = '' THEN (CASE WHEN (r.repos_path) = '' THEN (l.local_relpath) WHEN (l.local_relpath) = '' THEN (r.repos_path) ELSE (r.repos_path) || '/' || (l.local_relpath) END) WHEN (r.repos_path) = '' THEN (CASE WHEN (r.local_relpath) = '' THEN (l.local_relpath) WHEN SUBSTR((l.local_relpath), 1, LENGTH(r.local_relpath)) = (r.local_relpath) THEN CASE WHEN LENGTH(r.local_relpath) = LENGTH(l.local_relpath) THEN '' WHEN SUBSTR((l.local_relpath), LENGTH(r.local_relpath)+1, 1) = '/' THEN SUBSTR((l.local_relpath), LENGTH(r.local_relpath)+2) END END) WHEN SUBSTR((l.local_relpath), 1, LENGTH(r.local_relpath)) = (r.local_relpath) THEN CASE WHEN LENGTH(r.local_relpath) = LENGTH(l.local_relpath) THEN (r.repos_path) WHEN SUBSTR((l.local_relpath), LENGTH(r.local_relpath)+1, 1) = '/' THEN (r.repos_path) || SUBSTR((l.local_relpath), LENGTH(r.local_relpath)+1) END END))) " \
""
-#define STMT_UPGRADE_TO_32 232
-#define STMT_232_INFO {"STMT_UPGRADE_TO_32", NULL}
-#define STMT_232 \
+#define STMT_UPGRADE_TO_32 233
+#define STMT_233_INFO {"STMT_UPGRADE_TO_32", NULL}
+#define STMT_233 \
"DROP INDEX IF EXISTS I_ACTUAL_CHANGELIST; " \
"DROP INDEX IF EXISTS I_EXTERNALS_PARENT; " \
"CREATE INDEX I_EXTERNALS_PARENT ON EXTERNALS (wc_id, parent_relpath); " \
"DROP INDEX I_NODES_PARENT; " \
"CREATE UNIQUE INDEX I_NODES_PARENT ON NODES (wc_id, parent_relpath, " \
" local_relpath, op_depth); " \
"DROP INDEX I_ACTUAL_PARENT; " \
"CREATE UNIQUE INDEX I_ACTUAL_PARENT ON ACTUAL_NODE (wc_id, parent_relpath, " \
" local_relpath); " \
"-- format: YYY " \
""
#define WC_QUERIES_SQL_99 \
"CREATE TABLE ACTUAL_NODE_BACKUP ( " \
" wc_id INTEGER NOT NULL, " \
" local_relpath TEXT NOT NULL, " \
" parent_relpath TEXT, " \
" properties BLOB, " \
" conflict_old TEXT, " \
" conflict_new TEXT, " \
" conflict_working TEXT, " \
" prop_reject TEXT, " \
" changelist TEXT, " \
" text_mod TEXT " \
" ); " \
"INSERT INTO ACTUAL_NODE_BACKUP SELECT " \
" wc_id, local_relpath, parent_relpath, properties, conflict_old, " \
" conflict_new, conflict_working, prop_reject, changelist, text_mod " \
"FROM ACTUAL_NODE; " \
"DROP TABLE ACTUAL_NODE; " \
"CREATE TABLE ACTUAL_NODE ( " \
" wc_id INTEGER NOT NULL REFERENCES WCROOT (id), " \
" local_relpath TEXT NOT NULL, " \
" parent_relpath TEXT, " \
" properties BLOB, " \
" conflict_old TEXT, " \
" conflict_new TEXT, " \
" conflict_working TEXT, " \
" prop_reject TEXT, " \
" changelist TEXT, " \
" text_mod TEXT, " \
" PRIMARY KEY (wc_id, local_relpath) " \
" ); " \
"CREATE UNIQUE INDEX I_ACTUAL_PARENT ON ACTUAL_NODE (wc_id, parent_relpath, " \
" local_relpath); " \
"INSERT INTO ACTUAL_NODE SELECT " \
" wc_id, local_relpath, parent_relpath, properties, conflict_old, " \
" conflict_new, conflict_working, prop_reject, changelist, text_mod " \
"FROM ACTUAL_NODE_BACKUP; " \
"DROP TABLE ACTUAL_NODE_BACKUP; " \
""
-#define STMT_VERIFICATION_TRIGGERS 233
-#define STMT_233_INFO {"STMT_VERIFICATION_TRIGGERS", NULL}
-#define STMT_233 \
+#define STMT_VERIFICATION_TRIGGERS 234
+#define STMT_234_INFO {"STMT_VERIFICATION_TRIGGERS", NULL}
+#define STMT_234 \
"CREATE TEMPORARY TRIGGER no_repository_updates BEFORE UPDATE ON repository " \
"BEGIN " \
" SELECT RAISE(FAIL, 'Updates to REPOSITORY are not allowed.'); " \
"END; " \
"CREATE TEMPORARY TRIGGER validation_01 BEFORE INSERT ON nodes " \
"WHEN NOT ((new.local_relpath = '' AND new.parent_relpath IS NULL) " \
" OR (relpath_depth(new.local_relpath) " \
" = relpath_depth(new.parent_relpath) + 1)) " \
"BEGIN " \
" SELECT RAISE(FAIL, 'WC DB validity check 01 failed'); " \
"END; " \
"CREATE TEMPORARY TRIGGER validation_02 BEFORE INSERT ON nodes " \
"WHEN NOT new.op_depth <= relpath_depth(new.local_relpath) " \
"BEGIN " \
" SELECT RAISE(FAIL, 'WC DB validity check 02 failed'); " \
"END; " \
"CREATE TEMPORARY TRIGGER validation_03 BEFORE INSERT ON nodes " \
"WHEN NOT ( " \
" (new.op_depth = relpath_depth(new.local_relpath)) " \
" OR " \
" (EXISTS (SELECT 1 FROM nodes " \
" WHERE wc_id = new.wc_id AND op_depth = new.op_depth " \
" AND local_relpath = new.parent_relpath)) " \
" ) " \
" AND NOT (new.file_external IS NOT NULL AND new.op_depth = 0) " \
"BEGIN " \
" SELECT RAISE(FAIL, 'WC DB validity check 03 failed'); " \
"END; " \
"CREATE TEMPORARY TRIGGER validation_04 BEFORE INSERT ON actual_node " \
"WHEN NOT (new.local_relpath = '' " \
" OR EXISTS (SELECT 1 FROM nodes " \
" WHERE wc_id = new.wc_id " \
" AND local_relpath = new.parent_relpath)) " \
"BEGIN " \
" SELECT RAISE(FAIL, 'WC DB validity check 04 failed'); " \
"END; " \
""
#define WC_QUERIES_SQL_DECLARE_STATEMENTS(varname) \
static const char * const varname[] = { \
STMT_0, \
STMT_1, \
STMT_2, \
STMT_3, \
STMT_4, \
STMT_5, \
STMT_6, \
STMT_7, \
STMT_8, \
STMT_9, \
STMT_10, \
STMT_11, \
STMT_12, \
STMT_13, \
STMT_14, \
STMT_15, \
STMT_16, \
STMT_17, \
STMT_18, \
STMT_19, \
STMT_20, \
STMT_21, \
STMT_22, \
STMT_23, \
STMT_24, \
STMT_25, \
STMT_26, \
STMT_27, \
STMT_28, \
STMT_29, \
STMT_30, \
STMT_31, \
STMT_32, \
STMT_33, \
STMT_34, \
STMT_35, \
STMT_36, \
STMT_37, \
STMT_38, \
STMT_39, \
STMT_40, \
STMT_41, \
STMT_42, \
STMT_43, \
STMT_44, \
STMT_45, \
STMT_46, \
STMT_47, \
STMT_48, \
STMT_49, \
STMT_50, \
STMT_51, \
STMT_52, \
STMT_53, \
STMT_54, \
STMT_55, \
STMT_56, \
STMT_57, \
STMT_58, \
STMT_59, \
STMT_60, \
STMT_61, \
STMT_62, \
STMT_63, \
STMT_64, \
STMT_65, \
STMT_66, \
STMT_67, \
STMT_68, \
STMT_69, \
STMT_70, \
STMT_71, \
STMT_72, \
STMT_73, \
STMT_74, \
STMT_75, \
STMT_76, \
STMT_77, \
STMT_78, \
STMT_79, \
STMT_80, \
STMT_81, \
STMT_82, \
STMT_83, \
STMT_84, \
STMT_85, \
STMT_86, \
STMT_87, \
STMT_88, \
STMT_89, \
STMT_90, \
STMT_91, \
STMT_92, \
STMT_93, \
STMT_94, \
STMT_95, \
STMT_96, \
STMT_97, \
STMT_98, \
STMT_99, \
STMT_100, \
STMT_101, \
STMT_102, \
STMT_103, \
STMT_104, \
STMT_105, \
STMT_106, \
STMT_107, \
STMT_108, \
STMT_109, \
STMT_110, \
STMT_111, \
STMT_112, \
STMT_113, \
STMT_114, \
STMT_115, \
STMT_116, \
STMT_117, \
STMT_118, \
STMT_119, \
STMT_120, \
STMT_121, \
STMT_122, \
STMT_123, \
STMT_124, \
STMT_125, \
STMT_126, \
STMT_127, \
STMT_128, \
STMT_129, \
STMT_130, \
STMT_131, \
STMT_132, \
STMT_133, \
STMT_134, \
STMT_135, \
STMT_136, \
STMT_137, \
STMT_138, \
STMT_139, \
STMT_140, \
STMT_141, \
STMT_142, \
STMT_143, \
STMT_144, \
STMT_145, \
STMT_146, \
STMT_147, \
STMT_148, \
STMT_149, \
STMT_150, \
STMT_151, \
STMT_152, \
STMT_153, \
STMT_154, \
STMT_155, \
STMT_156, \
STMT_157, \
STMT_158, \
STMT_159, \
STMT_160, \
STMT_161, \
STMT_162, \
STMT_163, \
STMT_164, \
STMT_165, \
STMT_166, \
STMT_167, \
STMT_168, \
STMT_169, \
STMT_170, \
STMT_171, \
STMT_172, \
STMT_173, \
STMT_174, \
STMT_175, \
STMT_176, \
STMT_177, \
STMT_178, \
STMT_179, \
STMT_180, \
STMT_181, \
STMT_182, \
STMT_183, \
STMT_184, \
STMT_185, \
STMT_186, \
STMT_187, \
STMT_188, \
STMT_189, \
STMT_190, \
STMT_191, \
STMT_192, \
STMT_193, \
STMT_194, \
STMT_195, \
STMT_196, \
STMT_197, \
STMT_198, \
STMT_199, \
STMT_200, \
STMT_201, \
STMT_202, \
STMT_203, \
STMT_204, \
STMT_205, \
STMT_206, \
STMT_207, \
STMT_208, \
STMT_209, \
STMT_210, \
STMT_211, \
STMT_212, \
STMT_213, \
STMT_214, \
STMT_215, \
STMT_216, \
STMT_217, \
STMT_218, \
STMT_219, \
STMT_220, \
STMT_221, \
STMT_222, \
STMT_223, \
STMT_224, \
STMT_225, \
STMT_226, \
STMT_227, \
STMT_228, \
STMT_229, \
STMT_230, \
STMT_231, \
STMT_232, \
STMT_233, \
+ STMT_234, \
NULL \
}
#define WC_QUERIES_SQL_DECLARE_STATEMENT_INFO(varname) \
static const char * const varname[][2] = { \
STMT_0_INFO, \
STMT_1_INFO, \
STMT_2_INFO, \
STMT_3_INFO, \
STMT_4_INFO, \
STMT_5_INFO, \
STMT_6_INFO, \
STMT_7_INFO, \
STMT_8_INFO, \
STMT_9_INFO, \
STMT_10_INFO, \
STMT_11_INFO, \
STMT_12_INFO, \
STMT_13_INFO, \
STMT_14_INFO, \
STMT_15_INFO, \
STMT_16_INFO, \
STMT_17_INFO, \
STMT_18_INFO, \
STMT_19_INFO, \
STMT_20_INFO, \
STMT_21_INFO, \
STMT_22_INFO, \
STMT_23_INFO, \
STMT_24_INFO, \
STMT_25_INFO, \
STMT_26_INFO, \
STMT_27_INFO, \
STMT_28_INFO, \
STMT_29_INFO, \
STMT_30_INFO, \
STMT_31_INFO, \
STMT_32_INFO, \
STMT_33_INFO, \
STMT_34_INFO, \
STMT_35_INFO, \
STMT_36_INFO, \
STMT_37_INFO, \
STMT_38_INFO, \
STMT_39_INFO, \
STMT_40_INFO, \
STMT_41_INFO, \
STMT_42_INFO, \
STMT_43_INFO, \
STMT_44_INFO, \
STMT_45_INFO, \
STMT_46_INFO, \
STMT_47_INFO, \
STMT_48_INFO, \
STMT_49_INFO, \
STMT_50_INFO, \
STMT_51_INFO, \
STMT_52_INFO, \
STMT_53_INFO, \
STMT_54_INFO, \
STMT_55_INFO, \
STMT_56_INFO, \
STMT_57_INFO, \
STMT_58_INFO, \
STMT_59_INFO, \
STMT_60_INFO, \
STMT_61_INFO, \
STMT_62_INFO, \
STMT_63_INFO, \
STMT_64_INFO, \
STMT_65_INFO, \
STMT_66_INFO, \
STMT_67_INFO, \
STMT_68_INFO, \
STMT_69_INFO, \
STMT_70_INFO, \
STMT_71_INFO, \
STMT_72_INFO, \
STMT_73_INFO, \
STMT_74_INFO, \
STMT_75_INFO, \
STMT_76_INFO, \
STMT_77_INFO, \
STMT_78_INFO, \
STMT_79_INFO, \
STMT_80_INFO, \
STMT_81_INFO, \
STMT_82_INFO, \
STMT_83_INFO, \
STMT_84_INFO, \
STMT_85_INFO, \
STMT_86_INFO, \
STMT_87_INFO, \
STMT_88_INFO, \
STMT_89_INFO, \
STMT_90_INFO, \
STMT_91_INFO, \
STMT_92_INFO, \
STMT_93_INFO, \
STMT_94_INFO, \
STMT_95_INFO, \
STMT_96_INFO, \
STMT_97_INFO, \
STMT_98_INFO, \
STMT_99_INFO, \
STMT_100_INFO, \
STMT_101_INFO, \
STMT_102_INFO, \
STMT_103_INFO, \
STMT_104_INFO, \
STMT_105_INFO, \
STMT_106_INFO, \
STMT_107_INFO, \
STMT_108_INFO, \
STMT_109_INFO, \
STMT_110_INFO, \
STMT_111_INFO, \
STMT_112_INFO, \
STMT_113_INFO, \
STMT_114_INFO, \
STMT_115_INFO, \
STMT_116_INFO, \
STMT_117_INFO, \
STMT_118_INFO, \
STMT_119_INFO, \
STMT_120_INFO, \
STMT_121_INFO, \
STMT_122_INFO, \
STMT_123_INFO, \
STMT_124_INFO, \
STMT_125_INFO, \
STMT_126_INFO, \
STMT_127_INFO, \
STMT_128_INFO, \
STMT_129_INFO, \
STMT_130_INFO, \
STMT_131_INFO, \
STMT_132_INFO, \
STMT_133_INFO, \
STMT_134_INFO, \
STMT_135_INFO, \
STMT_136_INFO, \
STMT_137_INFO, \
STMT_138_INFO, \
STMT_139_INFO, \
STMT_140_INFO, \
STMT_141_INFO, \
STMT_142_INFO, \
STMT_143_INFO, \
STMT_144_INFO, \
STMT_145_INFO, \
STMT_146_INFO, \
STMT_147_INFO, \
STMT_148_INFO, \
STMT_149_INFO, \
STMT_150_INFO, \
STMT_151_INFO, \
STMT_152_INFO, \
STMT_153_INFO, \
STMT_154_INFO, \
STMT_155_INFO, \
STMT_156_INFO, \
STMT_157_INFO, \
STMT_158_INFO, \
STMT_159_INFO, \
STMT_160_INFO, \
STMT_161_INFO, \
STMT_162_INFO, \
STMT_163_INFO, \
STMT_164_INFO, \
STMT_165_INFO, \
STMT_166_INFO, \
STMT_167_INFO, \
STMT_168_INFO, \
STMT_169_INFO, \
STMT_170_INFO, \
STMT_171_INFO, \
STMT_172_INFO, \
STMT_173_INFO, \
STMT_174_INFO, \
STMT_175_INFO, \
STMT_176_INFO, \
STMT_177_INFO, \
STMT_178_INFO, \
STMT_179_INFO, \
STMT_180_INFO, \
STMT_181_INFO, \
STMT_182_INFO, \
STMT_183_INFO, \
STMT_184_INFO, \
STMT_185_INFO, \
STMT_186_INFO, \
STMT_187_INFO, \
STMT_188_INFO, \
STMT_189_INFO, \
STMT_190_INFO, \
STMT_191_INFO, \
STMT_192_INFO, \
STMT_193_INFO, \
STMT_194_INFO, \
STMT_195_INFO, \
STMT_196_INFO, \
STMT_197_INFO, \
STMT_198_INFO, \
STMT_199_INFO, \
STMT_200_INFO, \
STMT_201_INFO, \
STMT_202_INFO, \
STMT_203_INFO, \
STMT_204_INFO, \
STMT_205_INFO, \
STMT_206_INFO, \
STMT_207_INFO, \
STMT_208_INFO, \
STMT_209_INFO, \
STMT_210_INFO, \
STMT_211_INFO, \
STMT_212_INFO, \
STMT_213_INFO, \
STMT_214_INFO, \
STMT_215_INFO, \
STMT_216_INFO, \
STMT_217_INFO, \
STMT_218_INFO, \
STMT_219_INFO, \
STMT_220_INFO, \
STMT_221_INFO, \
STMT_222_INFO, \
STMT_223_INFO, \
STMT_224_INFO, \
STMT_225_INFO, \
STMT_226_INFO, \
STMT_227_INFO, \
STMT_228_INFO, \
STMT_229_INFO, \
STMT_230_INFO, \
STMT_231_INFO, \
STMT_232_INFO, \
STMT_233_INFO, \
+ STMT_234_INFO, \
{NULL, NULL} \
}
Index: vendor/subversion/dist/subversion/libsvn_wc/wc-queries.sql
===================================================================
--- vendor/subversion/dist/subversion/libsvn_wc/wc-queries.sql (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_wc/wc-queries.sql (revision 286501)
@@ -1,1724 +1,1728 @@
/* wc-queries.sql -- queries used to interact with the wc-metadata
* SQLite database
* This is intended for use with SQLite 3
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
/* ------------------------------------------------------------------------- */
/* these are used in wc_db.c */
-- STMT_SELECT_NODE_INFO
SELECT op_depth, repos_id, repos_path, presence, kind, revision, checksum,
translated_size, changed_revision, changed_date, changed_author, depth,
symlink_target, last_mod_time, properties, moved_here, inherited_props,
moved_to
FROM nodes
WHERE wc_id = ?1 AND local_relpath = ?2
ORDER BY op_depth DESC
-- STMT_SELECT_NODE_INFO_WITH_LOCK
SELECT op_depth, nodes.repos_id, nodes.repos_path, presence, kind, revision,
checksum, translated_size, changed_revision, changed_date, changed_author,
depth, symlink_target, last_mod_time, properties, moved_here,
inherited_props,
/* All the columns until now must match those returned by
STMT_SELECT_NODE_INFO. The implementation of svn_wc__db_read_info()
assumes that these columns are followed by the lock information) */
lock_token, lock_owner, lock_comment, lock_date
FROM nodes
LEFT OUTER JOIN lock ON nodes.repos_id = lock.repos_id
AND nodes.repos_path = lock.repos_relpath
WHERE wc_id = ?1 AND local_relpath = ?2
ORDER BY op_depth DESC
-- STMT_SELECT_BASE_NODE
SELECT repos_id, repos_path, presence, kind, revision, checksum,
translated_size, changed_revision, changed_date, changed_author, depth,
symlink_target, last_mod_time, properties, file_external
FROM nodes
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0
-- STMT_SELECT_BASE_NODE_WITH_LOCK
SELECT nodes.repos_id, nodes.repos_path, presence, kind, revision,
checksum, translated_size, changed_revision, changed_date, changed_author,
depth, symlink_target, last_mod_time, properties, file_external,
/* All the columns until now must match those returned by
STMT_SELECT_BASE_NODE. The implementation of svn_wc__db_base_get_info()
assumes that these columns are followed by the lock information) */
lock_token, lock_owner, lock_comment, lock_date
FROM nodes
LEFT OUTER JOIN lock ON nodes.repos_id = lock.repos_id
AND nodes.repos_path = lock.repos_relpath
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0
-- STMT_SELECT_BASE_CHILDREN_INFO
SELECT local_relpath, nodes.repos_id, nodes.repos_path, presence, kind,
revision, depth, file_external,
lock_token, lock_owner, lock_comment, lock_date
FROM nodes
LEFT OUTER JOIN lock ON nodes.repos_id = lock.repos_id
AND nodes.repos_path = lock.repos_relpath
WHERE wc_id = ?1 AND parent_relpath = ?2 AND op_depth = 0
-- STMT_SELECT_WORKING_NODE
SELECT op_depth, presence, kind, checksum, translated_size,
changed_revision, changed_date, changed_author, depth, symlink_target,
repos_id, repos_path, revision,
moved_here, moved_to, last_mod_time, properties
FROM nodes
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > 0
ORDER BY op_depth DESC
LIMIT 1
-- STMT_SELECT_DEPTH_NODE
SELECT repos_id, repos_path, presence, kind, revision, checksum,
translated_size, changed_revision, changed_date, changed_author, depth,
symlink_target, last_mod_time, properties
FROM nodes
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3
-- STMT_SELECT_LOWEST_WORKING_NODE
SELECT op_depth, presence, kind, moved_to
FROM nodes
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > ?3
ORDER BY op_depth
LIMIT 1
-- STMT_SELECT_HIGHEST_WORKING_NODE
SELECT op_depth
FROM nodes
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth < ?3
ORDER BY op_depth DESC
LIMIT 1
-- STMT_SELECT_ACTUAL_NODE
SELECT changelist, properties, conflict_data
FROM actual_node
WHERE wc_id = ?1 AND local_relpath = ?2
-- STMT_SELECT_NODE_CHILDREN_INFO
/* Getting rows in an advantageous order using
ORDER BY local_relpath, op_depth DESC
turns out to be slower than getting rows in a random order and making the
C code handle it. */
SELECT op_depth, nodes.repos_id, nodes.repos_path, presence, kind, revision,
checksum, translated_size, changed_revision, changed_date, changed_author,
depth, symlink_target, last_mod_time, properties, lock_token, lock_owner,
lock_comment, lock_date, local_relpath, moved_here, moved_to, file_external
FROM nodes
LEFT OUTER JOIN lock ON nodes.repos_id = lock.repos_id
AND nodes.repos_path = lock.repos_relpath AND op_depth = 0
WHERE wc_id = ?1 AND parent_relpath = ?2
-- STMT_SELECT_NODE_CHILDREN_WALKER_INFO
SELECT local_relpath, op_depth, presence, kind
FROM nodes_current
WHERE wc_id = ?1 AND parent_relpath = ?2
-- STMT_SELECT_ACTUAL_CHILDREN_INFO
SELECT local_relpath, changelist, properties, conflict_data
FROM actual_node
WHERE wc_id = ?1 AND parent_relpath = ?2
-- STMT_SELECT_REPOSITORY_BY_ID
SELECT root, uuid FROM repository WHERE id = ?1
-- STMT_SELECT_WCROOT_NULL
SELECT id FROM wcroot WHERE local_abspath IS NULL
-- STMT_SELECT_REPOSITORY
SELECT id FROM repository WHERE root = ?1
-- STMT_INSERT_REPOSITORY
INSERT INTO repository (root, uuid) VALUES (?1, ?2)
-- STMT_INSERT_NODE
INSERT OR REPLACE INTO nodes (
wc_id, local_relpath, op_depth, parent_relpath, repos_id, repos_path,
revision, presence, depth, kind, changed_revision, changed_date,
changed_author, checksum, properties, translated_size, last_mod_time,
dav_cache, symlink_target, file_external, moved_to, moved_here,
inherited_props)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14,
?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23)
-- STMT_SELECT_BASE_PRESENT
SELECT local_relpath, kind FROM nodes n
WHERE wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
AND op_depth = 0
AND presence in (MAP_NORMAL, MAP_INCOMPLETE)
AND NOT EXISTS(SELECT 1 FROM NODES w
WHERE w.wc_id = ?1 AND w.local_relpath = n.local_relpath
AND op_depth > 0)
ORDER BY local_relpath DESC
-- STMT_SELECT_WORKING_PRESENT
SELECT local_relpath, kind, checksum, translated_size, last_mod_time
FROM nodes n
WHERE wc_id = ?1
AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
AND presence in (MAP_NORMAL, MAP_INCOMPLETE)
AND op_depth = (SELECT MAX(op_depth)
FROM NODES w
WHERE w.wc_id = ?1
AND w.local_relpath = n.local_relpath)
ORDER BY local_relpath DESC
-- STMT_DELETE_NODE_RECURSIVE
DELETE FROM NODES
WHERE wc_id = ?1
AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
-- STMT_DELETE_NODE
DELETE
FROM NODES
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3
-- STMT_DELETE_ACTUAL_FOR_BASE_RECURSIVE
/* The ACTUAL_NODE applies to BASE, unless there is in at least one op_depth
a WORKING node that could have a conflict */
DELETE FROM actual_node
WHERE wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
AND EXISTS(SELECT 1 FROM NODES b
WHERE b.wc_id = ?1
AND b.local_relpath = actual_node.local_relpath
AND op_depth = 0)
AND NOT EXISTS(SELECT 1 FROM NODES w
WHERE w.wc_id = ?1
AND w.local_relpath = actual_node.local_relpath
AND op_depth > 0
AND presence in (MAP_NORMAL, MAP_INCOMPLETE, MAP_NOT_PRESENT))
-- STMT_DELETE_WORKING_BASE_DELETE
DELETE FROM nodes
WHERE wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
AND presence = MAP_BASE_DELETED
AND op_depth > 0
AND op_depth = (SELECT MIN(op_depth) FROM nodes n
WHERE n.wc_id = ?1
AND n.local_relpath = nodes.local_relpath
AND op_depth > 0)
-- STMT_DELETE_WORKING_RECURSIVE
DELETE FROM nodes
WHERE wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
AND op_depth > 0
-- STMT_DELETE_BASE_RECURSIVE
DELETE FROM nodes
WHERE wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
AND op_depth = 0
-- STMT_DELETE_WORKING_OP_DEPTH
DELETE FROM nodes
WHERE wc_id = ?1
AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
AND op_depth = ?3
-- STMT_DELETE_WORKING_OP_DEPTH_ABOVE
DELETE FROM nodes
WHERE wc_id = ?1
AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
AND op_depth > ?3
-- STMT_SELECT_LOCAL_RELPATH_OP_DEPTH
SELECT local_relpath
FROM nodes
WHERE wc_id = ?1
AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
AND op_depth = ?3
-- STMT_SELECT_CHILDREN_OP_DEPTH
SELECT local_relpath, kind
FROM nodes
WHERE wc_id = ?1
AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
AND op_depth = ?3
ORDER BY local_relpath DESC
-- STMT_COPY_NODE_MOVE
INSERT OR REPLACE INTO nodes (
wc_id, local_relpath, op_depth, parent_relpath, repos_id, repos_path,
revision, presence, depth, kind, changed_revision, changed_date,
changed_author, checksum, properties, translated_size, last_mod_time,
symlink_target, moved_here, moved_to )
SELECT
wc_id, ?4 /*local_relpath */, ?5 /*op_depth*/, ?6 /* parent_relpath */,
repos_id,
repos_path, revision, presence, depth, kind, changed_revision,
changed_date, changed_author, checksum, properties, translated_size,
last_mod_time, symlink_target, 1,
(SELECT dst.moved_to FROM nodes AS dst
WHERE dst.wc_id = ?1
AND dst.local_relpath = ?4
AND dst.op_depth = ?5)
FROM nodes
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3
-- STMT_SELECT_OP_DEPTH_CHILDREN
SELECT local_relpath, kind FROM nodes
WHERE wc_id = ?1
AND parent_relpath = ?2
AND op_depth = ?3
AND presence != MAP_BASE_DELETED
AND file_external is NULL
/* Used by non-recursive revert to detect higher level children, and
actual-only rows that would be left orphans, if the revert
proceeded. */
-- STMT_SELECT_GE_OP_DEPTH_CHILDREN
SELECT 1 FROM nodes
WHERE wc_id = ?1 AND parent_relpath = ?2
AND (op_depth > ?3 OR (op_depth = ?3 AND presence != MAP_BASE_DELETED))
UNION ALL
SELECT 1 FROM ACTUAL_NODE a
WHERE wc_id = ?1 AND parent_relpath = ?2
AND NOT EXISTS (SELECT 1 FROM nodes n
WHERE wc_id = ?1 AND n.local_relpath = a.local_relpath)
/* Delete the nodes shadowed by local_relpath. Not valid for the wc-root */
-- STMT_DELETE_SHADOWED_RECURSIVE
DELETE FROM nodes
WHERE wc_id = ?1
AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
AND (op_depth < ?3
OR (op_depth = ?3 AND presence = MAP_BASE_DELETED))
-- STMT_CLEAR_MOVED_TO_FROM_DEST
UPDATE NODES SET moved_to = NULL
WHERE wc_id = ?1
AND moved_to = ?2
/* Get not-present descendants of a copied node. Not valid for the wc-root */
-- STMT_SELECT_NOT_PRESENT_DESCENDANTS
SELECT local_relpath FROM nodes
WHERE wc_id = ?1 AND op_depth = ?3
AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
AND presence = MAP_NOT_PRESENT
-- STMT_COMMIT_DESCENDANTS_TO_BASE
UPDATE NODES SET op_depth = 0,
repos_id = ?4,
repos_path = ?5 || SUBSTR(local_relpath, LENGTH(?2)+1),
revision = ?6,
dav_cache = NULL,
moved_here = NULL,
presence = CASE presence
WHEN MAP_NORMAL THEN MAP_NORMAL
WHEN MAP_EXCLUDED THEN MAP_EXCLUDED
ELSE MAP_NOT_PRESENT
END
WHERE wc_id = ?1
AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
AND op_depth = ?3
-- STMT_SELECT_NODE_CHILDREN
/* Return all paths that are children of the directory (?1, ?2) in any
op-depth, including children of any underlying, replaced directories. */
SELECT local_relpath FROM nodes
WHERE wc_id = ?1 AND parent_relpath = ?2
-- STMT_SELECT_WORKING_CHILDREN
/* Return all paths that are children of the working version of the
directory (?1, ?2). A given path is not included just because it is a
child of an underlying (replaced) directory, it has to be in the
working version of the directory. */
SELECT local_relpath FROM nodes
WHERE wc_id = ?1 AND parent_relpath = ?2
AND (op_depth > (SELECT MAX(op_depth) FROM nodes
WHERE wc_id = ?1 AND local_relpath = ?2)
OR
(op_depth = (SELECT MAX(op_depth) FROM nodes
WHERE wc_id = ?1 AND local_relpath = ?2)
AND presence != MAP_BASE_DELETED))
-- STMT_SELECT_NODE_PROPS
SELECT properties, presence FROM nodes
WHERE wc_id = ?1 AND local_relpath = ?2
ORDER BY op_depth DESC
-- STMT_SELECT_ACTUAL_PROPS
SELECT properties FROM actual_node
WHERE wc_id = ?1 AND local_relpath = ?2
-- STMT_UPDATE_ACTUAL_PROPS
UPDATE actual_node SET properties = ?3
WHERE wc_id = ?1 AND local_relpath = ?2
-- STMT_INSERT_ACTUAL_PROPS
INSERT INTO actual_node (wc_id, local_relpath, parent_relpath, properties)
VALUES (?1, ?2, ?3, ?4)
-- STMT_INSERT_LOCK
INSERT OR REPLACE INTO lock
(repos_id, repos_relpath, lock_token, lock_owner, lock_comment,
lock_date)
VALUES (?1, ?2, ?3, ?4, ?5, ?6)
/* Not valid for the working copy root */
-- STMT_SELECT_BASE_NODE_LOCK_TOKENS_RECURSIVE
SELECT nodes.repos_id, nodes.repos_path, lock_token
FROM nodes
LEFT JOIN lock ON nodes.repos_id = lock.repos_id
AND nodes.repos_path = lock.repos_relpath
WHERE wc_id = ?1 AND op_depth = 0
AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
-- STMT_INSERT_WCROOT
INSERT INTO wcroot (local_abspath)
VALUES (?1)
-- STMT_UPDATE_BASE_NODE_DAV_CACHE
UPDATE nodes SET dav_cache = ?3
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0
-- STMT_SELECT_BASE_DAV_CACHE
SELECT dav_cache FROM nodes
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0
-- STMT_SELECT_DELETION_INFO
SELECT (SELECT b.presence FROM nodes AS b
WHERE b.wc_id = ?1 AND b.local_relpath = ?2 AND b.op_depth = 0),
work.presence, work.op_depth
FROM nodes_current AS work
WHERE work.wc_id = ?1 AND work.local_relpath = ?2 AND work.op_depth > 0
LIMIT 1
-- STMT_SELECT_DELETION_INFO_SCAN
/* ### FIXME. moved.moved_to IS NOT NULL works when there is
only one move but we need something else when there are several. */
SELECT (SELECT b.presence FROM nodes AS b
WHERE b.wc_id = ?1 AND b.local_relpath = ?2 AND b.op_depth = 0),
work.presence, work.op_depth, moved.moved_to
FROM nodes_current AS work
LEFT OUTER JOIN nodes AS moved
ON moved.wc_id = work.wc_id
AND moved.local_relpath = work.local_relpath
AND moved.moved_to IS NOT NULL
WHERE work.wc_id = ?1 AND work.local_relpath = ?2 AND work.op_depth > 0
LIMIT 1
-- STMT_SELECT_MOVED_TO_NODE
SELECT op_depth, moved_to
FROM nodes
WHERE wc_id = ?1 AND local_relpath = ?2 AND moved_to IS NOT NULL
ORDER BY op_depth DESC
-- STMT_SELECT_OP_DEPTH_MOVED_TO
SELECT op_depth, moved_to, repos_path, revision
FROM nodes
WHERE wc_id = ?1 AND local_relpath = ?2
AND op_depth <= (SELECT MIN(op_depth) FROM nodes
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > ?3)
ORDER BY op_depth DESC
-- STMT_SELECT_MOVED_TO
SELECT moved_to
FROM nodes
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3
-- STMT_SELECT_MOVED_HERE
SELECT moved_here, presence, repos_path, revision
FROM nodes
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth >= ?3
ORDER BY op_depth
-- STMT_SELECT_MOVED_BACK
SELECT u.local_relpath,
u.presence, u.repos_id, u.repos_path, u.revision,
l.presence, l.repos_id, l.repos_path, l.revision,
u.moved_here, u.moved_to
FROM nodes u
LEFT OUTER JOIN nodes l ON l.wc_id = ?1
AND l.local_relpath = u.local_relpath
AND l.op_depth = ?3
WHERE u.wc_id = ?1
AND u.local_relpath = ?2
AND u.op_depth = ?4
UNION ALL
SELECT u.local_relpath,
u.presence, u.repos_id, u.repos_path, u.revision,
l.presence, l.repos_id, l.repos_path, l.revision,
u.moved_here, NULL
FROM nodes u
LEFT OUTER JOIN nodes l ON l.wc_id=?1
AND l.local_relpath=u.local_relpath
AND l.op_depth=?3
WHERE u.wc_id = ?1
AND IS_STRICT_DESCENDANT_OF(u.local_relpath, ?2)
AND u.op_depth = ?4
-- STMT_DELETE_MOVED_BACK
DELETE FROM nodes
WHERE wc_id = ?1
AND (local_relpath = ?2
OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
AND op_depth = ?3
-- STMT_DELETE_LOCK
DELETE FROM lock
WHERE repos_id = ?1 AND repos_relpath = ?2
-- STMT_DELETE_LOCK_RECURSIVELY
DELETE FROM lock
WHERE repos_id = ?1 AND (repos_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(repos_relpath, ?2))
-- STMT_CLEAR_BASE_NODE_RECURSIVE_DAV_CACHE
UPDATE nodes SET dav_cache = NULL
WHERE dav_cache IS NOT NULL AND wc_id = ?1 AND op_depth = 0
AND (local_relpath = ?2
OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
-- STMT_RECURSIVE_UPDATE_NODE_REPO
UPDATE nodes SET repos_id = ?4, dav_cache = NULL
/* ### The Sqlite optimizer needs help here ###
* WHERE wc_id = ?1
* AND repos_id = ?3
* AND (local_relpath = ?2
* OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))*/
WHERE (wc_id = ?1 AND local_relpath = ?2 AND repos_id = ?3)
OR (wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
AND repos_id = ?3)
-- STMT_UPDATE_LOCK_REPOS_ID
UPDATE lock SET repos_id = ?2
WHERE repos_id = ?1
-- STMT_UPDATE_NODE_FILEINFO
UPDATE nodes SET translated_size = ?3, last_mod_time = ?4
WHERE wc_id = ?1 AND local_relpath = ?2
AND op_depth = (SELECT MAX(op_depth) FROM nodes
WHERE wc_id = ?1 AND local_relpath = ?2)
-- STMT_INSERT_ACTUAL_CONFLICT
INSERT INTO actual_node (wc_id, local_relpath, conflict_data, parent_relpath)
VALUES (?1, ?2, ?3, ?4)
-- STMT_UPDATE_ACTUAL_CONFLICT
UPDATE actual_node SET conflict_data = ?3
WHERE wc_id = ?1 AND local_relpath = ?2
-- STMT_UPDATE_ACTUAL_CHANGELISTS
UPDATE actual_node SET changelist = ?3
WHERE wc_id = ?1
AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
AND local_relpath = (SELECT local_relpath FROM targets_list AS t
WHERE wc_id = ?1
AND t.local_relpath = actual_node.local_relpath
AND kind = MAP_FILE)
-- STMT_UPDATE_ACTUAL_CLEAR_CHANGELIST
UPDATE actual_node SET changelist = NULL
WHERE wc_id = ?1 AND local_relpath = ?2
-- STMT_MARK_SKIPPED_CHANGELIST_DIRS
/* 7 corresponds to svn_wc_notify_skip */
INSERT INTO changelist_list (wc_id, local_relpath, notify, changelist)
SELECT wc_id, local_relpath, 7, ?3
FROM targets_list
WHERE wc_id = ?1
AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
AND kind = MAP_DIR
-- STMT_RESET_ACTUAL_WITH_CHANGELIST
REPLACE INTO actual_node (
wc_id, local_relpath, parent_relpath, changelist)
VALUES (?1, ?2, ?3, ?4)
-- STMT_CREATE_CHANGELIST_LIST
DROP TABLE IF EXISTS changelist_list;
CREATE TEMPORARY TABLE changelist_list (
wc_id INTEGER NOT NULL,
local_relpath TEXT NOT NULL,
notify INTEGER NOT NULL,
changelist TEXT NOT NULL,
/* Order NOTIFY descending to make us show clears (27) before adds (26) */
PRIMARY KEY (wc_id, local_relpath, notify DESC)
)
/* Create notify items for when a node is removed from a changelist and
when a node is added to a changelist. Make sure nothing is notified
if there were no changes.
*/
-- STMT_CREATE_CHANGELIST_TRIGGER
DROP TRIGGER IF EXISTS trigger_changelist_list_change;
CREATE TEMPORARY TRIGGER trigger_changelist_list_change
BEFORE UPDATE ON actual_node
WHEN old.changelist IS NOT new.changelist
BEGIN
/* 27 corresponds to svn_wc_notify_changelist_clear */
INSERT INTO changelist_list(wc_id, local_relpath, notify, changelist)
SELECT old.wc_id, old.local_relpath, 27, old.changelist
WHERE old.changelist is NOT NULL;
/* 26 corresponds to svn_wc_notify_changelist_set */
INSERT INTO changelist_list(wc_id, local_relpath, notify, changelist)
SELECT new.wc_id, new.local_relpath, 26, new.changelist
WHERE new.changelist IS NOT NULL;
END
-- STMT_FINALIZE_CHANGELIST
DROP TRIGGER trigger_changelist_list_change;
DROP TABLE changelist_list;
DROP TABLE targets_list
-- STMT_SELECT_CHANGELIST_LIST
SELECT wc_id, local_relpath, notify, changelist
FROM changelist_list
ORDER BY wc_id, local_relpath ASC, notify DESC
-- STMT_CREATE_TARGETS_LIST
DROP TABLE IF EXISTS targets_list;
CREATE TEMPORARY TABLE targets_list (
wc_id INTEGER NOT NULL,
local_relpath TEXT NOT NULL,
parent_relpath TEXT,
kind TEXT NOT NULL,
PRIMARY KEY (wc_id, local_relpath)
);
/* need more indicies? */
-- STMT_DROP_TARGETS_LIST
DROP TABLE targets_list
-- STMT_INSERT_TARGET
INSERT INTO targets_list(wc_id, local_relpath, parent_relpath, kind)
SELECT wc_id, local_relpath, parent_relpath, kind
FROM nodes_current
WHERE wc_id = ?1
AND local_relpath = ?2
-- STMT_INSERT_TARGET_DEPTH_FILES
INSERT INTO targets_list(wc_id, local_relpath, parent_relpath, kind)
SELECT wc_id, local_relpath, parent_relpath, kind
FROM nodes_current
WHERE wc_id = ?1
AND parent_relpath = ?2
AND kind = MAP_FILE
-- STMT_INSERT_TARGET_DEPTH_IMMEDIATES
INSERT INTO targets_list(wc_id, local_relpath, parent_relpath, kind)
SELECT wc_id, local_relpath, parent_relpath, kind
FROM nodes_current
WHERE wc_id = ?1
AND parent_relpath = ?2
-- STMT_INSERT_TARGET_DEPTH_INFINITY
INSERT INTO targets_list(wc_id, local_relpath, parent_relpath, kind)
SELECT wc_id, local_relpath, parent_relpath, kind
FROM nodes_current
WHERE wc_id = ?1
AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
-- STMT_INSERT_TARGET_WITH_CHANGELIST
INSERT INTO targets_list(wc_id, local_relpath, parent_relpath, kind)
SELECT N.wc_id, N.local_relpath, N.parent_relpath, N.kind
FROM actual_node AS A JOIN nodes_current AS N
ON A.wc_id = N.wc_id AND A.local_relpath = N.local_relpath
WHERE N.wc_id = ?1
AND N.local_relpath = ?2
AND A.changelist = ?3
-- STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_FILES
INSERT INTO targets_list(wc_id, local_relpath, parent_relpath, kind)
SELECT N.wc_id, N.local_relpath, N.parent_relpath, N.kind
FROM actual_node AS A JOIN nodes_current AS N
ON A.wc_id = N.wc_id AND A.local_relpath = N.local_relpath
WHERE N.wc_id = ?1
AND N.parent_relpath = ?2
AND kind = MAP_FILE
AND A.changelist = ?3
-- STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_IMMEDIATES
INSERT INTO targets_list(wc_id, local_relpath, parent_relpath, kind)
SELECT N.wc_id, N.local_relpath, N.parent_relpath, N.kind
FROM actual_node AS A JOIN nodes_current AS N
ON A.wc_id = N.wc_id AND A.local_relpath = N.local_relpath
WHERE N.wc_id = ?1
AND N.parent_relpath = ?2
AND A.changelist = ?3
-- STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_INFINITY
INSERT INTO targets_list(wc_id, local_relpath, parent_relpath, kind)
SELECT N.wc_id, N.local_relpath, N.parent_relpath, N.kind
FROM actual_node AS A JOIN nodes_current AS N
ON A.wc_id = N.wc_id AND A.local_relpath = N.local_relpath
WHERE N.wc_id = ?1
AND IS_STRICT_DESCENDANT_OF(N.local_relpath, ?2)
AND A.changelist = ?3
/* Only used by commented dump_targets() in wc_db.c */
/*-- STMT_SELECT_TARGETS
SELECT local_relpath, parent_relpath from targets_list*/
-- STMT_INSERT_ACTUAL_EMPTIES
INSERT OR IGNORE INTO actual_node (
wc_id, local_relpath, parent_relpath)
SELECT wc_id, local_relpath, parent_relpath
FROM targets_list
-- STMT_DELETE_ACTUAL_EMPTY
DELETE FROM actual_node
WHERE wc_id = ?1 AND local_relpath = ?2
AND properties IS NULL
AND conflict_data IS NULL
AND changelist IS NULL
AND text_mod IS NULL
AND older_checksum IS NULL
AND right_checksum IS NULL
AND left_checksum IS NULL
-- STMT_DELETE_ACTUAL_EMPTIES
DELETE FROM actual_node
WHERE wc_id = ?1
AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
AND properties IS NULL
AND conflict_data IS NULL
AND changelist IS NULL
AND text_mod IS NULL
AND older_checksum IS NULL
AND right_checksum IS NULL
AND left_checksum IS NULL
-- STMT_DELETE_BASE_NODE
DELETE FROM nodes
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0
-- STMT_DELETE_WORKING_NODE
DELETE FROM nodes
WHERE wc_id = ?1 AND local_relpath = ?2
AND op_depth = (SELECT MAX(op_depth) FROM nodes
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > 0)
-- STMT_DELETE_LOWEST_WORKING_NODE
DELETE FROM nodes
WHERE wc_id = ?1 AND local_relpath = ?2
AND op_depth = (SELECT MIN(op_depth) FROM nodes
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > ?3)
AND presence = MAP_BASE_DELETED
-- STMT_DELETE_NODE_ALL_LAYERS
DELETE FROM nodes
WHERE wc_id = ?1 AND local_relpath = ?2
-- STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE
DELETE FROM nodes
WHERE wc_id = ?1
AND (local_relpath = ?2
OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
AND op_depth >= ?3
-- STMT_DELETE_ACTUAL_NODE
DELETE FROM actual_node
WHERE wc_id = ?1 AND local_relpath = ?2
/* Will not delete recursive when run on the wcroot */
-- STMT_DELETE_ACTUAL_NODE_RECURSIVE
DELETE FROM actual_node
WHERE wc_id = ?1
AND (local_relpath = ?2
OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
-- STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST
DELETE FROM actual_node
WHERE wc_id = ?1
AND local_relpath = ?2
AND (changelist IS NULL
OR NOT EXISTS (SELECT 1 FROM nodes_current c
WHERE c.wc_id = ?1 AND c.local_relpath = ?2
AND c.kind = MAP_FILE))
-- STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE
DELETE FROM actual_node
WHERE wc_id = ?1
AND (local_relpath = ?2
OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
AND (changelist IS NULL
OR NOT EXISTS (SELECT 1 FROM nodes_current c
WHERE c.wc_id = ?1
AND c.local_relpath = actual_node.local_relpath
AND c.kind = MAP_FILE))
-- STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST
UPDATE actual_node
SET properties = NULL,
text_mod = NULL,
conflict_data = NULL,
tree_conflict_data = NULL,
older_checksum = NULL,
left_checksum = NULL,
right_checksum = NULL
WHERE wc_id = ?1 AND local_relpath = ?2
-- STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE
UPDATE actual_node
SET properties = NULL,
text_mod = NULL,
conflict_data = NULL,
tree_conflict_data = NULL,
older_checksum = NULL,
left_checksum = NULL,
right_checksum = NULL
WHERE wc_id = ?1
AND (local_relpath = ?2
OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
-- STMT_UPDATE_NODE_BASE_DEPTH
UPDATE nodes SET depth = ?3
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0
AND kind=MAP_DIR
-- STMT_UPDATE_NODE_BASE_PRESENCE
UPDATE nodes SET presence = ?3
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0
-- STMT_UPDATE_BASE_NODE_PRESENCE_REVNUM_AND_REPOS_PATH
UPDATE nodes SET presence = ?3, revision = ?4, repos_path = ?5
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0
-- STMT_LOOK_FOR_WORK
SELECT id FROM work_queue LIMIT 1
-- STMT_INSERT_WORK_ITEM
INSERT INTO work_queue (work) VALUES (?1)
-- STMT_SELECT_WORK_ITEM
SELECT id, work FROM work_queue ORDER BY id LIMIT 1
-- STMT_DELETE_WORK_ITEM
DELETE FROM work_queue WHERE id = ?1
-- STMT_INSERT_OR_IGNORE_PRISTINE
INSERT OR IGNORE INTO pristine (checksum, md5_checksum, size, refcount)
VALUES (?1, ?2, ?3, 0)
-- STMT_INSERT_PRISTINE
INSERT INTO pristine (checksum, md5_checksum, size, refcount)
VALUES (?1, ?2, ?3, 0)
-- STMT_SELECT_PRISTINE
SELECT md5_checksum
FROM pristine
WHERE checksum = ?1
-- STMT_SELECT_PRISTINE_SIZE
SELECT size
FROM pristine
WHERE checksum = ?1 LIMIT 1
-- STMT_SELECT_PRISTINE_BY_MD5
SELECT checksum
FROM pristine
WHERE md5_checksum = ?1
-- STMT_SELECT_UNREFERENCED_PRISTINES
SELECT checksum
FROM pristine
WHERE refcount = 0
-- STMT_DELETE_PRISTINE_IF_UNREFERENCED
DELETE FROM pristine
WHERE checksum = ?1 AND refcount = 0
-- STMT_SELECT_COPY_PRISTINES
/* For the root itself */
SELECT n.checksum, md5_checksum, size
FROM nodes_current n
LEFT JOIN pristine p ON n.checksum = p.checksum
WHERE wc_id = ?1
AND n.local_relpath = ?2
AND n.checksum IS NOT NULL
UNION ALL
/* And all descendants */
SELECT n.checksum, md5_checksum, size
FROM nodes n
LEFT JOIN pristine p ON n.checksum = p.checksum
WHERE wc_id = ?1
AND IS_STRICT_DESCENDANT_OF(n.local_relpath, ?2)
AND op_depth >=
(SELECT MAX(op_depth) FROM nodes WHERE wc_id = ?1 AND local_relpath = ?2)
AND n.checksum IS NOT NULL
-- STMT_VACUUM
VACUUM
-- STMT_SELECT_CONFLICT_VICTIMS
SELECT local_relpath, conflict_data
FROM actual_node
WHERE wc_id = ?1 AND parent_relpath = ?2 AND
NOT (conflict_data IS NULL)
-- STMT_INSERT_WC_LOCK
INSERT INTO wc_lock (wc_id, local_dir_relpath, locked_levels)
VALUES (?1, ?2, ?3)
-- STMT_SELECT_WC_LOCK
SELECT locked_levels FROM wc_lock
WHERE wc_id = ?1 AND local_dir_relpath = ?2
-- STMT_SELECT_ANCESTOR_WCLOCKS
SELECT local_dir_relpath, locked_levels FROM wc_lock
WHERE wc_id = ?1
AND ((local_dir_relpath >= ?3 AND local_dir_relpath <= ?2)
OR local_dir_relpath = '')
-- STMT_DELETE_WC_LOCK
DELETE FROM wc_lock
WHERE wc_id = ?1 AND local_dir_relpath = ?2
-- STMT_FIND_WC_LOCK
SELECT local_dir_relpath FROM wc_lock
WHERE wc_id = ?1
AND IS_STRICT_DESCENDANT_OF(local_dir_relpath, ?2)
-- STMT_DELETE_WC_LOCK_ORPHAN
DELETE FROM wc_lock
WHERE wc_id = ?1 AND local_dir_relpath = ?2
AND NOT EXISTS (SELECT 1 FROM nodes
WHERE nodes.wc_id = ?1
AND nodes.local_relpath = wc_lock.local_dir_relpath)
-- STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE
DELETE FROM wc_lock
WHERE wc_id = ?1
AND (local_dir_relpath = ?2
OR IS_STRICT_DESCENDANT_OF(local_dir_relpath, ?2))
AND NOT EXISTS (SELECT 1 FROM nodes
WHERE nodes.wc_id = ?1
AND nodes.local_relpath = wc_lock.local_dir_relpath)
-- STMT_APPLY_CHANGES_TO_BASE_NODE
/* translated_size and last_mod_time are not mentioned here because they will
be tweaked after the working-file is installed. When we replace an existing
BASE node (read: bump), preserve its file_external status. */
INSERT OR REPLACE INTO nodes (
wc_id, local_relpath, op_depth, parent_relpath, repos_id, repos_path,
revision, presence, depth, kind, changed_revision, changed_date,
changed_author, checksum, properties, dav_cache, symlink_target,
inherited_props, file_external )
VALUES (?1, ?2, 0,
?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17,
(SELECT file_external FROM nodes
WHERE wc_id = ?1
AND local_relpath = ?2
AND op_depth = 0))
-- STMT_INSTALL_WORKING_NODE_FOR_DELETE
INSERT OR REPLACE INTO nodes (
wc_id, local_relpath, op_depth,
parent_relpath, presence, kind)
VALUES(?1, ?2, ?3, ?4, MAP_BASE_DELETED, ?5)
-- STMT_DELETE_NO_LOWER_LAYER
DELETE FROM nodes
WHERE wc_id = ?1
AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
AND op_depth = ?3
AND NOT EXISTS (SELECT 1 FROM nodes n
WHERE n.wc_id = ?1
AND n.local_relpath = nodes.local_relpath
AND n.op_depth = ?4
AND n.presence IN (MAP_NORMAL, MAP_INCOMPLETE))
-- STMT_REPLACE_WITH_BASE_DELETED
INSERT OR REPLACE INTO nodes (wc_id, local_relpath, op_depth, parent_relpath,
kind, moved_to, presence)
SELECT wc_id, local_relpath, op_depth, parent_relpath,
kind, moved_to, MAP_BASE_DELETED
FROM nodes
WHERE wc_id = ?1
AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
AND op_depth = ?3
/* If this query is updated, STMT_INSERT_DELETE_LIST should too. */
-- STMT_INSERT_DELETE_FROM_NODE_RECURSIVE
INSERT INTO nodes (
wc_id, local_relpath, op_depth, parent_relpath, presence, kind)
SELECT wc_id, local_relpath, ?4 /*op_depth*/, parent_relpath, MAP_BASE_DELETED,
kind
FROM nodes
WHERE wc_id = ?1
AND (local_relpath = ?2
OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
AND op_depth = ?3
AND presence NOT IN (MAP_BASE_DELETED, MAP_NOT_PRESENT, MAP_EXCLUDED, MAP_SERVER_EXCLUDED)
AND file_external IS NULL
-- STMT_INSERT_WORKING_NODE_FROM_BASE_COPY
INSERT INTO nodes (
wc_id, local_relpath, op_depth, parent_relpath, repos_id, repos_path,
revision, presence, depth, kind, changed_revision, changed_date,
changed_author, checksum, properties, translated_size, last_mod_time,
symlink_target )
SELECT wc_id, local_relpath, ?3 /*op_depth*/, parent_relpath, repos_id,
repos_path, revision, presence, depth, kind, changed_revision,
changed_date, changed_author, checksum, properties, translated_size,
last_mod_time, symlink_target
FROM nodes
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0
-- STMT_INSERT_DELETE_FROM_BASE
INSERT INTO nodes (
wc_id, local_relpath, op_depth, parent_relpath, presence, kind)
SELECT wc_id, local_relpath, ?3 /*op_depth*/, parent_relpath,
MAP_BASE_DELETED, kind
FROM nodes
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0
/* Not valid on the wc-root */
-- STMT_UPDATE_OP_DEPTH_INCREASE_RECURSIVE
UPDATE nodes SET op_depth = ?3 + 1
WHERE wc_id = ?1
AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
AND op_depth = ?3
-- STMT_UPDATE_OP_DEPTH_RECURSIVE
UPDATE nodes SET op_depth = ?4, moved_here = NULL
WHERE wc_id = ?1
AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
AND op_depth = ?3
-- STMT_DOES_NODE_EXIST
SELECT 1 FROM nodes WHERE wc_id = ?1 AND local_relpath = ?2
LIMIT 1
-- STMT_HAS_SERVER_EXCLUDED_DESCENDANTS
SELECT local_relpath FROM nodes
WHERE wc_id = ?1
AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
AND op_depth = 0 AND presence = MAP_SERVER_EXCLUDED
LIMIT 1
/* Select all excluded nodes. Not valid on the WC-root */
-- STMT_SELECT_ALL_EXCLUDED_DESCENDANTS
SELECT local_relpath FROM nodes
WHERE wc_id = ?1
AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
AND op_depth = 0
AND (presence = MAP_SERVER_EXCLUDED OR presence = MAP_EXCLUDED)
/* Creates a copy from one top level NODE to a different location */
-- STMT_INSERT_WORKING_NODE_COPY_FROM
INSERT OR REPLACE INTO nodes (
wc_id, local_relpath, op_depth, parent_relpath, repos_id,
repos_path, revision, presence, depth, moved_here, kind, changed_revision,
changed_date, changed_author, checksum, properties, translated_size,
last_mod_time, symlink_target, moved_to )
SELECT wc_id, ?3 /*local_relpath*/, ?4 /*op_depth*/, ?5 /*parent_relpath*/,
repos_id, repos_path, revision, ?6 /*presence*/, depth,
?7/*moved_here*/, kind, changed_revision, changed_date,
changed_author, checksum, properties, translated_size,
last_mod_time, symlink_target,
(SELECT dst.moved_to FROM nodes AS dst
WHERE dst.wc_id = ?1
AND dst.local_relpath = ?3
AND dst.op_depth = ?4)
FROM nodes_current
WHERE wc_id = ?1 AND local_relpath = ?2
-- STMT_INSERT_WORKING_NODE_COPY_FROM_DEPTH
INSERT OR REPLACE INTO nodes (
wc_id, local_relpath, op_depth, parent_relpath, repos_id,
repos_path, revision, presence, depth, moved_here, kind, changed_revision,
changed_date, changed_author, checksum, properties, translated_size,
last_mod_time, symlink_target, moved_to )
SELECT wc_id, ?3 /*local_relpath*/, ?4 /*op_depth*/, ?5 /*parent_relpath*/,
repos_id, repos_path, revision, ?6 /*presence*/, depth,
?8 /*moved_here*/, kind, changed_revision, changed_date,
changed_author, checksum, properties, translated_size,
last_mod_time, symlink_target,
(SELECT dst.moved_to FROM nodes AS dst
WHERE dst.wc_id = ?1
AND dst.local_relpath = ?3
AND dst.op_depth = ?4)
FROM nodes
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?7
-- STMT_UPDATE_BASE_REVISION
UPDATE nodes SET revision = ?3
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0
-- STMT_UPDATE_BASE_REPOS
UPDATE nodes SET repos_id = ?3, repos_path = ?4
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0
-- STMT_ACTUAL_HAS_CHILDREN
SELECT 1 FROM actual_node
WHERE wc_id = ?1 AND parent_relpath = ?2
LIMIT 1
-- STMT_INSERT_EXTERNAL
INSERT OR REPLACE INTO externals (
wc_id, local_relpath, parent_relpath, presence, kind, def_local_relpath,
repos_id, def_repos_relpath, def_operational_revision, def_revision)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)
-- STMT_SELECT_EXTERNAL_INFO
SELECT presence, kind, def_local_relpath, repos_id,
def_repos_relpath, def_operational_revision, def_revision
FROM externals WHERE wc_id = ?1 AND local_relpath = ?2
LIMIT 1
-- STMT_DELETE_FILE_EXTERNALS
DELETE FROM nodes
WHERE wc_id = ?1
AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
AND op_depth = 0
AND file_external IS NOT NULL
-- STMT_DELETE_FILE_EXTERNAL_REGISTATIONS
DELETE FROM externals
WHERE wc_id = ?1
AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
AND kind != MAP_DIR
-- STMT_DELETE_EXTERNAL_REGISTATIONS
DELETE FROM externals
WHERE wc_id = ?1
AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
/* Select all committable externals, i.e. only unpegged ones on the same
* repository as the target path ?2, that are defined by WC ?1 to
* live below the target path. It does not matter which ancestor has the
* svn:externals definition, only the local path at which the external is
* supposed to be checked out is queried.
* Arguments:
* ?1: wc_id.
* ?2: the target path, local relpath inside ?1.
*
* ### NOTE: This statement deliberately removes file externals that live
* inside an unversioned dir, because commit still breaks on those.
* Once that's been fixed, the conditions below "--->8---" become obsolete. */
-- STMT_SELECT_COMMITTABLE_EXTERNALS_BELOW
SELECT local_relpath, kind, def_repos_relpath,
(SELECT root FROM repository AS r WHERE r.id = e.repos_id)
FROM externals e
WHERE wc_id = ?1
AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
AND def_revision IS NULL
AND repos_id = (SELECT repos_id
FROM nodes AS n
WHERE n.wc_id = ?1
AND n.local_relpath = ''
AND n.op_depth = 0)
AND ((kind='dir')
OR EXISTS (SELECT 1 FROM nodes
WHERE nodes.wc_id = e.wc_id
AND nodes.local_relpath = e.parent_relpath))
-- STMT_SELECT_COMMITTABLE_EXTERNALS_IMMEDIATELY_BELOW
SELECT local_relpath, kind, def_repos_relpath,
(SELECT root FROM repository AS r WHERE r.id = e.repos_id)
FROM externals e
WHERE wc_id = ?1
AND IS_STRICT_DESCENDANT_OF(e.local_relpath, ?2)
AND parent_relpath = ?2
AND def_revision IS NULL
AND repos_id = (SELECT repos_id
FROM nodes AS n
WHERE n.wc_id = ?1
AND n.local_relpath = ''
AND n.op_depth = 0)
AND ((kind='dir')
OR EXISTS (SELECT 1 FROM nodes
WHERE nodes.wc_id = e.wc_id
AND nodes.local_relpath = e.parent_relpath))
-- STMT_SELECT_EXTERNALS_DEFINED
SELECT local_relpath, def_local_relpath
FROM externals
/* ### The Sqlite optimizer needs help here ###
* WHERE wc_id = ?1
* AND (def_local_relpath = ?2
* OR IS_STRICT_DESCENDANT_OF(def_local_relpath, ?2)) */
WHERE (wc_id = ?1 AND def_local_relpath = ?2)
OR (wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(def_local_relpath, ?2))
-- STMT_DELETE_EXTERNAL
DELETE FROM externals
WHERE wc_id = ?1 AND local_relpath = ?2
-- STMT_SELECT_EXTERNAL_PROPERTIES
/* ### It would be nice if Sqlite would handle
* SELECT IFNULL((SELECT properties FROM actual_node a
* WHERE a.wc_id = ?1 AND A.local_relpath = n.local_relpath),
* properties),
* local_relpath, depth
* FROM nodes_current n
* WHERE wc_id = ?1
* AND (local_relpath = ?2
* OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
* AND kind = MAP_DIR AND presence IN (MAP_NORMAL, MAP_INCOMPLETE)
* ### But it would take a double table scan execution plan for it.
* ### Maybe there is something else going on? */
SELECT IFNULL((SELECT properties FROM actual_node a
WHERE a.wc_id = ?1 AND A.local_relpath = n.local_relpath),
properties),
local_relpath, depth
FROM nodes_current n
WHERE wc_id = ?1 AND local_relpath = ?2
AND kind = MAP_DIR AND presence IN (MAP_NORMAL, MAP_INCOMPLETE)
UNION ALL
SELECT IFNULL((SELECT properties FROM actual_node a
WHERE a.wc_id = ?1 AND A.local_relpath = n.local_relpath),
properties),
local_relpath, depth
FROM nodes_current n
WHERE wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
AND kind = MAP_DIR AND presence IN (MAP_NORMAL, MAP_INCOMPLETE)
-- STMT_SELECT_CURRENT_PROPS_RECURSIVE
/* ### Ugly OR to make sqlite use the proper optimizations */
SELECT IFNULL((SELECT properties FROM actual_node a
WHERE a.wc_id = ?1 AND A.local_relpath = n.local_relpath),
properties),
local_relpath
FROM nodes_current n
WHERE (wc_id = ?1 AND local_relpath = ?2)
OR (wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
-- STMT_PRAGMA_LOCKING_MODE
PRAGMA locking_mode = exclusive
/* ------------------------------------------------------------------------- */
/* these are used in entries.c */
-- STMT_INSERT_ACTUAL_NODE
INSERT OR REPLACE INTO actual_node (
wc_id, local_relpath, parent_relpath, properties, changelist, conflict_data)
VALUES (?1, ?2, ?3, ?4, ?5, ?6)
/* ------------------------------------------------------------------------- */
/* these are used in upgrade.c */
-- STMT_UPDATE_ACTUAL_CONFLICT_DATA
UPDATE actual_node SET conflict_data = ?3
WHERE wc_id = ?1 AND local_relpath = ?2
-- STMT_INSERT_ACTUAL_CONFLICT_DATA
INSERT INTO actual_node (wc_id, local_relpath, conflict_data, parent_relpath)
VALUES (?1, ?2, ?3, ?4)
-- STMT_SELECT_ALL_FILES
SELECT local_relpath FROM nodes_current
WHERE wc_id = ?1 AND parent_relpath = ?2 AND kind = MAP_FILE
-- STMT_UPDATE_NODE_PROPS
UPDATE nodes SET properties = ?4
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3
-- STMT_PRAGMA_TABLE_INFO_NODES
PRAGMA table_info("NODES")
/* --------------------------------------------------------------------------
* Complex queries for callback walks, caching results in a temporary table.
*
* These target table are then used for joins against NODES, or for reporting
*/
-- STMT_CREATE_TARGET_PROP_CACHE
DROP TABLE IF EXISTS target_prop_cache;
CREATE TEMPORARY TABLE target_prop_cache (
local_relpath TEXT NOT NULL PRIMARY KEY,
kind TEXT NOT NULL,
properties BLOB
);
/* ### Need index?
CREATE UNIQUE INDEX temp__node_props_cache_unique
ON temp__node_props_cache (local_relpath) */
-- STMT_CACHE_TARGET_PROPS
INSERT INTO target_prop_cache(local_relpath, kind, properties)
SELECT n.local_relpath, n.kind,
IFNULL((SELECT properties FROM actual_node AS a
WHERE a.wc_id = n.wc_id
AND a.local_relpath = n.local_relpath),
n.properties)
FROM targets_list AS t
JOIN nodes AS n
ON n.wc_id = ?1
AND n.local_relpath = t.local_relpath
AND n.op_depth = (SELECT MAX(op_depth) FROM nodes AS n3
WHERE n3.wc_id = ?1
AND n3.local_relpath = t.local_relpath)
WHERE t.wc_id = ?1
AND (presence=MAP_NORMAL OR presence=MAP_INCOMPLETE)
ORDER BY t.local_relpath
-- STMT_CACHE_TARGET_PRISTINE_PROPS
INSERT INTO target_prop_cache(local_relpath, kind, properties)
SELECT n.local_relpath, n.kind,
CASE n.presence
WHEN MAP_BASE_DELETED
THEN (SELECT properties FROM nodes AS p
WHERE p.wc_id = n.wc_id
AND p.local_relpath = n.local_relpath
AND p.op_depth < n.op_depth
ORDER BY p.op_depth DESC /* LIMIT 1 */)
ELSE properties END
FROM targets_list AS t
JOIN nodes AS n
ON n.wc_id = ?1
AND n.local_relpath = t.local_relpath
AND n.op_depth = (SELECT MAX(op_depth) FROM nodes AS n3
WHERE n3.wc_id = ?1
AND n3.local_relpath = t.local_relpath)
WHERE t.wc_id = ?1
AND (presence = MAP_NORMAL
OR presence = MAP_INCOMPLETE
OR presence = MAP_BASE_DELETED)
ORDER BY t.local_relpath
-- STMT_SELECT_ALL_TARGET_PROP_CACHE
SELECT local_relpath, properties FROM target_prop_cache
ORDER BY local_relpath
-- STMT_DROP_TARGET_PROP_CACHE
DROP TABLE target_prop_cache;
-- STMT_CREATE_REVERT_LIST
DROP TABLE IF EXISTS revert_list;
CREATE TEMPORARY TABLE revert_list (
/* need wc_id if/when revert spans multiple working copies */
local_relpath TEXT NOT NULL,
actual INTEGER NOT NULL, /* 1 if an actual row, 0 if a nodes row */
conflict_data BLOB,
notify INTEGER, /* 1 if an actual row had props or tree conflict */
op_depth INTEGER,
repos_id INTEGER,
kind TEXT,
PRIMARY KEY (local_relpath, actual)
);
DROP TRIGGER IF EXISTS trigger_revert_list_nodes;
CREATE TEMPORARY TRIGGER trigger_revert_list_nodes
BEFORE DELETE ON nodes
BEGIN
INSERT OR REPLACE INTO revert_list(local_relpath, actual, op_depth,
repos_id, kind)
SELECT OLD.local_relpath, 0, OLD.op_depth, OLD.repos_id, OLD.kind;
END;
DROP TRIGGER IF EXISTS trigger_revert_list_actual_delete;
CREATE TEMPORARY TRIGGER trigger_revert_list_actual_delete
BEFORE DELETE ON actual_node
BEGIN
INSERT OR REPLACE INTO revert_list(local_relpath, actual, conflict_data,
notify)
SELECT OLD.local_relpath, 1, OLD.conflict_data,
CASE
WHEN OLD.properties IS NOT NULL
THEN 1
WHEN NOT EXISTS(SELECT 1 FROM NODES n
WHERE n.wc_id = OLD.wc_id
AND n.local_relpath = OLD.local_relpath)
THEN 1
ELSE NULL
END;
END;
DROP TRIGGER IF EXISTS trigger_revert_list_actual_update;
CREATE TEMPORARY TRIGGER trigger_revert_list_actual_update
BEFORE UPDATE ON actual_node
BEGIN
INSERT OR REPLACE INTO revert_list(local_relpath, actual, conflict_data,
notify)
SELECT OLD.local_relpath, 1, OLD.conflict_data,
CASE
WHEN OLD.properties IS NOT NULL
THEN 1
WHEN NOT EXISTS(SELECT 1 FROM NODES n
WHERE n.wc_id = OLD.wc_id
AND n.local_relpath = OLD.local_relpath)
THEN 1
ELSE NULL
END;
END
-- STMT_DROP_REVERT_LIST_TRIGGERS
DROP TRIGGER trigger_revert_list_nodes;
DROP TRIGGER trigger_revert_list_actual_delete;
DROP TRIGGER trigger_revert_list_actual_update
-- STMT_SELECT_REVERT_LIST
SELECT actual, notify, kind, op_depth, repos_id, conflict_data
FROM revert_list
WHERE local_relpath = ?1
ORDER BY actual DESC
-- STMT_SELECT_REVERT_LIST_COPIED_CHILDREN
SELECT local_relpath, kind
FROM revert_list
WHERE IS_STRICT_DESCENDANT_OF(local_relpath, ?1)
AND op_depth >= ?2
AND repos_id IS NOT NULL
ORDER BY local_relpath
-- STMT_DELETE_REVERT_LIST
DELETE FROM revert_list WHERE local_relpath = ?1
-- STMT_SELECT_REVERT_LIST_RECURSIVE
SELECT DISTINCT local_relpath
FROM revert_list
WHERE (local_relpath = ?1
OR IS_STRICT_DESCENDANT_OF(local_relpath, ?1))
AND (notify OR actual = 0)
ORDER BY local_relpath
-- STMT_DELETE_REVERT_LIST_RECURSIVE
DELETE FROM revert_list
WHERE (local_relpath = ?1
OR IS_STRICT_DESCENDANT_OF(local_relpath, ?1))
-- STMT_DROP_REVERT_LIST
DROP TABLE IF EXISTS revert_list
-- STMT_CREATE_DELETE_LIST
DROP TABLE IF EXISTS delete_list;
CREATE TEMPORARY TABLE delete_list (
/* ### we should put the wc_id in here in case a delete spans multiple
### working copies. queries, etc will need to be adjusted. */
local_relpath TEXT PRIMARY KEY NOT NULL UNIQUE
)
/* This matches the selection in STMT_INSERT_DELETE_FROM_NODE_RECURSIVE.
A subquery is used instead of nodes_current to avoid a table scan */
-- STMT_INSERT_DELETE_LIST
INSERT INTO delete_list(local_relpath)
SELECT local_relpath FROM nodes AS n
WHERE wc_id = ?1
AND (local_relpath = ?2
OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
AND op_depth >= ?3
AND op_depth = (SELECT MAX(s.op_depth) FROM nodes AS s
WHERE s.wc_id = ?1
AND s.local_relpath = n.local_relpath)
AND presence NOT IN (MAP_BASE_DELETED, MAP_NOT_PRESENT, MAP_EXCLUDED, MAP_SERVER_EXCLUDED)
AND file_external IS NULL
-- STMT_SELECT_DELETE_LIST
SELECT local_relpath FROM delete_list
ORDER BY local_relpath
-- STMT_FINALIZE_DELETE
DROP TABLE IF EXISTS delete_list
-- STMT_CREATE_UPDATE_MOVE_LIST
DROP TABLE IF EXISTS update_move_list;
CREATE TEMPORARY TABLE update_move_list (
/* ### we should put the wc_id in here in case a move update spans multiple
### working copies. queries, etc will need to be adjusted. */
local_relpath TEXT PRIMARY KEY NOT NULL UNIQUE,
action INTEGER NOT NULL,
kind INTEGER NOT NULL,
content_state INTEGER NOT NULL,
prop_state INTEGER NOT NULL
)
-- STMT_INSERT_UPDATE_MOVE_LIST
INSERT INTO update_move_list(local_relpath, action, kind, content_state,
prop_state)
VALUES (?1, ?2, ?3, ?4, ?5)
-- STMT_SELECT_UPDATE_MOVE_LIST
SELECT local_relpath, action, kind, content_state, prop_state
FROM update_move_list
ORDER BY local_relpath
-- STMT_FINALIZE_UPDATE_MOVE
DROP TABLE IF EXISTS update_move_list
/* ------------------------------------------------------------------------- */
/* Queries for revision status. */
-- STMT_SELECT_MIN_MAX_REVISIONS
SELECT MIN(revision), MAX(revision),
MIN(changed_revision), MAX(changed_revision) FROM nodes
WHERE wc_id = ?1
AND (local_relpath = ?2
OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
AND presence IN (MAP_NORMAL, MAP_INCOMPLETE)
AND file_external IS NULL
AND op_depth = 0
-- STMT_HAS_SPARSE_NODES
SELECT 1 FROM nodes
WHERE wc_id = ?1
AND (local_relpath = ?2
OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
AND op_depth = 0
AND (presence IN (MAP_SERVER_EXCLUDED, MAP_EXCLUDED)
OR depth NOT IN (MAP_DEPTH_INFINITY, MAP_DEPTH_UNKNOWN))
AND file_external IS NULL
LIMIT 1
-- STMT_SUBTREE_HAS_TREE_MODIFICATIONS
SELECT 1 FROM nodes
WHERE wc_id = ?1
AND (local_relpath = ?2
OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
AND op_depth > 0
LIMIT 1
-- STMT_SUBTREE_HAS_PROP_MODIFICATIONS
SELECT 1 FROM actual_node
WHERE wc_id = ?1
AND (local_relpath = ?2
OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
AND properties IS NOT NULL
LIMIT 1
-- STMT_HAS_SWITCHED
SELECT 1
FROM nodes
WHERE wc_id = ?1
AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
AND op_depth = 0
AND file_external IS NULL
AND presence IN (MAP_NORMAL, MAP_INCOMPLETE)
AND repos_path IS NOT RELPATH_SKIP_JOIN(?2, ?3, local_relpath)
LIMIT 1
-- STMT_SELECT_BASE_FILES_RECURSIVE
SELECT local_relpath, translated_size, last_mod_time FROM nodes AS n
WHERE wc_id = ?1
AND (local_relpath = ?2
OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
AND op_depth = 0
AND kind=MAP_FILE
AND presence=MAP_NORMAL
AND file_external IS NULL
-- STMT_SELECT_MOVED_FROM_RELPATH
SELECT local_relpath, op_depth FROM nodes
WHERE wc_id = ?1 AND moved_to = ?2 AND op_depth > 0
-- STMT_UPDATE_MOVED_TO_RELPATH
UPDATE nodes SET moved_to = ?4
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3
-- STMT_CLEAR_MOVED_TO_RELPATH
UPDATE nodes SET moved_to = NULL
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3
-- STMT_CLEAR_MOVED_HERE_RECURSIVE
UPDATE nodes SET moved_here = NULL
WHERE wc_id = ?1
AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
AND op_depth = ?3
/* This statement returns pairs of move-roots below the path ?2 in WC_ID ?1.
* Each row returns a moved-here path (always a child of ?2) in the first
* column, and its matching moved-away (deleted) path in the second column. */
-- STMT_SELECT_MOVED_HERE_CHILDREN
SELECT moved_to, local_relpath FROM nodes
WHERE wc_id = ?1 AND op_depth > 0
AND IS_STRICT_DESCENDANT_OF(moved_to, ?2)
/* If the node is moved here (r.moved_here = 1) we are really interested in
where the node was moved from. To obtain that we need the op_depth, but
this form of select only allows a single return value */
-- STMT_SELECT_MOVED_FOR_DELETE
SELECT local_relpath, moved_to, op_depth,
(SELECT CASE WHEN r.moved_here THEN r.op_depth END FROM nodes r
WHERE r.wc_id = ?1
AND r.local_relpath = n.local_relpath
AND r.op_depth < n.op_depth
ORDER BY r.op_depth DESC LIMIT 1) AS moved_here_op_depth
FROM nodes n
WHERE wc_id = ?1
AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
AND moved_to IS NOT NULL
AND op_depth >= ?3
-- STMT_SELECT_MOVED_FROM_FOR_DELETE
SELECT local_relpath, op_depth,
(SELECT CASE WHEN r.moved_here THEN r.op_depth END FROM nodes r
WHERE r.wc_id = ?1
AND r.local_relpath = n.local_relpath
AND r.op_depth < n.op_depth
ORDER BY r.op_depth DESC LIMIT 1) AS moved_here_op_depth
FROM nodes n
WHERE wc_id = ?1 AND moved_to = ?2 AND op_depth > 0
-- STMT_UPDATE_MOVED_TO_DESCENDANTS
UPDATE nodes SET moved_to = RELPATH_SKIP_JOIN(?2, ?3, moved_to)
WHERE wc_id = ?1
AND IS_STRICT_DESCENDANT_OF(moved_to, ?2)
-- STMT_CLEAR_MOVED_TO_DESCENDANTS
UPDATE nodes SET moved_to = NULL
WHERE wc_id = ?1
AND IS_STRICT_DESCENDANT_OF(moved_to, ?2)
/* This statement returns pairs of move-roots below the path ?2 in WC_ID ?1,
* where the source of the move is within the subtree rooted at path ?2, and
* the destination of the move is outside the subtree rooted at path ?2. */
-- STMT_SELECT_MOVED_PAIR2
SELECT local_relpath, moved_to, op_depth FROM nodes
WHERE wc_id = ?1
AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
AND moved_to IS NOT NULL
AND NOT IS_STRICT_DESCENDANT_OF(moved_to, ?2)
AND op_depth >= (SELECT MAX(op_depth) FROM nodes o
WHERE o.wc_id = ?1
AND o.local_relpath = ?2)
-- STMT_SELECT_MOVED_PAIR3
SELECT local_relpath, moved_to, op_depth, kind FROM nodes
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > ?3
AND moved_to IS NOT NULL
UNION ALL
SELECT local_relpath, moved_to, op_depth, kind FROM nodes
WHERE wc_id = ?1
AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
AND op_depth > ?3
AND moved_to IS NOT NULL
ORDER BY local_relpath, op_depth
-- STMT_SELECT_MOVED_OUTSIDE
SELECT local_relpath, moved_to, op_depth FROM nodes
WHERE wc_id = ?1
AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
AND op_depth >= ?3
AND moved_to IS NOT NULL
AND NOT IS_STRICT_DESCENDANT_OF(moved_to, ?2)
-- STMT_SELECT_OP_DEPTH_MOVED_PAIR
SELECT n.local_relpath, n.moved_to,
(SELECT o.repos_path FROM nodes AS o
WHERE o.wc_id = n.wc_id
AND o.local_relpath = n.local_relpath
AND o.op_depth < ?3 ORDER BY o.op_depth DESC LIMIT 1)
FROM nodes AS n
WHERE n.wc_id = ?1
AND IS_STRICT_DESCENDANT_OF(n.local_relpath, ?2)
AND n.op_depth = ?3
AND n.moved_to IS NOT NULL
-- STMT_SELECT_MOVED_DESCENDANTS
SELECT n.local_relpath, h.moved_to
FROM nodes n, nodes h
WHERE n.wc_id = ?1
AND h.wc_id = ?1
AND IS_STRICT_DESCENDANT_OF(n.local_relpath, ?2)
AND h.local_relpath = n.local_relpath
AND n.op_depth = ?3
AND h.op_depth = (SELECT MIN(o.op_depth)
FROM nodes o
WHERE o.wc_id = ?1
AND o.local_relpath = n.local_relpath
AND o.op_depth > ?3)
AND h.moved_to IS NOT NULL
-- STMT_COMMIT_UPDATE_ORIGIN
/* Note that the only reason this SUBSTR() trick is valid is that you
can move neither the working copy nor the repository root.
SUBSTR(local_relpath, LENGTH(?2)+1) includes the '/' of the path */
UPDATE nodes SET repos_id = ?4,
repos_path = ?5 || SUBSTR(local_relpath, LENGTH(?2)+1),
revision = ?6
WHERE wc_id = ?1
AND (local_relpath = ?2
OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
AND op_depth = ?3
-- STMT_HAS_LAYER_BETWEEN
SELECT 1 FROM NODES
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > ?3 AND op_depth < ?4
-- STMT_SELECT_REPOS_PATH_REVISION
SELECT local_relpath, repos_path, revision FROM nodes
WHERE wc_id = ?1
AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
AND op_depth = 0
ORDER BY local_relpath
-- STMT_SELECT_HAS_NON_FILE_CHILDREN
SELECT 1 FROM nodes
WHERE wc_id = ?1 AND parent_relpath = ?2 AND op_depth = 0 AND kind != MAP_FILE
-- STMT_SELECT_HAS_GRANDCHILDREN
SELECT 1 FROM nodes
WHERE wc_id = ?1
AND IS_STRICT_DESCENDANT_OF(parent_relpath, ?2)
AND op_depth = 0
AND file_external IS NULL
/* ------------------------------------------------------------------------- */
/* Queries for verification. */
-- STMT_SELECT_ALL_NODES
SELECT op_depth, local_relpath, parent_relpath, file_external FROM nodes
WHERE wc_id = ?1
/* ------------------------------------------------------------------------- */
/* Queries for cached inherited properties. */
/* Select the inherited properties of a single base node. */
-- STMT_SELECT_IPROPS
SELECT inherited_props FROM nodes
WHERE wc_id = ?1
AND local_relpath = ?2
AND op_depth = 0
/* Update the inherited properties of a single base node. */
-- STMT_UPDATE_IPROP
UPDATE nodes
SET inherited_props = ?3
WHERE (wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0)
/* Select a single path if its base node has cached inherited properties. */
-- STMT_SELECT_IPROPS_NODE
SELECT local_relpath, repos_path FROM nodes
WHERE wc_id = ?1
AND local_relpath = ?2
AND op_depth = 0
AND (inherited_props not null)
/* Select all paths whose base nodes are below a given path, which
have cached inherited properties. */
-- STMT_SELECT_IPROPS_RECURSIVE
SELECT local_relpath, repos_path FROM nodes
WHERE wc_id = ?1
AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
AND op_depth = 0
AND (inherited_props not null)
-- STMT_SELECT_IPROPS_CHILDREN
SELECT local_relpath, repos_path FROM nodes
WHERE wc_id = ?1
AND parent_relpath = ?2
AND op_depth = 0
AND (inherited_props not null)
+-- STMT_HAVE_STAT1_TABLE
+SELECT 1 FROM sqlite_master WHERE name='sqlite_stat1' AND type='table'
+LIMIT 1
+
/* ------------------------------------------------------------------------- */
/* Grab all the statements related to the schema. */
-- include: wc-metadata
-- include: wc-checks
Index: vendor/subversion/dist/subversion/libsvn_wc/wc.h
===================================================================
--- vendor/subversion/dist/subversion/libsvn_wc/wc.h (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_wc/wc.h (revision 286501)
@@ -1,810 +1,814 @@
/*
* wc.h : shared stuff internal to the svn_wc library.
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#ifndef SVN_LIBSVN_WC_H
#define SVN_LIBSVN_WC_H
#include <apr_pools.h>
#include <apr_hash.h>
#include "svn_types.h"
#include "svn_error.h"
#include "svn_wc.h"
#include "private/svn_sqlite.h"
#include "private/svn_wc_private.h"
#include "private/svn_skel.h"
#include "wc_db.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define SVN_WC__PROP_REJ_EXT ".prej"
/* We can handle this format or anything lower, and we (should) error
* on anything higher.
*
* There is no format version 0; we started with 1.
*
* The bump to 2 introduced the ".svn-work" extension. For example,
* ".svn/props/foo" became ".svn/props/foo.svn-work".
*
* The bump to 3 introduced the entry attribute
* old-and-busted.c::ENTRIES_ATTR_ABSENT.
*
* The bump to 4 renamed the magic "svn:this_dir" entry name to "".
*
* == 1.0.x shipped with format 4
* == 1.1.x shipped with format 4
* == 1.2.x shipped with format 4
* == 1.3.x shipped with format 4
*
* The bump to 5 added support for replacing files with history (the
* "revert base"). This was introduced in 1.4.0, but buggy until 1.4.6.
*
* The bump to 6 introduced caching of property modification state and
* certain properties in the entries file.
*
* The bump to 7 changed the entries file format from XML to a custom
* text-based format.
*
* The bump to 8 placed wcprops in one file per directory (named
* upgrade.c::WCPROPS_ALL_DATA)
*
* == 1.4.x shipped with format 8
*
* The bump to 9 added changelists, keep-local, and sticky depth (for
* selective/sparse checkouts) to each entry.
*
* == 1.5.x shipped with format 9
*
* The bump to 10 added tree-conflicts, file externals and a different
* canonicalization of urls.
*
* == 1.6.x shipped with format 10
*
* The bump to 11 cleared the has_props, has_prop_mods, cachable_props,
* and present_props values in the entries file. Older clients expect
* proper values for these fields.
*
* The bump to 12 switched from 'entries' to the SQLite database 'wc.db'.
*
* The bump to 13 added the WORK_QUEUE table into 'wc.db', moved the
* wcprops into the 'dav_cache' column in BASE_NODE, and stopped using
* the 'incomplete_children' column of BASE_NODE.
*
* The bump to 14 added the WCLOCKS table (and migrated locks from the
* filesystem into wc.db), and some columns to ACTUAL_NODE for future
* use.
*
* The bump to 15 switched from depth='exclude' on directories to using
* presence='exclude' within the BASE_NODE and WORKING_NODE tables.
* This change also enabled exclude support on files and symlinks.
*
* The bump to 16 added 'locked_levels' to WC_LOCK, setting any existing
* locks to a level of 0. The 'md5_checksum' column was added to PRISTINE
* for future use.
*
* The bump to 17 added a '.svn/pristine' dir and moved the text bases into
* the Pristine Store (the PRISTINE table and '.svn/pristine' dir), and
* removed the '/.svn/text-base' dir.
*
* The bump to 18 moved the properties from separate files in the props and
* prop-base directory (and .svn for the dir itself) into the wc.db file,
* and then removed the props and prop-base dir.
*
* The bump to 19 introduced the 'single DB' per working copy. All metadata
* is held in a single '.svn/wc.db' in the root directory of the working
* copy. Bumped in r991236.
*
* The bump to 20 introduced NODES and drops BASE_NODE and WORKING_NODE,
* op_depth is always 0 or 2. Bumped in r1005388.
*
* The bump to 21 moved tree conflict storage from the parent to the
* conflicted node. Bumped in r1034436.
*
* The bump to 22 moved tree conflict storage from conflict_data column
* to the tree_conflict_data column. Bumped in r1040255.
*
* The bump to 23 introduced multi-layer op_depth processing for NODES.
* Bumped in r1044384.
*
* The bump to 24 started using the 'refcount' column of the PRISTINE table
* correctly, instead of always setting it to '1'. Bumped in r1058523.
*
* The bump to 25 introduced the NODES_CURRENT view. Bumped in r1071283.
*
* The bump to 26 introduced the NODES_BASE view. Bumped in r1076617.
*
* The bump to 27 stored conflict files as relpaths rather than basenames.
* Bumped in r1089593.
*
* The bump to 28 converted any remaining references to MD5 checksums
* to SHA1 checksums. Bumped in r1095214.
*
* The bump to 29 renamed the pristine files from '<SHA1>' to '<SHA1>.svn-base'
* and introduced the EXTERNALS store. Bumped in r1129286.
*
* == 1.7.x shipped with format 29
*
* The bump to 30 switched the conflict storage to a skel inside conflict_data.
* Also clears some known invalid state. Bumped in r1387742.
*
* The bump to 31 added the inherited_props column in the NODES table.
* Bumped in r1395109.
*
* == 1.8.x shipped with format 31
*
* Please document any further format changes here.
*/
#define SVN_WC__VERSION 31
/* Formats <= this have no concept of "revert text-base/props". */
#define SVN_WC__NO_REVERT_FILES 4
/* A version <= this has wcprops stored in one file per entry. */
#define SVN_WC__WCPROPS_MANY_FILES_VERSION 7
/* A version < this can have urls that aren't canonical according to the new
rules. See issue #2475. */
#define SVN_WC__CHANGED_CANONICAL_URLS 10
/* The format number written to wc-ng working copies so that old clients
can recognize them as "newer Subversion"'s working copies. */
#define SVN_WC__NON_ENTRIES 12
#define SVN_WC__NON_ENTRIES_STRING "12\n"
/* A version < this uses the old 'entries' file mechanism. */
#define SVN_WC__WC_NG_VERSION 12
/* In this version, the wcprops are "lost" between files and wc.db. We want
to ignore them in upgrades. */
#define SVN_WC__WCPROPS_LOST 12
/* A version < this has no work queue (see workqueue.h). */
#define SVN_WC__HAS_WORK_QUEUE 13
+/* While we still have this DB version we should verify if there is
+ sqlite_stat1 table on opening */
+#define SVN_WC__ENSURE_STAT1_TABLE 31
+
/* Return a string indicating the released version (or versions) of
* Subversion that used WC format number WC_FORMAT, or some other
* suitable string if no released version used WC_FORMAT.
*
* ### It's not ideal to encode this sort of knowledge in this low-level
* library. On the other hand, it doesn't need to be updated often and
* should be easily found when it does need to be updated. */
const char *
svn_wc__version_string_from_format(int wc_format);
/* Return true iff error E indicates an "is not a working copy" type
of error, either because something wasn't a working copy at all, or
because it's a working copy from a previous version (in need of
upgrade). */
#define SVN_WC__ERR_IS_NOT_CURRENT_WC(e) \
((e->apr_err == SVN_ERR_WC_NOT_WORKING_COPY) || \
(e->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED))
/*** Context handling ***/
struct svn_wc_context_t
{
/* The wc_db handle for this working copy. */
svn_wc__db_t *db;
/* Close the DB when we destroy this context?
(This is used inside backward compat wrappers, and should only be
modified by the proper create() functions. */
svn_boolean_t close_db_on_destroy;
/* The state pool for this context. */
apr_pool_t *state_pool;
};
/**
* Just like svn_wc_context_create(), only use the provided DB to construct
* the context.
*
* Even though DB is not allocated from the same pool at *WC_CTX, it is
* expected to remain open throughout the life of *WC_CTX.
*/
svn_error_t *
svn_wc__context_create_with_db(svn_wc_context_t **wc_ctx,
svn_config_t *config,
svn_wc__db_t *db,
apr_pool_t *result_pool);
/*** Committed Queue ***/
/**
* Return the pool associated with QUEUE. (This so we can keep some
* deprecated functions that need to peek inside the QUEUE struct in
* deprecated.c).
*/
apr_pool_t *
svn_wc__get_committed_queue_pool(const struct svn_wc_committed_queue_t *queue);
/** Internal helper for svn_wc_process_committed_queue2().
*
* ### If @a queue is NULL, then ...?
* ### else:
* Bump an item from @a queue (the one associated with @a
* local_abspath) to @a new_revnum after a commit succeeds, recursing
* if @a recurse is set.
*
* @a new_date is the (server-side) date of the new revision, or 0.
*
* @a rev_author is the (server-side) author of the new
* revision; it may be @c NULL.
*
* @a new_dav_cache is a hash of dav property changes to be made to
* the @a local_abspath.
* ### [JAF] Is it? See svn_wc_queue_committed3(). It ends up being
* ### assigned as a whole to wc.db:BASE_NODE:dav_cache.
*
* If @a no_unlock is set, don't release any user locks on @a
* local_abspath; otherwise release them as part of this processing.
*
* If @a keep_changelist is set, don't remove any changeset assignments
* from @a local_abspath; otherwise, clear it of such assignments.
*
* If @a sha1_checksum is non-NULL, use it to identify the node's pristine
* text.
*
* Set TOP_OF_RECURSE to TRUE to show that this the top of a possibly
* recursive commit operation.
*/
svn_error_t *
svn_wc__process_committed_internal(svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t recurse,
svn_boolean_t top_of_recurse,
svn_revnum_t new_revnum,
apr_time_t new_date,
const char *rev_author,
apr_hash_t *new_dav_cache,
svn_boolean_t no_unlock,
svn_boolean_t keep_changelist,
const svn_checksum_t *sha1_checksum,
const svn_wc_committed_queue_t *queue,
apr_pool_t *scratch_pool);
/*** Update traversals. ***/
struct svn_wc_traversal_info_t
{
/* The pool in which this structure and everything inside it is
allocated. */
apr_pool_t *pool;
/* The before and after values of the SVN_PROP_EXTERNALS property,
* for each directory on which that property changed. These have
* the same layout as those returned by svn_wc_edited_externals().
*
* The hashes, their keys, and their values are allocated in the
* above pool.
*/
apr_hash_t *externals_old;
apr_hash_t *externals_new;
/* The ambient depths of the working copy directories. The keys are
working copy paths (as for svn_wc_edited_externals()), the values
are the result of svn_depth_to_word(depth_of_each_dir). */
apr_hash_t *depths;
};
/*** Names and file/dir operations in the administrative area. ***/
/** The files within the administrative subdir. **/
#define SVN_WC__ADM_FORMAT "format"
#define SVN_WC__ADM_ENTRIES "entries"
#define SVN_WC__ADM_TMP "tmp"
#define SVN_WC__ADM_PRISTINE "pristine"
#define SVN_WC__ADM_NONEXISTENT_PATH "nonexistent-path"
/* The basename of the ".prej" file, if a directory ever has property
conflicts. This .prej file will appear *within* the conflicted
directory. */
#define SVN_WC__THIS_DIR_PREJ "dir_conflicts"
/* A few declarations for stuff in util.c.
* If this section gets big, move it all out into a new util.h file. */
/* Ensure that DIR exists. */
svn_error_t *svn_wc__ensure_directory(const char *path, apr_pool_t *pool);
/* Return a hash keyed by 'const char *' property names and with
'svn_string_t *' values built from PROPS (which is an array of
pointers to svn_prop_t's) or to NULL if PROPS is NULL or empty.
PROPS items which lack a value will be ignored. If PROPS contains
multiple properties with the same name, each successive such item
reached in a walk from the beginning to the end of the array will
overwrite the previous in the returned hash.
NOTE: While the returned hash will be allocated in RESULT_POOL, the
items it holds will share storage with those in PROPS.
### This is rather the reverse of svn_prop_hash_to_array(), except
### that function's arrays contains svn_prop_t's, whereas this
### one's contains *pointers* to svn_prop_t's. So much for
### consistency. */
apr_hash_t *
svn_wc__prop_array_to_hash(const apr_array_header_t *props,
apr_pool_t *result_pool);
/* Set *MODIFIED_P to non-zero if LOCAL_ABSPATH's text is modified with
* regard to the base revision, else set *MODIFIED_P to zero.
*
* If EXACT_COMPARISON is FALSE, translate LOCAL_ABSPATH's EOL
* style and keywords to repository-normal form according to its properties,
* and compare the result with the text base.
* Usually, EXACT_COMPARISON should be FALSE.
*
* If LOCAL_ABSPATH does not exist, consider it unmodified. If it exists
* but is not under revision control (not even scheduled for
* addition), return the error SVN_WC_PATH_NOT_FOUND.
*
* If the text is unmodified and a write-lock is held this function
* will ensure that the last-known-unmodified timestamp and
* filesize of the file as recorded in DB matches the corresponding
* attributes of the actual file. (This is often referred to as
* "timestamp repair", and serves to help future unforced is-modified
* checks return quickly if the file remains untouched.)
*/
svn_error_t *
svn_wc__internal_file_modified_p(svn_boolean_t *modified_p,
svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t exact_comparison,
apr_pool_t *scratch_pool);
/* Prepare to merge a file content change into the working copy.
This does not merge properties; see svn_wc__merge_props() for that.
This does not necessarily change the file TARGET_ABSPATH on disk; it
may instead return work items that will replace the file on disk when
they are run. ### Can we be more consistent about this?
Merge the difference between LEFT_ABSPATH and RIGHT_ABSPATH into
TARGET_ABSPATH.
Set *WORK_ITEMS to the appropriate work queue operations.
If there are any conflicts, append a conflict description to
*CONFLICT_SKEL. (First allocate *CONFLICT_SKEL from RESULT_POOL if
it is initially NULL. CONFLICT_SKEL itself must not be NULL.)
Also, unless it is considered to be a 'binary' file, mark any
conflicts in the text of the file TARGET_ABSPATH using LEFT_LABEL,
RIGHT_LABEL and TARGET_LABEL.
Set *MERGE_OUTCOME to indicate the result.
When DRY_RUN is true, no actual changes are made to the working copy.
If DIFF3_CMD is specified, the given external diff3 tool will
be used instead of our built in diff3 routines.
When MERGE_OPTIONS are specified, they are used by the internal
diff3 routines, or passed to the external diff3 tool.
WRI_ABSPATH describes in which working copy information should be
retrieved. (Interesting for merging file externals).
OLD_ACTUAL_PROPS is the set of actual properties before merging; used for
detranslating the file before merging. This is necessary because, in
the case of updating, the update can have sent new properties, so we
cannot simply fetch and use the current actual properties.
### Is OLD_ACTUAL_PROPS still necessary, now that we first prepare the
content change and property change and then apply them both to
the WC together?
Property changes sent by the update are provided in PROP_DIFF.
For a complete description, see svn_wc_merge5() for which this is
the (loggy) implementation.
*WORK_ITEMS will be allocated in RESULT_POOL. All temporary allocations
will be performed in SCRATCH_POOL.
*/
svn_error_t *
svn_wc__internal_merge(svn_skel_t **work_items,
svn_skel_t **conflict_skel,
enum svn_wc_merge_outcome_t *merge_outcome,
svn_wc__db_t *db,
const char *left_abspath,
const char *right_abspath,
const char *target_abspath,
const char *wri_abspath,
const char *left_label,
const char *right_label,
const char *target_label,
apr_hash_t *old_actual_props,
svn_boolean_t dry_run,
const char *diff3_cmd,
const apr_array_header_t *merge_options,
const apr_array_header_t *prop_diff,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* A default error handler for svn_wc_walk_entries3(). Returns ERR in
all cases. */
svn_error_t *
svn_wc__walker_default_error_handler(const char *path,
svn_error_t *err,
void *walk_baton,
apr_pool_t *pool);
/* Set *EDITOR and *EDIT_BATON to an ambient-depth-based filtering
* editor that wraps WRAPPED_EDITOR and WRAPPED_BATON. This is only
* required for operations where the requested depth is @c
* svn_depth_unknown and the server's editor driver doesn't understand
* depth. It is safe for *EDITOR and *EDIT_BATON to start as
* WRAPPED_EDITOR and WRAPPED_BATON.
*
* ANCHOR, TARGET, and DB are as in svn_wc_get_update_editor3.
*
* @a requested_depth must be one of the following depth values:
* @c svn_depth_infinity, @c svn_depth_empty, @c svn_depth_files,
* @c svn_depth_immediates, or @c svn_depth_unknown.
*
* Allocations are done in POOL.
*/
svn_error_t *
svn_wc__ambient_depth_filter_editor(const svn_delta_editor_t **editor,
void **edit_baton,
svn_wc__db_t *db,
const char *anchor_abspath,
const char *target,
const svn_delta_editor_t *wrapped_editor,
void *wrapped_edit_baton,
apr_pool_t *result_pool);
/* Similar to svn_wc_conflicted_p3(), but with a wc_db parameter in place of
* a wc_context. */
svn_error_t *
svn_wc__internal_conflicted_p(svn_boolean_t *text_conflicted_p,
svn_boolean_t *prop_conflicted_p,
svn_boolean_t *tree_conflicted_p,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool);
/* Similar to svn_wc__internal_conflicted_p(), but ignores
* moved-away-edit tree conflicts. If CONFLICT_IGNORED_P is not NULL
* then sets *CONFLICT_IGNORED_P TRUE if a tree-conflict is ignored
* and FALSE otherwise. Also ignores text and property conflicts if
* TREE_ONLY is TRUE */
svn_error_t *
svn_wc__conflicted_for_update_p(svn_boolean_t *conflicted_p,
svn_boolean_t *conflict_ignored_p,
svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t tree_only,
apr_pool_t *scratch_pool);
/* Internal version of svn_wc_transmit_text_deltas3(). */
svn_error_t *
svn_wc__internal_transmit_text_deltas(const char **tempfile,
const svn_checksum_t **new_text_base_md5_checksum,
const svn_checksum_t **new_text_base_sha1_checksum,
svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t fulltext,
const svn_delta_editor_t *editor,
void *file_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Internal version of svn_wc_transmit_prop_deltas2(). */
svn_error_t *
svn_wc__internal_transmit_prop_deltas(svn_wc__db_t *db,
const char *local_abspath,
const svn_delta_editor_t *editor,
void *baton,
apr_pool_t *scratch_pool);
/* Library-internal version of svn_wc_ensure_adm4(). */
svn_error_t *
svn_wc__internal_ensure_adm(svn_wc__db_t *db,
const char *local_abspath,
const char *url,
const char *repos_root_url,
const char *repos_uuid,
svn_revnum_t revision,
svn_depth_t depth,
apr_pool_t *scratch_pool);
/* Library-internal version of svn_wc__changelist_match(). */
svn_boolean_t
svn_wc__internal_changelist_match(svn_wc__db_t *db,
const char *local_abspath,
const apr_hash_t *clhash,
apr_pool_t *scratch_pool);
/* Library-internal version of svn_wc_walk_status(), which see. */
svn_error_t *
svn_wc__internal_walk_status(svn_wc__db_t *db,
const char *local_abspath,
svn_depth_t depth,
svn_boolean_t get_all,
svn_boolean_t no_ignore,
svn_boolean_t ignore_text_mods,
const apr_array_header_t *ignore_patterns,
svn_wc_status_func4_t status_func,
void *status_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool);
/** A callback invoked by the generic node-walker function. */
typedef svn_error_t *(*svn_wc__node_found_func_t)(const char *local_abspath,
svn_node_kind_t kind,
void *walk_baton,
apr_pool_t *scratch_pool);
/* Call @a walk_callback with @a walk_baton for @a local_abspath and all
nodes underneath it, restricted by @a walk_depth, and possibly
@a changelists.
If @a show_hidden is true, include hidden nodes, else ignore them.
If CHANGELISTS is non-NULL and non-empty, filter thereon. */
svn_error_t *
svn_wc__internal_walk_children(svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t show_hidden,
const apr_array_header_t *changelists,
svn_wc__node_found_func_t walk_callback,
void *walk_baton,
svn_depth_t walk_depth,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool);
/* Library-internal version of svn_wc_remove_from_revision_control2,
which see.*/
svn_error_t *
svn_wc__internal_remove_from_revision_control(svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t destroy_wf,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool);
/* Library-internal version of svn_wc__node_get_schedule(). */
svn_error_t *
svn_wc__internal_node_get_schedule(svn_wc_schedule_t *schedule,
svn_boolean_t *copied,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool);
/* Internal version of svn_wc__node_get_origin() */
svn_error_t *
svn_wc__internal_get_origin(svn_boolean_t *is_copy,
svn_revnum_t *revision,
const char **repos_relpath,
const char **repos_root_url,
const char **repos_uuid,
const char **copy_root_abspath,
svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t scan_deleted,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Internal version of svn_wc__node_get_repos_info() */
svn_error_t *
svn_wc__internal_get_repos_info(svn_revnum_t *revision,
const char **repos_relpath,
const char **repos_root_url,
const char **repos_uuid,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Upgrade the wc sqlite database given in SDB for the wc located at
WCROOT_ABSPATH. It's current/starting format is given by START_FORMAT.
After the upgrade is complete (to as far as the automatic upgrade will
perform), the resulting format is RESULT_FORMAT. All allocations are
performed in SCRATCH_POOL. */
svn_error_t *
svn_wc__upgrade_sdb(int *result_format,
const char *wcroot_abspath,
svn_sqlite__db_t *sdb,
int start_format,
apr_pool_t *scratch_pool);
/* Create a conflict skel from the old separated data */
svn_error_t *
svn_wc__upgrade_conflict_skel_from_raw(svn_skel_t **conflicts,
svn_wc__db_t *db,
const char *wri_abspath,
const char *local_relpath,
const char *conflict_old,
const char *conflict_wrk,
const char *conflict_new,
const char *prej_file,
const char *tree_conflict_data,
apr_size_t tree_conflict_len,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
svn_error_t *
svn_wc__wipe_postupgrade(const char *dir_abspath,
svn_boolean_t whole_admin,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool);
/* Ensure LOCAL_ABSPATH is still locked in DB. Returns the error
* SVN_ERR_WC_NOT_LOCKED if this is not the case.
*/
svn_error_t *
svn_wc__write_check(svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool);
/* Read into CONFLICTS svn_wc_conflict_description2_t* structs
* for all conflicts that have LOCAL_ABSPATH as victim.
*
* Victim must be versioned or be part of a tree conflict.
*
* If CREATE_TEMPFILES is TRUE, create temporary files for property conflicts.
*
* Allocate *CONFLICTS in RESULT_POOL and do temporary allocations in
* SCRATCH_POOL
*/
svn_error_t *
svn_wc__read_conflicts(const apr_array_header_t **conflicts,
svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t create_tempfiles,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Perform the actual merge of file changes between an original file,
identified by ORIGINAL_CHECKSUM (an empty file if NULL) to a new file
identified by NEW_CHECKSUM in the working copy identified by WRI_ABSPATH.
Merge the result into LOCAL_ABSPATH, which is part of the working copy
identified by WRI_ABSPATH. Use OLD_REVISION and TARGET_REVISION for naming
the intermediate files.
Set *FOUND_TEXT_CONFLICT to TRUE when the merge encountered a conflict,
otherwise to FALSE.
The rest of the arguments are passed to svn_wc__internal_merge.
*/
svn_error_t *
svn_wc__perform_file_merge(svn_skel_t **work_items,
svn_skel_t **conflict_skel,
svn_boolean_t *found_conflict,
svn_wc__db_t *db,
const char *local_abspath,
const char *wri_abspath,
const svn_checksum_t *new_checksum,
const svn_checksum_t *original_checksum,
apr_hash_t *old_actual_props,
const apr_array_header_t *ext_patterns,
svn_revnum_t old_revision,
svn_revnum_t target_revision,
const apr_array_header_t *propchanges,
const char *diff3_cmd,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Couple of random helpers for the Ev2 shims.
### These will eventually be obsoleted and removed. */
struct svn_wc__shim_fetch_baton_t
{
svn_wc__db_t *db;
const char *base_abspath;
svn_boolean_t fetch_base;
};
/* Using a BATON of struct shim_fetch_baton, return KIND for PATH. */
svn_error_t *
svn_wc__fetch_kind_func(svn_node_kind_t *kind,
void *baton,
const char *path,
svn_revnum_t base_revision,
apr_pool_t *scratch_pool);
/* Using a BATON of struct shim_fetch_baton, return PROPS for PATH. */
svn_error_t *
svn_wc__fetch_props_func(apr_hash_t **props,
void *baton,
const char *path,
svn_revnum_t base_revision,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Using a BATON of struct shim_fetch_baton, return a delta base for PATH. */
svn_error_t *
svn_wc__fetch_base_func(const char **filename,
void *baton,
const char *path,
svn_revnum_t base_revision,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Find duplicate targets in *EXTERNALS, a list of svn_wc_external_item2_t*
* elements, and store each target string in *DUPLICATE_TARGETS as const
* char * elements. *DUPLICATE_TARGETS will be NULL if no duplicates were
* found. */
svn_error_t *
svn_wc__externals_find_target_dups(apr_array_header_t **duplicate_targets,
apr_array_header_t *externals,
apr_pool_t *pool,
apr_pool_t *scratch_pool);
/* Revert tree LOCAL_ABSPATH to depth DEPTH and notify for all
reverts. */
svn_error_t *
svn_wc__revert_internal(svn_wc__db_t *db,
const char *local_abspath,
svn_depth_t depth,
svn_boolean_t use_commit_times,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool);
svn_error_t *
svn_wc__node_has_local_mods(svn_boolean_t *modified,
svn_boolean_t *all_edits_are_deletes,
svn_wc__db_t *db,
const char *local_abspath,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* SVN_LIBSVN_WC_H */
Index: vendor/subversion/dist/subversion/libsvn_wc/wc_db.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_wc/wc_db.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_wc/wc_db.c (revision 286501)
@@ -1,15551 +1,15610 @@
/*
* wc_db.c : manipulating the administrative database
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#define SVN_WC__I_AM_WC_DB
#include <assert.h>
#include <apr_pools.h>
#include <apr_hash.h>
#include "svn_types.h"
#include "svn_error.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_hash.h"
#include "svn_sorts.h"
#include "svn_wc.h"
#include "svn_checksum.h"
#include "svn_pools.h"
#include "wc.h"
#include "wc_db.h"
#include "adm_files.h"
#include "wc-queries.h"
#include "entries.h"
#include "lock.h"
#include "conflicts.h"
#include "wc_db_private.h"
#include "workqueue.h"
#include "token-map.h"
#include "svn_private_config.h"
#include "private/svn_sqlite.h"
#include "private/svn_skel.h"
#include "private/svn_wc_private.h"
#include "private/svn_token.h"
#define NOT_IMPLEMENTED() SVN__NOT_IMPLEMENTED()
/*
* Some filename constants.
*/
#define SDB_FILE "wc.db"
#define WCROOT_TEMPDIR_RELPATH "tmp"
/*
* PARAMETER ASSERTIONS
*
* Every (semi-)public entrypoint in this file has a set of assertions on
* the parameters passed into the function. Since this is a brand new API,
* we want to make sure that everybody calls it properly. The original WC
* code had years to catch stray bugs, but we do not have that luxury in
* the wc-nb rewrite. Any extra assurances that we can find will be
* welcome. The asserts will ensure we have no doubt about the values
* passed into the function.
*
* Some parameters are *not* specifically asserted. Typically, these are
* params that will be used immediately, so something like a NULL value
* will be obvious.
*
* ### near 1.7 release, it would be a Good Thing to review the assertions
* ### and decide if any can be removed or switched to assert() in order
* ### to remove their runtime cost in the production release.
*
*
* DATABASE OPERATIONS
*
* Each function should leave the database in a consistent state. If it
* does *not*, then the implication is some other function needs to be
* called to restore consistency. Subtle requirements like that are hard
* to maintain over a long period of time, so this API will not allow it.
*
*
* STANDARD VARIABLE NAMES
*
* db working copy database (this module)
* sdb SQLite database (not to be confused with 'db')
* wc_id a WCROOT id associated with a node
*/
#define INVALID_REPOS_ID ((apr_int64_t) -1)
#define UNKNOWN_WC_ID ((apr_int64_t) -1)
#define FORMAT_FROM_SDB (-1)
/* Check if column number I, a property-skel column, contains a non-empty
set of properties. The empty set of properties is stored as "()", so we
have properties if the size of the column is larger than 2. */
#define SQLITE_PROPERTIES_AVAILABLE(stmt, i) \
(svn_sqlite__column_bytes(stmt, i) > 2)
int
svn_wc__db_op_depth_for_upgrade(const char *local_relpath)
{
return relpath_depth(local_relpath);
}
/* Representation of a new base row for the NODES table */
typedef struct insert_base_baton_t {
/* common to all insertions into BASE */
svn_wc__db_status_t status;
svn_node_kind_t kind;
apr_int64_t repos_id;
const char *repos_relpath;
svn_revnum_t revision;
/* Only used when repos_id == INVALID_REPOS_ID */
const char *repos_root_url;
const char *repos_uuid;
/* common to all "normal" presence insertions */
const apr_hash_t *props;
svn_revnum_t changed_rev;
apr_time_t changed_date;
const char *changed_author;
const apr_hash_t *dav_cache;
/* for inserting directories */
const apr_array_header_t *children;
svn_depth_t depth;
/* for inserting files */
const svn_checksum_t *checksum;
/* for inserting symlinks */
const char *target;
svn_boolean_t file_external;
/* may need to insert/update ACTUAL to record a conflict */
const svn_skel_t *conflict;
/* may need to insert/update ACTUAL to record new properties */
svn_boolean_t update_actual_props;
const apr_hash_t *new_actual_props;
/* A depth-first ordered array of svn_prop_inherited_item_t *
structures representing the properties inherited by the base
node. */
apr_array_header_t *iprops;
/* maybe we should copy information from a previous record? */
svn_boolean_t keep_recorded_info;
/* insert a base-deleted working node as well as a base node */
svn_boolean_t insert_base_deleted;
/* delete the current working nodes above BASE */
svn_boolean_t delete_working;
/* may have work items to queue in this transaction */
const svn_skel_t *work_items;
} insert_base_baton_t;
/* Representation of a new working row for the NODES table */
typedef struct insert_working_baton_t {
/* common to all insertions into WORKING (including NODE_DATA) */
svn_wc__db_status_t presence;
svn_node_kind_t kind;
int op_depth;
/* common to all "normal" presence insertions */
const apr_hash_t *props;
svn_revnum_t changed_rev;
apr_time_t changed_date;
const char *changed_author;
apr_int64_t original_repos_id;
const char *original_repos_relpath;
svn_revnum_t original_revnum;
svn_boolean_t moved_here;
/* for inserting directories */
const apr_array_header_t *children;
svn_depth_t depth;
/* for inserting (copied/moved-here) files */
const svn_checksum_t *checksum;
/* for inserting symlinks */
const char *target;
svn_boolean_t update_actual_props;
const apr_hash_t *new_actual_props;
/* may have work items to queue in this transaction */
const svn_skel_t *work_items;
/* may have conflict to install in this transaction */
const svn_skel_t *conflict;
/* If the value is > 0 and < op_depth, also insert a not-present
at op-depth NOT_PRESENT_OP_DEPTH, based on this same information */
int not_present_op_depth;
} insert_working_baton_t;
/* Representation of a new row for the EXTERNALS table */
typedef struct insert_external_baton_t {
/* common to all insertions into EXTERNALS */
svn_node_kind_t kind;
svn_wc__db_status_t presence;
/* The repository of the external */
apr_int64_t repos_id;
/* for file and symlink externals */
const char *repos_relpath;
svn_revnum_t revision;
/* Only used when repos_id == INVALID_REPOS_ID */
const char *repos_root_url;
const char *repos_uuid;
/* for file and symlink externals */
const apr_hash_t *props;
apr_array_header_t *iprops;
svn_revnum_t changed_rev;
apr_time_t changed_date;
const char *changed_author;
const apr_hash_t *dav_cache;
/* for inserting files */
const svn_checksum_t *checksum;
/* for inserting symlinks */
const char *target;
const char *record_ancestor_relpath;
const char *recorded_repos_relpath;
svn_revnum_t recorded_peg_revision;
svn_revnum_t recorded_revision;
/* may need to insert/update ACTUAL to record a conflict */
const svn_skel_t *conflict;
/* may need to insert/update ACTUAL to record new properties */
svn_boolean_t update_actual_props;
const apr_hash_t *new_actual_props;
/* maybe we should copy information from a previous record? */
svn_boolean_t keep_recorded_info;
/* may have work items to queue in this transaction */
const svn_skel_t *work_items;
} insert_external_baton_t;
/* Forward declarations */
static svn_error_t *
add_work_items(svn_sqlite__db_t *sdb,
const svn_skel_t *skel,
apr_pool_t *scratch_pool);
static svn_error_t *
set_actual_props(apr_int64_t wc_id,
const char *local_relpath,
apr_hash_t *props,
svn_sqlite__db_t *db,
apr_pool_t *scratch_pool);
static svn_error_t *
insert_incomplete_children(svn_sqlite__db_t *sdb,
apr_int64_t wc_id,
const char *local_relpath,
apr_int64_t repos_id,
const char *repos_relpath,
svn_revnum_t revision,
const apr_array_header_t *children,
int op_depth,
apr_pool_t *scratch_pool);
static svn_error_t *
db_read_pristine_props(apr_hash_t **props,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
svn_boolean_t deleted_ok,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
static svn_error_t *
read_info(svn_wc__db_status_t *status,
svn_node_kind_t *kind,
svn_revnum_t *revision,
const char **repos_relpath,
apr_int64_t *repos_id,
svn_revnum_t *changed_rev,
apr_time_t *changed_date,
const char **changed_author,
svn_depth_t *depth,
const svn_checksum_t **checksum,
const char **target,
const char **original_repos_relpath,
apr_int64_t *original_repos_id,
svn_revnum_t *original_revision,
svn_wc__db_lock_t **lock,
svn_filesize_t *recorded_size,
apr_time_t *recorded_time,
const char **changelist,
svn_boolean_t *conflicted,
svn_boolean_t *op_root,
svn_boolean_t *had_props,
svn_boolean_t *props_mod,
svn_boolean_t *have_base,
svn_boolean_t *have_more_work,
svn_boolean_t *have_work,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
static svn_error_t *
scan_addition(svn_wc__db_status_t *status,
const char **op_root_relpath,
const char **repos_relpath,
apr_int64_t *repos_id,
const char **original_repos_relpath,
apr_int64_t *original_repos_id,
svn_revnum_t *original_revision,
const char **moved_from_relpath,
const char **moved_from_op_root_relpath,
int *moved_from_op_depth,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
static svn_error_t *
convert_to_working_status(svn_wc__db_status_t *working_status,
svn_wc__db_status_t status);
static svn_error_t *
wclock_owns_lock(svn_boolean_t *own_lock,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
svn_boolean_t exact,
apr_pool_t *scratch_pool);
static svn_error_t *
db_is_switched(svn_boolean_t *is_switched,
svn_node_kind_t *kind,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *scratch_pool);
/* Return the absolute path, in local path style, of LOCAL_RELPATH
in WCROOT. */
static const char *
path_for_error_message(const svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *result_pool)
{
const char *local_abspath
= svn_dirent_join(wcroot->abspath, local_relpath, result_pool);
return svn_dirent_local_style(local_abspath, result_pool);
}
/* Return a file size from column SLOT of the SQLITE statement STMT, or
SVN_INVALID_FILESIZE if the column value is NULL. */
static svn_filesize_t
get_recorded_size(svn_sqlite__stmt_t *stmt, int slot)
{
if (svn_sqlite__column_is_null(stmt, slot))
return SVN_INVALID_FILESIZE;
return svn_sqlite__column_int64(stmt, slot);
}
/* Return a lock info structure constructed from the given columns of the
SQLITE statement STMT, or return NULL if the token column value is null. */
static svn_wc__db_lock_t *
lock_from_columns(svn_sqlite__stmt_t *stmt,
int col_token,
int col_owner,
int col_comment,
int col_date,
apr_pool_t *result_pool)
{
svn_wc__db_lock_t *lock;
if (svn_sqlite__column_is_null(stmt, col_token))
{
lock = NULL;
}
else
{
lock = apr_pcalloc(result_pool, sizeof(svn_wc__db_lock_t));
lock->token = svn_sqlite__column_text(stmt, col_token, result_pool);
lock->owner = svn_sqlite__column_text(stmt, col_owner, result_pool);
lock->comment = svn_sqlite__column_text(stmt, col_comment, result_pool);
lock->date = svn_sqlite__column_int64(stmt, col_date);
}
return lock;
}
svn_error_t *
svn_wc__db_fetch_repos_info(const char **repos_root_url,
const char **repos_uuid,
svn_sqlite__db_t *sdb,
apr_int64_t repos_id,
apr_pool_t *result_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
if (!repos_root_url && !repos_uuid)
return SVN_NO_ERROR;
if (repos_id == INVALID_REPOS_ID)
{
if (repos_root_url)
*repos_root_url = NULL;
if (repos_uuid)
*repos_uuid = NULL;
return SVN_NO_ERROR;
}
SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
STMT_SELECT_REPOSITORY_BY_ID));
SVN_ERR(svn_sqlite__bindf(stmt, "i", repos_id));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (!have_row)
return svn_error_createf(SVN_ERR_WC_CORRUPT, svn_sqlite__reset(stmt),
_("No REPOSITORY table entry for id '%ld'"),
(long int)repos_id);
if (repos_root_url)
*repos_root_url = svn_sqlite__column_text(stmt, 0, result_pool);
if (repos_uuid)
*repos_uuid = svn_sqlite__column_text(stmt, 1, result_pool);
return svn_error_trace(svn_sqlite__reset(stmt));
}
/* Set *REPOS_ID, *REVISION and *REPOS_RELPATH from the given columns of the
SQLITE statement STMT, or to NULL/SVN_INVALID_REVNUM if the respective
column value is null. Any of the output parameters may be NULL if not
required. */
static void
repos_location_from_columns(apr_int64_t *repos_id,
svn_revnum_t *revision,
const char **repos_relpath,
svn_sqlite__stmt_t *stmt,
int col_repos_id,
int col_revision,
int col_repos_relpath,
apr_pool_t *result_pool)
{
if (repos_id)
{
/* Fetch repository information via REPOS_ID. */
if (svn_sqlite__column_is_null(stmt, col_repos_id))
*repos_id = INVALID_REPOS_ID;
else
*repos_id = svn_sqlite__column_int64(stmt, col_repos_id);
}
if (revision)
{
*revision = svn_sqlite__column_revnum(stmt, col_revision);
}
if (repos_relpath)
{
*repos_relpath = svn_sqlite__column_text(stmt, col_repos_relpath,
result_pool);
}
}
/* Get the statement given by STMT_IDX, and bind the appropriate wc_id and
local_relpath based upon LOCAL_ABSPATH. Store it in *STMT, and use
SCRATCH_POOL for temporary allocations.
Note: WC_ID and LOCAL_RELPATH must be arguments 1 and 2 in the statement. */
static svn_error_t *
get_statement_for_path(svn_sqlite__stmt_t **stmt,
svn_wc__db_t *db,
const char *local_abspath,
int stmt_idx,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_ERR(svn_sqlite__get_statement(stmt, wcroot->sdb, stmt_idx));
SVN_ERR(svn_sqlite__bindf(*stmt, "is", wcroot->wc_id, local_relpath));
return SVN_NO_ERROR;
}
/* For a given REPOS_ROOT_URL/REPOS_UUID pair, return the existing REPOS_ID
value. If one does not exist, then create a new one. */
static svn_error_t *
create_repos_id(apr_int64_t *repos_id,
const char *repos_root_url,
const char *repos_uuid,
svn_sqlite__db_t *sdb,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *get_stmt;
svn_sqlite__stmt_t *insert_stmt;
svn_boolean_t have_row;
SVN_ERR(svn_sqlite__get_statement(&get_stmt, sdb, STMT_SELECT_REPOSITORY));
SVN_ERR(svn_sqlite__bindf(get_stmt, "s", repos_root_url));
SVN_ERR(svn_sqlite__step(&have_row, get_stmt));
if (have_row)
{
*repos_id = svn_sqlite__column_int64(get_stmt, 0);
return svn_error_trace(svn_sqlite__reset(get_stmt));
}
SVN_ERR(svn_sqlite__reset(get_stmt));
/* NOTE: strictly speaking, there is a race condition between the
above query and the insertion below. We're simply going to ignore
that, as it means two processes are *modifying* the working copy
at the same time, *and* new repositores are becoming visible.
This is rare enough, let alone the miniscule chance of hitting
this race condition. Further, simply failing out will leave the
database in a consistent state, and the user can just re-run the
failed operation. */
SVN_ERR(svn_sqlite__get_statement(&insert_stmt, sdb,
STMT_INSERT_REPOSITORY));
SVN_ERR(svn_sqlite__bindf(insert_stmt, "ss", repos_root_url, repos_uuid));
return svn_error_trace(svn_sqlite__insert(repos_id, insert_stmt));
}
/* Initialize the baton with appropriate "blank" values. This allows the
insertion function to leave certain columns null. */
static void
blank_ibb(insert_base_baton_t *pibb)
{
memset(pibb, 0, sizeof(*pibb));
pibb->revision = SVN_INVALID_REVNUM;
pibb->changed_rev = SVN_INVALID_REVNUM;
pibb->depth = svn_depth_infinity;
pibb->repos_id = INVALID_REPOS_ID;
}
svn_error_t *
svn_wc__db_extend_parent_delete(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
svn_node_kind_t kind,
int op_depth,
apr_pool_t *scratch_pool)
{
svn_boolean_t have_row;
svn_sqlite__stmt_t *stmt;
int parent_op_depth;
const char *parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
SVN_ERR_ASSERT(local_relpath[0]);
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_LOWEST_WORKING_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, parent_relpath,
op_depth));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row)
parent_op_depth = svn_sqlite__column_int(stmt, 0);
SVN_ERR(svn_sqlite__reset(stmt));
if (have_row)
{
int existing_op_depth;
SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
op_depth));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row)
existing_op_depth = svn_sqlite__column_int(stmt, 0);
SVN_ERR(svn_sqlite__reset(stmt));
if (!have_row || parent_op_depth < existing_op_depth)
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_INSTALL_WORKING_NODE_FOR_DELETE));
SVN_ERR(svn_sqlite__bindf(stmt, "isdst", wcroot->wc_id,
local_relpath, parent_op_depth,
parent_relpath, kind_map, kind));
SVN_ERR(svn_sqlite__update(NULL, stmt));
}
}
return SVN_NO_ERROR;
}
/* This is the reverse of svn_wc__db_extend_parent_delete.
When removing a node if the parent has a higher working node then
the parent node and this node are both deleted or replaced and any
delete over this node must be removed.
This function (like most wcroot functions) assumes that its caller
only uses this function within an sqlite transaction if atomic
behavior is needed.
*/
svn_error_t *
svn_wc__db_retract_parent_delete(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
int op_depth,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
int working_depth;
svn_wc__db_status_t presence;
const char *moved_to;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_LOWEST_WORKING_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
op_depth));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (!have_row)
return svn_error_trace(svn_sqlite__reset(stmt));
working_depth = svn_sqlite__column_int(stmt, 0);
presence = svn_sqlite__column_token(stmt, 1, presence_map);
moved_to = svn_sqlite__column_text(stmt, 3, scratch_pool);
SVN_ERR(svn_sqlite__reset(stmt));
if (moved_to)
{
/* Turn the move into a copy to keep the NODES table valid */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_CLEAR_MOVED_HERE_RECURSIVE));
SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
moved_to, relpath_depth(moved_to)));
SVN_ERR(svn_sqlite__step_done(stmt));
/* This leaves just the moved_to information on the origin,
which we will remove in the next step */
}
if (presence == svn_wc__db_status_base_deleted)
{
/* Nothing left to shadow; remove the base-deleted node */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_NODE));
}
else if (moved_to)
{
/* Clear moved to information, as this node is no longer base-deleted */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_CLEAR_MOVED_TO_RELPATH));
}
else
{
/* Nothing to update */
return SVN_NO_ERROR;
}
SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
working_depth));
return svn_error_trace(svn_sqlite__update(NULL, stmt));
}
/* Insert the base row represented by (insert_base_baton_t *) BATON. */
static svn_error_t *
insert_base_node(const insert_base_baton_t *pibb,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *scratch_pool)
{
apr_int64_t repos_id = pibb->repos_id;
svn_sqlite__stmt_t *stmt;
svn_filesize_t recorded_size = SVN_INVALID_FILESIZE;
apr_int64_t recorded_time;
+ svn_boolean_t present;
/* The directory at the WCROOT has a NULL parent_relpath. Otherwise,
bind the appropriate parent_relpath. */
const char *parent_relpath =
(*local_relpath == '\0') ? NULL
: svn_relpath_dirname(local_relpath, scratch_pool);
if (pibb->repos_id == INVALID_REPOS_ID)
SVN_ERR(create_repos_id(&repos_id, pibb->repos_root_url, pibb->repos_uuid,
wcroot->sdb, scratch_pool));
SVN_ERR_ASSERT(repos_id != INVALID_REPOS_ID);
SVN_ERR_ASSERT(pibb->repos_relpath != NULL);
if (pibb->keep_recorded_info)
{
svn_boolean_t have_row;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_BASE_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row)
{
/* Preserve size and modification time if caller asked us to. */
recorded_size = get_recorded_size(stmt, 6);
recorded_time = svn_sqlite__column_int64(stmt, 12);
}
SVN_ERR(svn_sqlite__reset(stmt));
}
+ present = (pibb->status == svn_wc__db_status_normal
+ || pibb->status == svn_wc__db_status_incomplete);
+
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "isdsisr"
"tstr" /* 8 - 11 */
"isnnnnns", /* 12 - 19 */
wcroot->wc_id, /* 1 */
local_relpath, /* 2 */
0, /* op_depth is 0 for base */
parent_relpath, /* 4 */
repos_id,
pibb->repos_relpath,
pibb->revision,
presence_map, pibb->status, /* 8 */
- (pibb->kind == svn_node_dir) ? /* 9 */
- svn_token__to_word(depth_map, pibb->depth) : NULL,
+ (pibb->kind == svn_node_dir && present) /* 9 */
+ ? svn_token__to_word(depth_map, pibb->depth)
+ : NULL,
kind_map, pibb->kind, /* 10 */
pibb->changed_rev, /* 11 */
pibb->changed_date, /* 12 */
pibb->changed_author, /* 13 */
- (pibb->kind == svn_node_symlink) ?
+ (pibb->kind == svn_node_symlink && present) ?
pibb->target : NULL)); /* 19 */
- if (pibb->kind == svn_node_file)
+ if (pibb->kind == svn_node_file && present)
{
if (!pibb->checksum
&& pibb->status != svn_wc__db_status_not_present
&& pibb->status != svn_wc__db_status_excluded
&& pibb->status != svn_wc__db_status_server_excluded)
return svn_error_createf(SVN_ERR_WC_CORRUPT, svn_sqlite__reset(stmt),
_("The file '%s' has no checksum."),
path_for_error_message(wcroot, local_relpath,
scratch_pool));
SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, pibb->checksum,
scratch_pool));
if (recorded_size != SVN_INVALID_FILESIZE)
{
SVN_ERR(svn_sqlite__bind_int64(stmt, 16, recorded_size));
SVN_ERR(svn_sqlite__bind_int64(stmt, 17, recorded_time));
}
}
/* Set properties. Must be null if presence not normal or incomplete. */
assert(pibb->status == svn_wc__db_status_normal
|| pibb->status == svn_wc__db_status_incomplete
|| pibb->props == NULL);
- SVN_ERR(svn_sqlite__bind_properties(stmt, 15, pibb->props,
- scratch_pool));
+ if (present)
+ {
+ SVN_ERR(svn_sqlite__bind_properties(stmt, 15, pibb->props,
+ scratch_pool));
- SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, pibb->iprops,
+ SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, pibb->iprops,
scratch_pool));
+ }
if (pibb->dav_cache)
SVN_ERR(svn_sqlite__bind_properties(stmt, 18, pibb->dav_cache,
scratch_pool));
if (pibb->file_external)
SVN_ERR(svn_sqlite__bind_int(stmt, 20, 1));
SVN_ERR(svn_sqlite__insert(NULL, stmt));
if (pibb->update_actual_props)
{
/* Cast away const, to allow calling property helpers */
apr_hash_t *base_props = (apr_hash_t *)pibb->props;
apr_hash_t *new_actual_props = (apr_hash_t *)pibb->new_actual_props;
if (base_props != NULL
&& new_actual_props != NULL
&& (apr_hash_count(base_props) == apr_hash_count(new_actual_props)))
{
apr_array_header_t *diffs;
SVN_ERR(svn_prop_diffs(&diffs, new_actual_props, base_props,
scratch_pool));
if (diffs->nelts == 0)
new_actual_props = NULL;
}
SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath, new_actual_props,
wcroot->sdb, scratch_pool));
}
if (pibb->kind == svn_node_dir && pibb->children)
SVN_ERR(insert_incomplete_children(wcroot->sdb, wcroot->wc_id,
local_relpath,
repos_id,
pibb->repos_relpath,
pibb->revision,
pibb->children,
0 /* BASE */,
scratch_pool));
/* When this is not the root node, check shadowing behavior */
if (*local_relpath)
{
if (parent_relpath
&& ((pibb->status == svn_wc__db_status_normal)
|| (pibb->status == svn_wc__db_status_incomplete))
&& ! pibb->file_external)
{
SVN_ERR(svn_wc__db_extend_parent_delete(wcroot, local_relpath,
pibb->kind, 0,
scratch_pool));
}
else if (pibb->status == svn_wc__db_status_not_present
|| pibb->status == svn_wc__db_status_server_excluded
|| pibb->status == svn_wc__db_status_excluded)
{
SVN_ERR(svn_wc__db_retract_parent_delete(wcroot, local_relpath, 0,
scratch_pool));
}
}
if (pibb->delete_working)
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_WORKING_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
}
if (pibb->insert_base_deleted)
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_INSERT_DELETE_FROM_BASE));
SVN_ERR(svn_sqlite__bindf(stmt, "isd",
wcroot->wc_id, local_relpath,
relpath_depth(local_relpath)));
SVN_ERR(svn_sqlite__step_done(stmt));
}
SVN_ERR(add_work_items(wcroot->sdb, pibb->work_items, scratch_pool));
if (pibb->conflict)
SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
pibb->conflict, scratch_pool));
return SVN_NO_ERROR;
}
/* Initialize the baton with appropriate "blank" values. This allows the
insertion function to leave certain columns null. */
static void
blank_iwb(insert_working_baton_t *piwb)
{
memset(piwb, 0, sizeof(*piwb));
piwb->changed_rev = SVN_INVALID_REVNUM;
piwb->depth = svn_depth_infinity;
/* ORIGINAL_REPOS_ID and ORIGINAL_REVNUM could use some kind of "nil"
value, but... meh. We'll avoid them if ORIGINAL_REPOS_RELPATH==NULL. */
}
/* Insert a row in NODES for each (const char *) child name in CHILDREN,
whose parent directory is LOCAL_RELPATH, at op_depth=OP_DEPTH. Set each
child's presence to 'incomplete', kind to 'unknown', repos_id to REPOS_ID,
repos_path by appending the child name to REPOS_PATH, and revision to
REVISION (which should match the parent's revision).
If REPOS_ID is INVALID_REPOS_ID, set each child's repos_id to null. */
static svn_error_t *
insert_incomplete_children(svn_sqlite__db_t *sdb,
apr_int64_t wc_id,
const char *local_relpath,
apr_int64_t repos_id,
const char *repos_path,
svn_revnum_t revision,
const apr_array_header_t *children,
int op_depth,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
int i;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
apr_hash_t *moved_to_relpaths = apr_hash_make(scratch_pool);
SVN_ERR_ASSERT(repos_path != NULL || op_depth > 0);
SVN_ERR_ASSERT((repos_id != INVALID_REPOS_ID)
== (repos_path != NULL));
/* If we're inserting WORKING nodes, we might be replacing existing
* nodes which were moved-away. We need to retain the moved-to relpath of
* such nodes in order not to lose move information during replace. */
if (op_depth > 0)
{
for (i = children->nelts; i--; )
{
const char *name = APR_ARRAY_IDX(children, i, const char *);
svn_boolean_t have_row;
svn_pool_clear(iterpool);
SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
STMT_SELECT_WORKING_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id,
svn_relpath_join(local_relpath, name,
iterpool)));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row && !svn_sqlite__column_is_null(stmt, 14))
svn_hash_sets(moved_to_relpaths, name,
svn_sqlite__column_text(stmt, 14, scratch_pool));
SVN_ERR(svn_sqlite__reset(stmt));
}
}
SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_NODE));
for (i = children->nelts; i--; )
{
const char *name = APR_ARRAY_IDX(children, i, const char *);
svn_pool_clear(iterpool);
SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnrsnsnnnnnnnnnnsn",
wc_id,
svn_relpath_join(local_relpath, name,
iterpool),
op_depth,
local_relpath,
revision,
"incomplete", /* 8, presence */
"unknown", /* 10, kind */
/* 21, moved_to */
svn_hash_gets(moved_to_relpaths, name)));
if (repos_id != INVALID_REPOS_ID)
{
SVN_ERR(svn_sqlite__bind_int64(stmt, 5, repos_id));
SVN_ERR(svn_sqlite__bind_text(stmt, 6,
svn_relpath_join(repos_path, name,
iterpool)));
}
SVN_ERR(svn_sqlite__insert(NULL, stmt));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Insert the working row represented by (insert_working_baton_t *) BATON. */
static svn_error_t *
insert_working_node(const insert_working_baton_t *piwb,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *scratch_pool)
{
const char *parent_relpath;
const char *moved_to_relpath = NULL;
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
+ svn_boolean_t present;
SVN_ERR_ASSERT(piwb->op_depth > 0);
/* We cannot insert a WORKING_NODE row at the wcroot. */
SVN_ERR_ASSERT(*local_relpath != '\0');
parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
/* Preserve existing moved-to information for this relpath,
* which might exist in case we're replacing an existing base-deleted
* node. */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_TO));
SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
piwb->op_depth));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row)
moved_to_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
SVN_ERR(svn_sqlite__reset(stmt));
+ present = (piwb->presence == svn_wc__db_status_normal
+ || piwb->presence == svn_wc__db_status_incomplete);
+
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnntstrisn"
"nnnn" /* properties translated_size last_mod_time dav_cache */
"sns", /* symlink_target, file_external, moved_to */
wcroot->wc_id, local_relpath,
piwb->op_depth,
parent_relpath,
presence_map, piwb->presence,
- (piwb->kind == svn_node_dir)
+ (piwb->kind == svn_node_dir && present)
? svn_token__to_word(depth_map, piwb->depth) : NULL,
kind_map, piwb->kind,
piwb->changed_rev,
piwb->changed_date,
piwb->changed_author,
/* Note: incomplete nodes may have a NULL target. */
- (piwb->kind == svn_node_symlink)
+ (piwb->kind == svn_node_symlink && present)
? piwb->target : NULL,
moved_to_relpath));
if (piwb->moved_here)
{
SVN_ERR(svn_sqlite__bind_int(stmt, 8, TRUE));
}
- if (piwb->kind == svn_node_file)
+ if (piwb->kind == svn_node_file && present)
{
SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, piwb->checksum,
scratch_pool));
}
if (piwb->original_repos_relpath != NULL)
{
SVN_ERR(svn_sqlite__bind_int64(stmt, 5, piwb->original_repos_id));
SVN_ERR(svn_sqlite__bind_text(stmt, 6, piwb->original_repos_relpath));
SVN_ERR(svn_sqlite__bind_revnum(stmt, 7, piwb->original_revnum));
}
/* Set properties. Must be null if presence not normal or incomplete. */
assert(piwb->presence == svn_wc__db_status_normal
|| piwb->presence == svn_wc__db_status_incomplete
|| piwb->props == NULL);
- SVN_ERR(svn_sqlite__bind_properties(stmt, 15, piwb->props, scratch_pool));
+ if (present && piwb->original_repos_relpath)
+ SVN_ERR(svn_sqlite__bind_properties(stmt, 15, piwb->props, scratch_pool));
SVN_ERR(svn_sqlite__insert(NULL, stmt));
/* Insert incomplete children, if specified.
The children are part of the same op and so have the same op_depth.
(The only time we'd want a different depth is during a recursive
simple add, but we never insert children here during a simple add.) */
if (piwb->kind == svn_node_dir && piwb->children)
SVN_ERR(insert_incomplete_children(wcroot->sdb, wcroot->wc_id,
local_relpath,
INVALID_REPOS_ID /* inherit repos_id */,
NULL /* inherit repos_path */,
piwb->original_revnum,
piwb->children,
piwb->op_depth,
scratch_pool));
if (piwb->update_actual_props)
{
/* Cast away const, to allow calling property helpers */
apr_hash_t *base_props = (apr_hash_t *)piwb->props;
apr_hash_t *new_actual_props = (apr_hash_t *)piwb->new_actual_props;
if (base_props != NULL
&& new_actual_props != NULL
&& (apr_hash_count(base_props) == apr_hash_count(new_actual_props)))
{
apr_array_header_t *diffs;
SVN_ERR(svn_prop_diffs(&diffs, new_actual_props, base_props,
scratch_pool));
if (diffs->nelts == 0)
new_actual_props = NULL;
}
SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath, new_actual_props,
wcroot->sdb, scratch_pool));
}
if (piwb->kind == svn_node_dir)
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_UPDATE_ACTUAL_CLEAR_CHANGELIST));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_ACTUAL_EMPTY));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
}
if (piwb->not_present_op_depth > 0
&& piwb->not_present_op_depth < piwb->op_depth)
{
/* And also insert a not-present node to tell the commit processing that
a child of the parent node was not copied. */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_INSERT_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtnt",
wcroot->wc_id, local_relpath,
piwb->not_present_op_depth, parent_relpath,
piwb->original_repos_id,
piwb->original_repos_relpath,
piwb->original_revnum,
presence_map, svn_wc__db_status_not_present,
/* NULL */
kind_map, piwb->kind));
SVN_ERR(svn_sqlite__step_done(stmt));
}
SVN_ERR(add_work_items(wcroot->sdb, piwb->work_items, scratch_pool));
if (piwb->conflict)
SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
piwb->conflict, scratch_pool));
return SVN_NO_ERROR;
}
/* Each name is allocated in RESULT_POOL and stored into CHILDREN as a key
pointed to the same name. */
static svn_error_t *
add_children_to_hash(apr_hash_t *children,
int stmt_idx,
svn_sqlite__db_t *sdb,
apr_int64_t wc_id,
const char *parent_relpath,
apr_pool_t *result_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, stmt_idx));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, parent_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
while (have_row)
{
const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
const char *name = svn_relpath_basename(child_relpath, result_pool);
svn_hash_sets(children, name, name);
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
return svn_sqlite__reset(stmt);
}
/* Set *CHILDREN to a new array of the (const char *) basenames of the
immediate children, whatever their status, of the working node at
LOCAL_RELPATH. */
static svn_error_t *
gather_children2(const apr_array_header_t **children,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_hash_t *names_hash = apr_hash_make(scratch_pool);
apr_array_header_t *names_array;
/* All of the names get allocated in RESULT_POOL. It
appears to be faster to use the hash to remove duplicates than to
use DISTINCT in the SQL query. */
SVN_ERR(add_children_to_hash(names_hash, STMT_SELECT_WORKING_CHILDREN,
wcroot->sdb, wcroot->wc_id,
local_relpath, result_pool));
SVN_ERR(svn_hash_keys(&names_array, names_hash, result_pool));
*children = names_array;
return SVN_NO_ERROR;
}
/* Return in *CHILDREN all of the children of the directory LOCAL_RELPATH,
of any status, in all op-depths in the NODES table. */
static svn_error_t *
gather_children(const apr_array_header_t **children,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_hash_t *names_hash = apr_hash_make(scratch_pool);
apr_array_header_t *names_array;
/* All of the names get allocated in RESULT_POOL. It
appears to be faster to use the hash to remove duplicates than to
use DISTINCT in the SQL query. */
SVN_ERR(add_children_to_hash(names_hash, STMT_SELECT_NODE_CHILDREN,
wcroot->sdb, wcroot->wc_id,
local_relpath, result_pool));
SVN_ERR(svn_hash_keys(&names_array, names_hash, result_pool));
*children = names_array;
return SVN_NO_ERROR;
}
/* Set *CHILDREN to a new array of (const char *) names of the children of
the repository directory corresponding to WCROOT:LOCAL_RELPATH:OP_DEPTH -
that is, only the children that are at the same op-depth as their parent. */
static svn_error_t *
gather_repo_children(const apr_array_header_t **children,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
int op_depth,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_array_header_t *result
= apr_array_make(result_pool, 0, sizeof(const char *));
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_OP_DEPTH_CHILDREN));
SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
op_depth));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
while (have_row)
{
const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
/* Allocate the name in RESULT_POOL so we won't have to copy it. */
APR_ARRAY_PUSH(result, const char *)
= svn_relpath_basename(child_relpath, result_pool);
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
SVN_ERR(svn_sqlite__reset(stmt));
*children = result;
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_get_children_op_depth(apr_hash_t **children,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
int op_depth,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
*children = apr_hash_make(result_pool);
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_OP_DEPTH_CHILDREN));
SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
op_depth));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
while (have_row)
{
const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
svn_node_kind_t *child_kind = apr_palloc(result_pool, sizeof(svn_node_kind_t));
*child_kind = svn_sqlite__column_token(stmt, 1, kind_map);
svn_hash_sets(*children,
svn_relpath_basename(child_relpath, result_pool),
child_kind);
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
SVN_ERR(svn_sqlite__reset(stmt));
return SVN_NO_ERROR;
}
/* Return TRUE if CHILD_ABSPATH is an immediate child of PARENT_ABSPATH.
* Else, return FALSE. */
static svn_boolean_t
is_immediate_child_path(const char *parent_abspath, const char *child_abspath)
{
const char *local_relpath = svn_dirent_skip_ancestor(parent_abspath,
child_abspath);
/* To be an immediate child local_relpath should have one (not empty)
component */
return local_relpath && *local_relpath && !strchr(local_relpath, '/');
}
/* Remove the access baton for LOCAL_ABSPATH from ACCESS_CACHE. */
static void
remove_from_access_cache(apr_hash_t *access_cache,
const char *local_abspath)
{
svn_wc_adm_access_t *adm_access;
adm_access = svn_hash_gets(access_cache, local_abspath);
if (adm_access)
svn_wc__adm_access_set_entries(adm_access, NULL);
}
/* Flush the access baton for LOCAL_ABSPATH, and any of its children up to
* the specified DEPTH, from the access baton cache in WCROOT.
* Also flush the access baton for the parent of LOCAL_ABSPATH.I
*
* This function must be called when the access baton cache goes stale,
* i.e. data about LOCAL_ABSPATH will need to be read again from disk.
*
* Use SCRATCH_POOL for temporary allocations. */
static svn_error_t *
flush_entries(svn_wc__db_wcroot_t *wcroot,
const char *local_abspath,
svn_depth_t depth,
apr_pool_t *scratch_pool)
{
const char *parent_abspath;
if (apr_hash_count(wcroot->access_cache) == 0)
return SVN_NO_ERROR;
remove_from_access_cache(wcroot->access_cache, local_abspath);
if (depth > svn_depth_empty)
{
apr_hash_index_t *hi;
/* Flush access batons of children within the specified depth. */
for (hi = apr_hash_first(scratch_pool, wcroot->access_cache);
hi;
hi = apr_hash_next(hi))
{
const char *item_abspath = svn__apr_hash_index_key(hi);
if ((depth == svn_depth_files || depth == svn_depth_immediates) &&
is_immediate_child_path(local_abspath, item_abspath))
{
remove_from_access_cache(wcroot->access_cache, item_abspath);
}
else if (depth == svn_depth_infinity &&
svn_dirent_is_ancestor(local_abspath, item_abspath))
{
remove_from_access_cache(wcroot->access_cache, item_abspath);
}
}
}
/* We're going to be overly aggressive here and just flush the parent
without doing much checking. This may hurt performance for
legacy API consumers, but that's not our problem. :) */
parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
remove_from_access_cache(wcroot->access_cache, parent_abspath);
return SVN_NO_ERROR;
}
/* Add a single WORK_ITEM into the given SDB's WORK_QUEUE table. This does
not perform its work within a transaction, assuming the caller will
manage that. */
static svn_error_t *
add_single_work_item(svn_sqlite__db_t *sdb,
const svn_skel_t *work_item,
apr_pool_t *scratch_pool)
{
svn_stringbuf_t *serialized;
svn_sqlite__stmt_t *stmt;
serialized = svn_skel__unparse(work_item, scratch_pool);
SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_WORK_ITEM));
SVN_ERR(svn_sqlite__bind_blob(stmt, 1, serialized->data, serialized->len));
return svn_error_trace(svn_sqlite__insert(NULL, stmt));
}
/* Add work item(s) to the given SDB. Also see add_single_work_item(). This
SKEL is usually passed to the various wc_db operation functions. It may
be NULL, indicating no additional work items are needed, it may be a
single work item, or it may be a list of work items. */
static svn_error_t *
add_work_items(svn_sqlite__db_t *sdb,
const svn_skel_t *skel,
apr_pool_t *scratch_pool)
{
apr_pool_t *iterpool;
/* Maybe there are no work items to insert. */
if (skel == NULL)
return SVN_NO_ERROR;
/* Should have a list. */
SVN_ERR_ASSERT(!skel->is_atom);
/* Is the list a single work item? Or a list of work items? */
if (SVN_WC__SINGLE_WORK_ITEM(skel))
return svn_error_trace(add_single_work_item(sdb, skel, scratch_pool));
/* SKEL is a list-of-lists, aka list of work items. */
iterpool = svn_pool_create(scratch_pool);
for (skel = skel->children; skel; skel = skel->next)
{
svn_pool_clear(iterpool);
SVN_ERR(add_single_work_item(sdb, skel, iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Determine whether the node exists for a given WCROOT and LOCAL_RELPATH. */
static svn_error_t *
does_node_exist(svn_boolean_t *exists,
const svn_wc__db_wcroot_t *wcroot,
const char *local_relpath)
{
svn_sqlite__stmt_t *stmt;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DOES_NODE_EXIST));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(exists, stmt));
return svn_error_trace(svn_sqlite__reset(stmt));
}
svn_error_t *
svn_wc__db_install_schema_statistics(svn_sqlite__db_t *sdb,
apr_pool_t *scratch_pool)
{
SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_INSTALL_SCHEMA_STATISTICS));
return SVN_NO_ERROR;
}
/* Helper for create_db(). Initializes our wc.db schema.
*/
static svn_error_t *
init_db(/* output values */
apr_int64_t *repos_id,
apr_int64_t *wc_id,
/* input values */
svn_sqlite__db_t *db,
const char *repos_root_url,
const char *repos_uuid,
const char *root_node_repos_relpath,
svn_revnum_t root_node_revision,
svn_depth_t root_node_depth,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
/* Create the database's schema. */
SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_SCHEMA));
SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_NODES));
SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_NODES_TRIGGERS));
SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_EXTERNALS));
/* Insert the repository. */
SVN_ERR(create_repos_id(repos_id, repos_root_url, repos_uuid,
db, scratch_pool));
SVN_ERR(svn_wc__db_install_schema_statistics(db, scratch_pool));
/* Insert the wcroot. */
/* ### Right now, this just assumes wc metadata is being stored locally. */
SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_WCROOT));
SVN_ERR(svn_sqlite__insert(wc_id, stmt));
if (root_node_repos_relpath)
{
svn_wc__db_status_t status = svn_wc__db_status_normal;
if (root_node_revision > 0)
status = svn_wc__db_status_incomplete; /* Will be filled by update */
SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtst",
*wc_id, /* 1 */
"", /* 2 */
0, /* op_depth is 0 for base */
NULL, /* 4 */
*repos_id,
root_node_repos_relpath,
root_node_revision,
presence_map, status, /* 8 */
svn_token__to_word(depth_map,
root_node_depth),
kind_map, svn_node_dir /* 10 */));
SVN_ERR(svn_sqlite__insert(NULL, stmt));
}
return SVN_NO_ERROR;
}
/* Create an sqlite database at DIR_ABSPATH/SDB_FNAME and insert
records for REPOS_ID (using REPOS_ROOT_URL and REPOS_UUID) into
REPOSITORY and for WC_ID into WCROOT. Return the DB connection
in *SDB.
If ROOT_NODE_REPOS_RELPATH is not NULL, insert a BASE node at
the working copy root with repository relpath ROOT_NODE_REPOS_RELPATH,
revision ROOT_NODE_REVISION and depth ROOT_NODE_DEPTH.
*/
static svn_error_t *
create_db(svn_sqlite__db_t **sdb,
apr_int64_t *repos_id,
apr_int64_t *wc_id,
const char *dir_abspath,
const char *repos_root_url,
const char *repos_uuid,
const char *sdb_fname,
const char *root_node_repos_relpath,
svn_revnum_t root_node_revision,
svn_depth_t root_node_depth,
svn_boolean_t exclusive,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
SVN_ERR(svn_wc__db_util_open_db(sdb, dir_abspath, sdb_fname,
svn_sqlite__mode_rwcreate, exclusive,
NULL /* my_statements */,
result_pool, scratch_pool));
SVN_SQLITE__WITH_LOCK(init_db(repos_id, wc_id,
*sdb, repos_root_url, repos_uuid,
root_node_repos_relpath, root_node_revision,
root_node_depth, scratch_pool),
*sdb);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_init(svn_wc__db_t *db,
const char *local_abspath,
const char *repos_relpath,
const char *repos_root_url,
const char *repos_uuid,
svn_revnum_t initial_rev,
svn_depth_t depth,
apr_pool_t *scratch_pool)
{
svn_sqlite__db_t *sdb;
apr_int64_t repos_id;
apr_int64_t wc_id;
svn_wc__db_wcroot_t *wcroot;
svn_boolean_t sqlite_exclusive = FALSE;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR_ASSERT(repos_relpath != NULL);
SVN_ERR_ASSERT(depth == svn_depth_empty
|| depth == svn_depth_files
|| depth == svn_depth_immediates
|| depth == svn_depth_infinity);
/* ### REPOS_ROOT_URL and REPOS_UUID may be NULL. ... more doc: tbd */
SVN_ERR(svn_config_get_bool((svn_config_t *)db->config, &sqlite_exclusive,
SVN_CONFIG_SECTION_WORKING_COPY,
SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE,
FALSE));
/* Create the SDB and insert the basic rows. */
SVN_ERR(create_db(&sdb, &repos_id, &wc_id, local_abspath, repos_root_url,
repos_uuid, SDB_FILE,
repos_relpath, initial_rev, depth, sqlite_exclusive,
db->state_pool, scratch_pool));
/* Create the WCROOT for this directory. */
SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot,
apr_pstrdup(db->state_pool, local_abspath),
sdb, wc_id, FORMAT_FROM_SDB,
FALSE /* auto-upgrade */,
FALSE /* enforce_empty_wq */,
db->state_pool, scratch_pool));
/* The WCROOT is complete. Stash it into DB. */
svn_hash_sets(db->dir_data, wcroot->abspath, wcroot);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_to_relpath(const char **local_relpath,
svn_wc__db_t *db,
const char *wri_abspath,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &relpath, db,
wri_abspath, result_pool, scratch_pool));
/* This function is indirectly called from the upgrade code, so we
can't verify the wcroot here. Just check that it is not NULL */
CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
if (svn_dirent_is_ancestor(wcroot->abspath, local_abspath))
{
*local_relpath = apr_pstrdup(result_pool,
svn_dirent_skip_ancestor(wcroot->abspath,
local_abspath));
}
else
/* Probably moving from $TMP. Should we allow this? */
*local_relpath = apr_pstrdup(result_pool, local_abspath);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_from_relpath(const char **local_abspath,
svn_wc__db_t *db,
const char *wri_abspath,
const char *local_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *unused_relpath;
#if 0
SVN_ERR_ASSERT(svn_relpath_is_canonical(local_relpath));
#endif
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &unused_relpath, db,
wri_abspath, scratch_pool, scratch_pool));
/* This function is indirectly called from the upgrade code, so we
can't verify the wcroot here. Just check that it is not NULL */
CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
*local_abspath = svn_dirent_join(wcroot->abspath,
local_relpath,
result_pool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_get_wcroot(const char **wcroot_abspath,
svn_wc__db_t *db,
const char *wri_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *unused_relpath;
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &unused_relpath, db,
wri_abspath, scratch_pool, scratch_pool));
/* Can't use VERIFY_USABLE_WCROOT, as this should be usable to detect
where call upgrade */
CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
*wcroot_abspath = apr_pstrdup(result_pool, wcroot->abspath);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_base_add_directory(svn_wc__db_t *db,
const char *local_abspath,
const char *wri_abspath,
const char *repos_relpath,
const char *repos_root_url,
const char *repos_uuid,
svn_revnum_t revision,
const apr_hash_t *props,
svn_revnum_t changed_rev,
apr_time_t changed_date,
const char *changed_author,
const apr_array_header_t *children,
svn_depth_t depth,
apr_hash_t *dav_cache,
const svn_skel_t *conflict,
svn_boolean_t update_actual_props,
apr_hash_t *new_actual_props,
apr_array_header_t *new_iprops,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
insert_base_baton_t ibb;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR_ASSERT(repos_relpath != NULL);
SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
SVN_ERR_ASSERT(repos_uuid != NULL);
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
SVN_ERR_ASSERT(props != NULL);
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
#if 0
SVN_ERR_ASSERT(children != NULL);
#endif
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
wri_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
blank_ibb(&ibb);
/* Calculate repos_id in insert_base_node() to avoid extra transaction */
ibb.repos_root_url = repos_root_url;
ibb.repos_uuid = repos_uuid;
ibb.status = svn_wc__db_status_normal;
ibb.kind = svn_node_dir;
ibb.repos_relpath = repos_relpath;
ibb.revision = revision;
ibb.iprops = new_iprops;
ibb.props = props;
ibb.changed_rev = changed_rev;
ibb.changed_date = changed_date;
ibb.changed_author = changed_author;
ibb.children = children;
ibb.depth = depth;
ibb.dav_cache = dav_cache;
ibb.conflict = conflict;
ibb.work_items = work_items;
if (update_actual_props)
{
ibb.update_actual_props = TRUE;
ibb.new_actual_props = new_actual_props;
}
/* Insert the directory and all its children transactionally.
Note: old children can stick around, even if they are no longer present
in this directory's revision. */
SVN_WC__DB_WITH_TXN(
insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
wcroot);
SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_base_add_incomplete_directory(svn_wc__db_t *db,
const char *local_abspath,
const char *repos_relpath,
const char *repos_root_url,
const char *repos_uuid,
svn_revnum_t revision,
svn_depth_t depth,
svn_boolean_t insert_base_deleted,
svn_boolean_t delete_working,
svn_skel_t *conflict,
svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
struct insert_base_baton_t ibb;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
SVN_ERR_ASSERT(repos_relpath && repos_root_url && repos_uuid);
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
db, local_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
blank_ibb(&ibb);
/* Calculate repos_id in insert_base_node() to avoid extra transaction */
ibb.repos_root_url = repos_root_url;
ibb.repos_uuid = repos_uuid;
ibb.status = svn_wc__db_status_incomplete;
ibb.kind = svn_node_dir;
ibb.repos_relpath = repos_relpath;
ibb.revision = revision;
ibb.depth = depth;
ibb.insert_base_deleted = insert_base_deleted;
ibb.delete_working = delete_working;
ibb.conflict = conflict;
ibb.work_items = work_items;
SVN_WC__DB_WITH_TXN(
insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
wcroot);
SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_base_add_file(svn_wc__db_t *db,
const char *local_abspath,
const char *wri_abspath,
const char *repos_relpath,
const char *repos_root_url,
const char *repos_uuid,
svn_revnum_t revision,
const apr_hash_t *props,
svn_revnum_t changed_rev,
apr_time_t changed_date,
const char *changed_author,
const svn_checksum_t *checksum,
apr_hash_t *dav_cache,
svn_boolean_t delete_working,
svn_boolean_t update_actual_props,
apr_hash_t *new_actual_props,
apr_array_header_t *new_iprops,
svn_boolean_t keep_recorded_info,
svn_boolean_t insert_base_deleted,
const svn_skel_t *conflict,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
insert_base_baton_t ibb;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR_ASSERT(repos_relpath != NULL);
SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
SVN_ERR_ASSERT(repos_uuid != NULL);
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
SVN_ERR_ASSERT(props != NULL);
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
SVN_ERR_ASSERT(checksum != NULL);
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
wri_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
blank_ibb(&ibb);
/* Calculate repos_id in insert_base_node() to avoid extra transaction */
ibb.repos_root_url = repos_root_url;
ibb.repos_uuid = repos_uuid;
ibb.status = svn_wc__db_status_normal;
ibb.kind = svn_node_file;
ibb.repos_relpath = repos_relpath;
ibb.revision = revision;
ibb.props = props;
ibb.changed_rev = changed_rev;
ibb.changed_date = changed_date;
ibb.changed_author = changed_author;
ibb.checksum = checksum;
ibb.dav_cache = dav_cache;
ibb.iprops = new_iprops;
if (update_actual_props)
{
ibb.update_actual_props = TRUE;
ibb.new_actual_props = new_actual_props;
}
ibb.keep_recorded_info = keep_recorded_info;
ibb.insert_base_deleted = insert_base_deleted;
ibb.delete_working = delete_working;
ibb.conflict = conflict;
ibb.work_items = work_items;
SVN_WC__DB_WITH_TXN(
insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
wcroot);
/* If this used to be a directory we should remove children so pass
* depth infinity. */
SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_base_add_symlink(svn_wc__db_t *db,
const char *local_abspath,
const char *wri_abspath,
const char *repos_relpath,
const char *repos_root_url,
const char *repos_uuid,
svn_revnum_t revision,
const apr_hash_t *props,
svn_revnum_t changed_rev,
apr_time_t changed_date,
const char *changed_author,
const char *target,
apr_hash_t *dav_cache,
svn_boolean_t delete_working,
svn_boolean_t update_actual_props,
apr_hash_t *new_actual_props,
apr_array_header_t *new_iprops,
svn_boolean_t keep_recorded_info,
svn_boolean_t insert_base_deleted,
const svn_skel_t *conflict,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
insert_base_baton_t ibb;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR_ASSERT(repos_relpath != NULL);
SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
SVN_ERR_ASSERT(repos_uuid != NULL);
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
SVN_ERR_ASSERT(props != NULL);
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
SVN_ERR_ASSERT(target != NULL);
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
wri_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
blank_ibb(&ibb);
/* Calculate repos_id in insert_base_node() to avoid extra transaction */
ibb.repos_root_url = repos_root_url;
ibb.repos_uuid = repos_uuid;
ibb.status = svn_wc__db_status_normal;
ibb.kind = svn_node_symlink;
ibb.repos_relpath = repos_relpath;
ibb.revision = revision;
ibb.props = props;
ibb.changed_rev = changed_rev;
ibb.changed_date = changed_date;
ibb.changed_author = changed_author;
ibb.target = target;
ibb.dav_cache = dav_cache;
ibb.iprops = new_iprops;
if (update_actual_props)
{
ibb.update_actual_props = TRUE;
ibb.new_actual_props = new_actual_props;
}
ibb.keep_recorded_info = keep_recorded_info;
ibb.insert_base_deleted = insert_base_deleted;
ibb.delete_working = delete_working;
ibb.conflict = conflict;
ibb.work_items = work_items;
SVN_WC__DB_WITH_TXN(
insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
wcroot);
/* If this used to be a directory we should remove children so pass
* depth infinity. */
SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
scratch_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
add_excluded_or_not_present_node(svn_wc__db_t *db,
const char *local_abspath,
const char *repos_relpath,
const char *repos_root_url,
const char *repos_uuid,
svn_revnum_t revision,
svn_node_kind_t kind,
svn_wc__db_status_t status,
const svn_skel_t *conflict,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
insert_base_baton_t ibb;
const char *dir_abspath, *name;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR_ASSERT(repos_relpath != NULL);
SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
SVN_ERR_ASSERT(repos_uuid != NULL);
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
SVN_ERR_ASSERT(status == svn_wc__db_status_server_excluded
|| status == svn_wc__db_status_excluded
|| status == svn_wc__db_status_not_present);
/* These absent presence nodes are only useful below a parent node that is
present. To avoid problems with working copies obstructing the child
we calculate the wcroot and local_relpath of the parent and then add
our own relpath. */
svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
dir_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
blank_ibb(&ibb);
/* Calculate repos_id in insert_base_node() to avoid extra transaction */
ibb.repos_root_url = repos_root_url;
ibb.repos_uuid = repos_uuid;
ibb.status = status;
ibb.kind = kind;
ibb.repos_relpath = repos_relpath;
ibb.revision = revision;
/* Depending upon KIND, any of these might get used. */
ibb.children = NULL;
ibb.depth = svn_depth_unknown;
ibb.checksum = NULL;
ibb.target = NULL;
ibb.conflict = conflict;
ibb.work_items = work_items;
SVN_WC__DB_WITH_TXN(
insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
wcroot);
/* If this used to be a directory we should remove children so pass
* depth infinity. */
SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_base_add_excluded_node(svn_wc__db_t *db,
const char *local_abspath,
const char *repos_relpath,
const char *repos_root_url,
const char *repos_uuid,
svn_revnum_t revision,
svn_node_kind_t kind,
svn_wc__db_status_t status,
const svn_skel_t *conflict,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
SVN_ERR_ASSERT(status == svn_wc__db_status_server_excluded
|| status == svn_wc__db_status_excluded);
return add_excluded_or_not_present_node(
db, local_abspath, repos_relpath, repos_root_url, repos_uuid, revision,
kind, status, conflict, work_items, scratch_pool);
}
svn_error_t *
svn_wc__db_base_add_not_present_node(svn_wc__db_t *db,
const char *local_abspath,
const char *repos_relpath,
const char *repos_root_url,
const char *repos_uuid,
svn_revnum_t revision,
svn_node_kind_t kind,
const svn_skel_t *conflict,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
return add_excluded_or_not_present_node(
db, local_abspath, repos_relpath, repos_root_url, repos_uuid, revision,
kind, svn_wc__db_status_not_present, conflict, work_items, scratch_pool);
}
/* Recursively clear moved-here information at the copy-half of the move
* which moved the node at SRC_RELPATH away. This transforms the move into
* a simple copy. */
static svn_error_t *
clear_moved_here(const char *src_relpath,
svn_wc__db_wcroot_t *wcroot,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
const char *dst_relpath;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_TO));
SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
src_relpath, relpath_depth(src_relpath)));
SVN_ERR(svn_sqlite__step_row(stmt));
dst_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
SVN_ERR(svn_sqlite__reset(stmt));
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_CLEAR_MOVED_HERE_RECURSIVE));
SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
dst_relpath, relpath_depth(dst_relpath)));
SVN_ERR(svn_sqlite__step_done(stmt));
return SVN_NO_ERROR;
}
/* The body of svn_wc__db_base_remove().
*/
static svn_error_t *
db_base_remove(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
svn_wc__db_t *db, /* For checking conflicts */
svn_boolean_t keep_as_working,
svn_boolean_t queue_deletes,
svn_boolean_t remove_locks,
svn_revnum_t not_present_revision,
svn_skel_t *conflict,
svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
svn_wc__db_status_t status;
apr_int64_t repos_id;
const char *repos_relpath;
svn_node_kind_t kind;
svn_boolean_t keep_working;
SVN_ERR(svn_wc__db_base_get_info_internal(&status, &kind, NULL,
&repos_relpath, &repos_id,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
wcroot, local_relpath,
scratch_pool, scratch_pool));
if (remove_locks)
{
svn_sqlite__stmt_t *lock_stmt;
SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb,
STMT_DELETE_LOCK_RECURSIVELY));
SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath));
SVN_ERR(svn_sqlite__step_done(lock_stmt));
}
if (status == svn_wc__db_status_normal
&& keep_as_working)
{
SVN_ERR(svn_wc__db_op_make_copy(db,
svn_dirent_join(wcroot->abspath,
local_relpath,
scratch_pool),
NULL, NULL,
scratch_pool));
keep_working = TRUE;
}
else
{
/* Check if there is already a working node */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_WORKING_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&keep_working, stmt));
SVN_ERR(svn_sqlite__reset(stmt));
}
/* Step 1: Create workqueue operations to remove files and dirs in the
local-wc */
if (!keep_working
&& queue_deletes
&& (status == svn_wc__db_status_normal
|| status == svn_wc__db_status_incomplete))
{
svn_skel_t *work_item;
const char *local_abspath;
local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
scratch_pool);
if (kind == svn_node_dir)
{
apr_pool_t *iterpool;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_BASE_PRESENT));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
iterpool = svn_pool_create(scratch_pool);
SVN_ERR(svn_sqlite__step(&have_row, stmt));
while (have_row)
{
const char *node_relpath = svn_sqlite__column_text(stmt, 0, NULL);
svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 1,
kind_map);
const char *node_abspath;
svn_error_t *err;
svn_pool_clear(iterpool);
node_abspath = svn_dirent_join(wcroot->abspath, node_relpath,
iterpool);
if (node_kind == svn_node_dir)
err = svn_wc__wq_build_dir_remove(&work_item,
db, wcroot->abspath,
node_abspath, FALSE,
iterpool, iterpool);
else
err = svn_wc__wq_build_file_remove(&work_item,
db,
wcroot->abspath,
node_abspath,
iterpool, iterpool);
if (!err)
err = add_work_items(wcroot->sdb, work_item, iterpool);
if (err)
return svn_error_compose_create(err, svn_sqlite__reset(stmt));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
SVN_ERR(svn_sqlite__reset(stmt));
SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
db, wcroot->abspath,
local_abspath, FALSE,
scratch_pool, iterpool));
svn_pool_destroy(iterpool);
}
else
SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
db, wcroot->abspath,
local_abspath,
scratch_pool, scratch_pool));
SVN_ERR(add_work_items(wcroot->sdb, work_item, scratch_pool));
}
/* Step 2: Delete ACTUAL nodes */
if (! keep_working)
{
/* There won't be a record in NODE left for this node, so we want
to remove *all* ACTUAL nodes, including ACTUAL ONLY. */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_ACTUAL_NODE_RECURSIVE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
}
else if (! keep_as_working)
{
/* Delete only the ACTUAL nodes that apply to a delete of a BASE node */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_ACTUAL_FOR_BASE_RECURSIVE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
}
/* Else: Everything has been turned into a copy, so we want to keep all
ACTUAL_NODE records */
/* Step 3: Delete WORKING nodes */
if (conflict)
{
apr_pool_t *iterpool;
/*
* When deleting a conflicted node, moves of any moved-outside children
* of the node must be broken. Else, the destination will still be marked
* moved-here after the move source disappears from the working copy.
*
* ### FIXME: It would be nicer to have the conflict resolver
* break the move instead. It might also be a good idea to
* flag a tree conflict on each moved-away child. But doing so
* might introduce actual-only nodes without direct parents,
* and we're not yet sure if other existing code is prepared
* to handle such nodes. To be revisited post-1.8.
*
* ### In case of a conflict we are most likely creating WORKING nodes
* describing a copy of what was in BASE. The move information
* should be updated to describe a move from the WORKING layer.
* When stored that way the resolver of the tree conflict still has
* the knowledge of what was moved.
*/
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_MOVED_OUTSIDE));
SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
local_relpath,
relpath_depth(local_relpath)));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
iterpool = svn_pool_create(scratch_pool);
while (have_row)
{
const char *child_relpath;
svn_error_t *err;
svn_pool_clear(iterpool);
child_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
err = clear_moved_here(child_relpath, wcroot, iterpool);
if (err)
return svn_error_compose_create(err, svn_sqlite__reset(stmt));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
svn_pool_destroy(iterpool);
SVN_ERR(svn_sqlite__reset(stmt));
}
if (keep_working)
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_WORKING_BASE_DELETE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
}
else
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_WORKING_RECURSIVE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
}
/* Step 4: Delete the BASE node descendants */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_BASE_RECURSIVE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
/* Step 5: handle the BASE node itself */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_BASE_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
SVN_ERR(svn_wc__db_retract_parent_delete(wcroot, local_relpath, 0,
scratch_pool));
/* Step 6: Delete actual node if we don't keep working */
if (! keep_working)
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_ACTUAL_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
}
if (SVN_IS_VALID_REVNUM(not_present_revision))
{
struct insert_base_baton_t ibb;
blank_ibb(&ibb);
ibb.repos_id = repos_id;
ibb.status = svn_wc__db_status_not_present;
ibb.kind = kind;
ibb.repos_relpath = repos_relpath;
ibb.revision = not_present_revision;
/* Depending upon KIND, any of these might get used. */
ibb.children = NULL;
ibb.depth = svn_depth_unknown;
ibb.checksum = NULL;
ibb.target = NULL;
SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
}
SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
if (conflict)
SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
conflict, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_base_remove(svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t keep_as_working,
svn_boolean_t queue_deletes,
svn_boolean_t remove_locks,
svn_revnum_t not_present_revision,
svn_skel_t *conflict,
svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_WC__DB_WITH_TXN(db_base_remove(wcroot, local_relpath,
db, keep_as_working, queue_deletes,
remove_locks, not_present_revision,
conflict, work_items, scratch_pool),
wcroot);
/* If this used to be a directory we should remove children so pass
* depth infinity. */
SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_base_get_info_internal(svn_wc__db_status_t *status,
svn_node_kind_t *kind,
svn_revnum_t *revision,
const char **repos_relpath,
apr_int64_t *repos_id,
svn_revnum_t *changed_rev,
apr_time_t *changed_date,
const char **changed_author,
svn_depth_t *depth,
const svn_checksum_t **checksum,
const char **target,
svn_wc__db_lock_t **lock,
svn_boolean_t *had_props,
apr_hash_t **props,
svn_boolean_t *update_root,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
svn_error_t *err = SVN_NO_ERROR;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
lock ? STMT_SELECT_BASE_NODE_WITH_LOCK
: STMT_SELECT_BASE_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row)
{
svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
presence_map);
svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
if (kind)
{
*kind = node_kind;
}
if (status)
{
*status = node_status;
}
repos_location_from_columns(repos_id, revision, repos_relpath,
stmt, 0, 4, 1, result_pool);
SVN_ERR_ASSERT(!repos_id || *repos_id != INVALID_REPOS_ID);
SVN_ERR_ASSERT(!repos_relpath || *repos_relpath);
if (lock)
{
*lock = lock_from_columns(stmt, 15, 16, 17, 18, result_pool);
}
if (changed_rev)
{
*changed_rev = svn_sqlite__column_revnum(stmt, 7);
}
if (changed_date)
{
*changed_date = svn_sqlite__column_int64(stmt, 8);
}
if (changed_author)
{
/* Result may be NULL. */
*changed_author = svn_sqlite__column_text(stmt, 9, result_pool);
}
if (depth)
{
if (node_kind != svn_node_dir)
{
*depth = svn_depth_unknown;
}
else
{
*depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
svn_depth_unknown);
}
}
if (checksum)
{
if (node_kind != svn_node_file)
{
*checksum = NULL;
}
else
{
err = svn_sqlite__column_checksum(checksum, stmt, 5,
result_pool);
if (err != NULL)
err = svn_error_createf(
err->apr_err, err,
_("The node '%s' has a corrupt checksum value."),
path_for_error_message(wcroot, local_relpath,
scratch_pool));
}
}
if (target)
{
if (node_kind != svn_node_symlink)
*target = NULL;
else
*target = svn_sqlite__column_text(stmt, 11, result_pool);
}
if (had_props)
{
*had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 13);
}
if (props)
{
if (node_status == svn_wc__db_status_normal
|| node_status == svn_wc__db_status_incomplete)
{
SVN_ERR(svn_sqlite__column_properties(props, stmt, 13,
result_pool, scratch_pool));
if (*props == NULL)
*props = apr_hash_make(result_pool);
}
else
{
assert(svn_sqlite__column_is_null(stmt, 13));
*props = NULL;
}
}
if (update_root)
{
/* It's an update root iff it's a file external. */
*update_root = svn_sqlite__column_boolean(stmt, 14);
}
}
else
{
err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("The node '%s' was not found."),
path_for_error_message(wcroot, local_relpath,
scratch_pool));
}
/* Note: given the composition, no need to wrap for tracing. */
return svn_error_compose_create(err, svn_sqlite__reset(stmt));
}
svn_error_t *
svn_wc__db_base_get_info(svn_wc__db_status_t *status,
svn_node_kind_t *kind,
svn_revnum_t *revision,
const char **repos_relpath,
const char **repos_root_url,
const char **repos_uuid,
svn_revnum_t *changed_rev,
apr_time_t *changed_date,
const char **changed_author,
svn_depth_t *depth,
const svn_checksum_t **checksum,
const char **target,
svn_wc__db_lock_t **lock,
svn_boolean_t *had_props,
apr_hash_t **props,
svn_boolean_t *update_root,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
apr_int64_t repos_id;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
- SVN_ERR(svn_wc__db_base_get_info_internal(status, kind, revision,
+ SVN_WC__DB_WITH_TXN4(
+ svn_wc__db_base_get_info_internal(status, kind, revision,
repos_relpath, &repos_id,
changed_rev, changed_date,
changed_author, depth,
checksum, target, lock,
had_props, props, update_root,
wcroot, local_relpath,
- result_pool, scratch_pool));
+ result_pool, scratch_pool),
+ svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
+ wcroot->sdb, repos_id, result_pool),
+ SVN_NO_ERROR,
+ SVN_NO_ERROR,
+ wcroot);
SVN_ERR_ASSERT(repos_id != INVALID_REPOS_ID);
- SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
- wcroot->sdb, repos_id, result_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_base_get_children_info(apr_hash_t **nodes,
svn_wc__db_t *db,
const char *dir_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
dir_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
*nodes = apr_hash_make(result_pool);
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_BASE_CHILDREN_INFO));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
while (have_row)
{
struct svn_wc__db_base_info_t *info;
svn_error_t *err;
apr_int64_t repos_id;
const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
const char *name = svn_relpath_basename(child_relpath, result_pool);
info = apr_pcalloc(result_pool, sizeof(*info));
repos_id = svn_sqlite__column_int64(stmt, 1);
info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
info->status = svn_sqlite__column_token(stmt, 3, presence_map);
info->kind = svn_sqlite__column_token(stmt, 4, kind_map);
info->revnum = svn_sqlite__column_revnum(stmt, 5);
info->depth = svn_sqlite__column_token_null(stmt, 6, depth_map,
svn_depth_unknown);
info->update_root = svn_sqlite__column_boolean(stmt, 7);
info->lock = lock_from_columns(stmt, 8, 9, 10, 11, result_pool);
err = svn_wc__db_fetch_repos_info(&info->repos_root_url, NULL,
wcroot->sdb, repos_id, result_pool);
if (err)
return svn_error_trace(
svn_error_compose_create(err,
svn_sqlite__reset(stmt)));
svn_hash_sets(*nodes, name, info);
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
SVN_ERR(svn_sqlite__reset(stmt));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_base_get_props(apr_hash_t **props,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_status_t presence;
SVN_ERR(svn_wc__db_base_get_info(&presence, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, props, NULL,
db, local_abspath,
result_pool, scratch_pool));
if (presence != svn_wc__db_status_normal
&& presence != svn_wc__db_status_incomplete)
{
return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
_("The node '%s' has a BASE status that"
" has no properties."),
svn_dirent_local_style(local_abspath,
scratch_pool));
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_base_get_children(const apr_array_header_t **children,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
return gather_repo_children(children, wcroot, local_relpath, 0,
result_pool, scratch_pool);
}
svn_error_t *
svn_wc__db_base_set_dav_cache(svn_wc__db_t *db,
const char *local_abspath,
const apr_hash_t *props,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
int affected_rows;
SVN_ERR(get_statement_for_path(&stmt, db, local_abspath,
STMT_UPDATE_BASE_NODE_DAV_CACHE,
scratch_pool));
SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
if (affected_rows != 1)
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("The node '%s' was not found."),
svn_dirent_local_style(local_abspath,
scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_base_get_dav_cache(apr_hash_t **props,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
SVN_ERR(get_statement_for_path(&stmt, db, local_abspath,
STMT_SELECT_BASE_DAV_CACHE, scratch_pool));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (!have_row)
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
svn_sqlite__reset(stmt),
_("The node '%s' was not found."),
svn_dirent_local_style(local_abspath,
scratch_pool));
SVN_ERR(svn_sqlite__column_properties(props, stmt, 0, result_pool,
scratch_pool));
return svn_error_trace(svn_sqlite__reset(stmt));
}
svn_error_t *
svn_wc__db_base_clear_dav_cache_recursive(svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
svn_sqlite__stmt_t *stmt;
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
db, local_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_CLEAR_BASE_NODE_RECURSIVE_DAV_CACHE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_depth_get_info(svn_wc__db_status_t *status,
svn_node_kind_t *kind,
svn_revnum_t *revision,
const char **repos_relpath,
apr_int64_t *repos_id,
svn_revnum_t *changed_rev,
apr_time_t *changed_date,
const char **changed_author,
svn_depth_t *depth,
const svn_checksum_t **checksum,
const char **target,
svn_boolean_t *had_props,
apr_hash_t **props,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
int op_depth,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
svn_error_t *err = SVN_NO_ERROR;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_DEPTH_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "isd",
wcroot->wc_id, local_relpath, op_depth));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row)
{
svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
presence_map);
svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
if (kind)
{
*kind = node_kind;
}
if (status)
{
*status = node_status;
if (op_depth > 0)
SVN_ERR(convert_to_working_status(status, *status));
}
repos_location_from_columns(repos_id, revision, repos_relpath,
stmt, 0, 4, 1, result_pool);
if (changed_rev)
{
*changed_rev = svn_sqlite__column_revnum(stmt, 7);
}
if (changed_date)
{
*changed_date = svn_sqlite__column_int64(stmt, 8);
}
if (changed_author)
{
/* Result may be NULL. */
*changed_author = svn_sqlite__column_text(stmt, 9, result_pool);
}
if (depth)
{
if (node_kind != svn_node_dir)
{
*depth = svn_depth_unknown;
}
else
{
*depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
svn_depth_unknown);
}
}
if (checksum)
{
if (node_kind != svn_node_file)
{
*checksum = NULL;
}
else
{
err = svn_sqlite__column_checksum(checksum, stmt, 5,
result_pool);
if (err != NULL)
err = svn_error_createf(
err->apr_err, err,
_("The node '%s' has a corrupt checksum value."),
path_for_error_message(wcroot, local_relpath,
scratch_pool));
}
}
if (target)
{
if (node_kind != svn_node_symlink)
*target = NULL;
else
*target = svn_sqlite__column_text(stmt, 11, result_pool);
}
if (had_props)
{
*had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 13);
}
if (props)
{
if (node_status == svn_wc__db_status_normal
|| node_status == svn_wc__db_status_incomplete)
{
SVN_ERR(svn_sqlite__column_properties(props, stmt, 13,
result_pool, scratch_pool));
if (*props == NULL)
*props = apr_hash_make(result_pool);
}
else
{
assert(svn_sqlite__column_is_null(stmt, 13));
*props = NULL;
}
}
}
else
{
err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("The node '%s' was not found."),
path_for_error_message(wcroot, local_relpath,
scratch_pool));
}
/* Note: given the composition, no need to wrap for tracing. */
return svn_error_compose_create(err, svn_sqlite__reset(stmt));
}
/* Baton for passing args to with_triggers(). */
struct with_triggers_baton_t {
int create_trigger;
int drop_trigger;
svn_wc__db_txn_callback_t cb_func;
void *cb_baton;
};
/* Helper for creating SQLite triggers, running the main transaction
callback, and then dropping the triggers. It guarantees that the
triggers will not survive the transaction. This could be used for
any general prefix/postscript statements where the postscript
*must* be executed if the transaction completes.
Implements svn_wc__db_txn_callback_t. */
static svn_error_t *
with_triggers(void *baton,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *scratch_pool)
{
struct with_triggers_baton_t *b = baton;
svn_error_t *err1;
svn_error_t *err2;
SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, b->create_trigger));
err1 = b->cb_func(b->cb_baton, wcroot, local_relpath, scratch_pool);
err2 = svn_sqlite__exec_statements(wcroot->sdb, b->drop_trigger);
return svn_error_trace(svn_error_compose_create(err1, err2));
}
/* Prototype for the "work callback" used by with_finalization(). */
typedef svn_error_t * (*work_callback_t)(
void *baton,
svn_wc__db_wcroot_t *wcroot,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool);
/* Utility function to provide several features, with a guaranteed
finalization (ie. to drop temporary tables).
1) for WCROOT and LOCAL_RELPATH, run TXN_CB(TXN_BATON) within a
sqlite transaction
2) if (1) is successful and a NOTIFY_FUNC is provided, then run
the "work" step: WORK_CB(WORK_BATON).
3) execute FINALIZE_STMT_IDX no matter what errors may be thrown
from the above two steps.
CANCEL_FUNC, CANCEL_BATON, NOTIFY_FUNC and NOTIFY_BATON are their
typical values. These are passed to the work callback, which typically
provides notification about the work done by TXN_CB. */
static svn_error_t *
with_finalization(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
svn_wc__db_txn_callback_t txn_cb,
void *txn_baton,
work_callback_t work_cb,
void *work_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
int finalize_stmt_idx,
apr_pool_t *scratch_pool)
{
svn_error_t *err1;
svn_error_t *err2;
err1 = svn_wc__db_with_txn(wcroot, local_relpath, txn_cb, txn_baton,
scratch_pool);
if (err1 == NULL && notify_func != NULL)
{
err2 = work_cb(work_baton, wcroot,
cancel_func, cancel_baton,
notify_func, notify_baton,
scratch_pool);
err1 = svn_error_compose_create(err1, err2);
}
err2 = svn_sqlite__exec_statements(wcroot->sdb, finalize_stmt_idx);
return svn_error_trace(svn_error_compose_create(err1, err2));
}
/* Initialize the baton with appropriate "blank" values. This allows the
insertion function to leave certain columns null. */
static void
blank_ieb(insert_external_baton_t *ieb)
{
memset(ieb, 0, sizeof(*ieb));
ieb->revision = SVN_INVALID_REVNUM;
ieb->changed_rev = SVN_INVALID_REVNUM;
ieb->repos_id = INVALID_REPOS_ID;
ieb->recorded_peg_revision = SVN_INVALID_REVNUM;
ieb->recorded_revision = SVN_INVALID_REVNUM;
}
/* Insert the externals row represented by (insert_external_baton_t *) BATON.
*
* Implements svn_wc__db_txn_callback_t. */
static svn_error_t *
insert_external_node(const insert_external_baton_t *ieb,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *scratch_pool)
{
svn_wc__db_status_t status;
svn_error_t *err;
svn_boolean_t update_root;
apr_int64_t repos_id;
svn_sqlite__stmt_t *stmt;
if (ieb->repos_id != INVALID_REPOS_ID)
repos_id = ieb->repos_id;
else
SVN_ERR(create_repos_id(&repos_id, ieb->repos_root_url, ieb->repos_uuid,
wcroot->sdb, scratch_pool));
/* And there must be no existing BASE node or it must be a file external */
err = svn_wc__db_base_get_info_internal(&status, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, &update_root,
wcroot, local_relpath,
scratch_pool, scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
svn_error_clear(err);
}
else if (status == svn_wc__db_status_normal && !update_root)
return svn_error_create(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, NULL);
if (ieb->kind == svn_node_file
|| ieb->kind == svn_node_symlink)
{
struct insert_base_baton_t ibb;
blank_ibb(&ibb);
ibb.status = svn_wc__db_status_normal;
ibb.kind = ieb->kind;
ibb.repos_id = repos_id;
ibb.repos_relpath = ieb->repos_relpath;
ibb.revision = ieb->revision;
ibb.props = ieb->props;
ibb.iprops = ieb->iprops;
ibb.changed_rev = ieb->changed_rev;
ibb.changed_date = ieb->changed_date;
ibb.changed_author = ieb->changed_author;
ibb.dav_cache = ieb->dav_cache;
ibb.checksum = ieb->checksum;
ibb.target = ieb->target;
ibb.conflict = ieb->conflict;
ibb.update_actual_props = ieb->update_actual_props;
ibb.new_actual_props = ieb->new_actual_props;
ibb.keep_recorded_info = ieb->keep_recorded_info;
ibb.work_items = ieb->work_items;
ibb.file_external = TRUE;
SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
}
else
SVN_ERR(add_work_items(wcroot->sdb, ieb->work_items, scratch_pool));
/* The externals table only support presence normal and excluded */
SVN_ERR_ASSERT(ieb->presence == svn_wc__db_status_normal
|| ieb->presence == svn_wc__db_status_excluded);
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_EXTERNAL));
SVN_ERR(svn_sqlite__bindf(stmt, "issttsis",
wcroot->wc_id,
local_relpath,
svn_relpath_dirname(local_relpath,
scratch_pool),
presence_map, ieb->presence,
kind_map, ieb->kind,
ieb->record_ancestor_relpath,
repos_id,
ieb->recorded_repos_relpath));
if (SVN_IS_VALID_REVNUM(ieb->recorded_peg_revision))
SVN_ERR(svn_sqlite__bind_revnum(stmt, 9, ieb->recorded_peg_revision));
if (SVN_IS_VALID_REVNUM(ieb->recorded_revision))
SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, ieb->recorded_revision));
SVN_ERR(svn_sqlite__insert(NULL, stmt));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_external_add_file(svn_wc__db_t *db,
const char *local_abspath,
const char *wri_abspath,
const char *repos_relpath,
const char *repos_root_url,
const char *repos_uuid,
svn_revnum_t revision,
const apr_hash_t *props,
apr_array_header_t *iprops,
svn_revnum_t changed_rev,
apr_time_t changed_date,
const char *changed_author,
const svn_checksum_t *checksum,
const apr_hash_t *dav_cache,
const char *record_ancestor_abspath,
const char *recorded_repos_relpath,
svn_revnum_t recorded_peg_revision,
svn_revnum_t recorded_revision,
svn_boolean_t update_actual_props,
apr_hash_t *new_actual_props,
svn_boolean_t keep_recorded_info,
const svn_skel_t *conflict,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
insert_external_baton_t ieb;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
if (! wri_abspath)
wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
wri_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
record_ancestor_abspath));
SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
blank_ieb(&ieb);
ieb.kind = svn_node_file;
ieb.presence = svn_wc__db_status_normal;
ieb.repos_root_url = repos_root_url;
ieb.repos_uuid = repos_uuid;
ieb.repos_relpath = repos_relpath;
ieb.revision = revision;
ieb.props = props;
ieb.iprops = iprops;
ieb.changed_rev = changed_rev;
ieb.changed_date = changed_date;
ieb.changed_author = changed_author;
ieb.checksum = checksum;
ieb.dav_cache = dav_cache;
ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
wcroot->abspath,
record_ancestor_abspath);
ieb.recorded_repos_relpath = recorded_repos_relpath;
ieb.recorded_peg_revision = recorded_peg_revision;
ieb.recorded_revision = recorded_revision;
ieb.update_actual_props = update_actual_props;
ieb.new_actual_props = new_actual_props;
ieb.keep_recorded_info = keep_recorded_info;
ieb.conflict = conflict;
ieb.work_items = work_items;
SVN_WC__DB_WITH_TXN(
insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
wcroot);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_external_add_symlink(svn_wc__db_t *db,
const char *local_abspath,
const char *wri_abspath,
const char *repos_relpath,
const char *repos_root_url,
const char *repos_uuid,
svn_revnum_t revision,
const apr_hash_t *props,
svn_revnum_t changed_rev,
apr_time_t changed_date,
const char *changed_author,
const char *target,
const apr_hash_t *dav_cache,
const char *record_ancestor_abspath,
const char *recorded_repos_relpath,
svn_revnum_t recorded_peg_revision,
svn_revnum_t recorded_revision,
svn_boolean_t update_actual_props,
apr_hash_t *new_actual_props,
svn_boolean_t keep_recorded_info,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
insert_external_baton_t ieb;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
if (! wri_abspath)
wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
wri_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
record_ancestor_abspath));
SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
blank_ieb(&ieb);
ieb.kind = svn_node_symlink;
ieb.presence = svn_wc__db_status_normal;
ieb.repos_root_url = repos_root_url;
ieb.repos_uuid = repos_uuid;
ieb.repos_relpath = repos_relpath;
ieb.revision = revision;
ieb.props = props;
ieb.changed_rev = changed_rev;
ieb.changed_date = changed_date;
ieb.changed_author = changed_author;
ieb.target = target;
ieb.dav_cache = dav_cache;
ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
wcroot->abspath,
record_ancestor_abspath);
ieb.recorded_repos_relpath = recorded_repos_relpath;
ieb.recorded_peg_revision = recorded_peg_revision;
ieb.recorded_revision = recorded_revision;
ieb.update_actual_props = update_actual_props;
ieb.new_actual_props = new_actual_props;
ieb.keep_recorded_info = keep_recorded_info;
ieb.work_items = work_items;
SVN_WC__DB_WITH_TXN(
insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
wcroot);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_external_add_dir(svn_wc__db_t *db,
const char *local_abspath,
const char *wri_abspath,
const char *repos_root_url,
const char *repos_uuid,
const char *record_ancestor_abspath,
const char *recorded_repos_relpath,
svn_revnum_t recorded_peg_revision,
svn_revnum_t recorded_revision,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
insert_external_baton_t ieb;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
if (! wri_abspath)
wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
wri_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
record_ancestor_abspath));
SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
blank_ieb(&ieb);
ieb.kind = svn_node_dir;
ieb.presence = svn_wc__db_status_normal;
ieb.repos_root_url = repos_root_url;
ieb.repos_uuid = repos_uuid;
ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
wcroot->abspath,
record_ancestor_abspath);
ieb.recorded_repos_relpath = recorded_repos_relpath;
ieb.recorded_peg_revision = recorded_peg_revision;
ieb.recorded_revision = recorded_revision;
ieb.work_items = work_items;
SVN_WC__DB_WITH_TXN(
insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
wcroot);
return SVN_NO_ERROR;
}
/* The body of svn_wc__db_external_remove(). */
static svn_error_t *
db_external_remove(const svn_skel_t *work_items,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_EXTERNAL));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
/* ### What about actual? */
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_external_remove(svn_wc__db_t *db,
const char *local_abspath,
const char *wri_abspath,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
if (! wri_abspath)
wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
wri_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
SVN_WC__DB_WITH_TXN(db_external_remove(work_items, wcroot, local_relpath,
scratch_pool),
wcroot);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_external_read(svn_wc__db_status_t *status,
svn_node_kind_t *kind,
const char **definining_abspath,
const char **repos_root_url,
const char **repos_uuid,
const char **recorded_repos_relpath,
svn_revnum_t *recorded_peg_revision,
svn_revnum_t *recorded_revision,
svn_wc__db_t *db,
const char *local_abspath,
const char *wri_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_info;
svn_error_t *err = NULL;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
if (! wri_abspath)
wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
wri_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_EXTERNAL_INFO));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_info, stmt));
if (have_info)
{
if (status)
*status = svn_sqlite__column_token(stmt, 0, presence_map);
if (kind)
*kind = svn_sqlite__column_token(stmt, 1, kind_map);
if (definining_abspath)
{
const char *record_relpath = svn_sqlite__column_text(stmt, 2, NULL);
*definining_abspath = svn_dirent_join(wcroot->abspath,
record_relpath, result_pool);
}
if (repos_root_url || repos_uuid)
{
apr_int64_t repos_id;
repos_id = svn_sqlite__column_int64(stmt, 3);
err = svn_error_compose_create(
err,
svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
wcroot->sdb, repos_id,
result_pool));
}
if (recorded_repos_relpath)
*recorded_repos_relpath = svn_sqlite__column_text(stmt, 4,
result_pool);
if (recorded_peg_revision)
*recorded_peg_revision = svn_sqlite__column_revnum(stmt, 5);
if (recorded_revision)
*recorded_revision = svn_sqlite__column_revnum(stmt, 6);
}
else
{
err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("The node '%s' is not an external."),
svn_dirent_local_style(local_abspath,
scratch_pool));
}
return svn_error_trace(
svn_error_compose_create(err, svn_sqlite__reset(stmt)));
}
svn_error_t *
svn_wc__db_committable_externals_below(apr_array_header_t **externals,
svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t immediates_only,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
svn_sqlite__stmt_t *stmt;
const char *local_relpath;
svn_boolean_t have_row;
svn_wc__committable_external_info_t *info;
svn_node_kind_t db_kind;
apr_array_header_t *result = NULL;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_ERR(svn_sqlite__get_statement(
&stmt, wcroot->sdb,
immediates_only
? STMT_SELECT_COMMITTABLE_EXTERNALS_IMMEDIATELY_BELOW
: STMT_SELECT_COMMITTABLE_EXTERNALS_BELOW));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row)
result = apr_array_make(result_pool, 0,
sizeof(svn_wc__committable_external_info_t *));
while (have_row)
{
info = apr_palloc(result_pool, sizeof(*info));
local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
info->local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
result_pool);
db_kind = svn_sqlite__column_token(stmt, 1, kind_map);
SVN_ERR_ASSERT(db_kind == svn_node_file || db_kind == svn_node_dir);
info->kind = db_kind;
info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
info->repos_root_url = svn_sqlite__column_text(stmt, 3, result_pool);
APR_ARRAY_PUSH(result, svn_wc__committable_external_info_t *) = info;
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
*externals = result;
return svn_error_trace(svn_sqlite__reset(stmt));
}
svn_error_t *
svn_wc__db_externals_defined_below(apr_hash_t **externals,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
svn_sqlite__stmt_t *stmt;
const char *local_relpath;
svn_boolean_t have_row;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_EXTERNALS_DEFINED));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
*externals = apr_hash_make(result_pool);
SVN_ERR(svn_sqlite__step(&have_row, stmt));
while (have_row)
{
const char *def_local_relpath;
local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
def_local_relpath = svn_sqlite__column_text(stmt, 1, NULL);
svn_hash_sets(*externals,
svn_dirent_join(wcroot->abspath, local_relpath,
result_pool),
svn_dirent_join(wcroot->abspath, def_local_relpath,
result_pool));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
return svn_error_trace(svn_sqlite__reset(stmt));
}
svn_error_t *
svn_wc__db_externals_gather_definitions(apr_hash_t **externals,
apr_hash_t **depths,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
svn_sqlite__stmt_t *stmt;
const char *local_relpath;
svn_boolean_t have_row;
svn_error_t *err = NULL;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, iterpool));
VERIFY_USABLE_WCROOT(wcroot);
*externals = apr_hash_make(result_pool);
if (depths != NULL)
*depths = apr_hash_make(result_pool);
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_EXTERNAL_PROPERTIES));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
while (have_row)
{
apr_hash_t *node_props;
const char *external_value;
svn_pool_clear(iterpool);
err = svn_sqlite__column_properties(&node_props, stmt, 0, iterpool,
iterpool);
if (err)
break;
external_value = svn_prop_get_value(node_props, SVN_PROP_EXTERNALS);
if (external_value)
{
const char *node_abspath;
const char *node_relpath = svn_sqlite__column_text(stmt, 1, NULL);
node_abspath = svn_dirent_join(wcroot->abspath, node_relpath,
result_pool);
svn_hash_sets(*externals, node_abspath,
apr_pstrdup(result_pool, external_value));
if (depths)
{
svn_depth_t depth
= svn_sqlite__column_token_null(stmt, 2, depth_map,
svn_depth_unknown);
svn_hash_sets(*depths, node_abspath,
/* Use static string */
svn_token__to_word(depth_map, depth));
}
}
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
svn_pool_destroy(iterpool);
return svn_error_trace(svn_error_compose_create(err,
svn_sqlite__reset(stmt)));
}
/* Copy the ACTUAL data for SRC_RELPATH and tweak it to refer to DST_RELPATH.
The new ACTUAL data won't have any conflicts. */
static svn_error_t *
copy_actual(svn_wc__db_wcroot_t *src_wcroot,
const char *src_relpath,
svn_wc__db_wcroot_t *dst_wcroot,
const char *dst_relpath,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
STMT_SELECT_ACTUAL_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", src_wcroot->wc_id, src_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row)
{
apr_size_t props_size;
const char *changelist;
const char *properties;
/* Skipping conflict data... */
changelist = svn_sqlite__column_text(stmt, 0, scratch_pool);
/* No need to parse the properties when simply copying. */
properties = svn_sqlite__column_blob(stmt, 1, &props_size, scratch_pool);
if (changelist || properties)
{
SVN_ERR(svn_sqlite__reset(stmt));
SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
STMT_INSERT_ACTUAL_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "issbs",
dst_wcroot->wc_id, dst_relpath,
svn_relpath_dirname(dst_relpath, scratch_pool),
properties, props_size, changelist));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
}
SVN_ERR(svn_sqlite__reset(stmt));
return SVN_NO_ERROR;
}
/* Helper for svn_wc__db_op_copy to handle copying from one db to
another */
static svn_error_t *
cross_db_copy(svn_wc__db_wcroot_t *src_wcroot,
const char *src_relpath,
svn_wc__db_wcroot_t *dst_wcroot,
const char *dst_relpath,
svn_wc__db_status_t dst_status,
int dst_op_depth,
int dst_np_op_depth,
svn_node_kind_t kind,
const apr_array_header_t *children,
apr_int64_t copyfrom_id,
const char *copyfrom_relpath,
svn_revnum_t copyfrom_rev,
apr_pool_t *scratch_pool)
{
insert_working_baton_t iwb;
svn_revnum_t changed_rev;
apr_time_t changed_date;
const char *changed_author;
const svn_checksum_t *checksum;
apr_hash_t *props;
svn_depth_t depth;
SVN_ERR_ASSERT(kind == svn_node_file
|| kind == svn_node_dir
);
SVN_ERR(read_info(NULL, NULL, NULL, NULL, NULL,
&changed_rev, &changed_date, &changed_author, &depth,
&checksum, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
src_wcroot, src_relpath, scratch_pool, scratch_pool));
if (dst_status != svn_wc__db_status_not_present
&& dst_status != svn_wc__db_status_excluded
&& dst_status != svn_wc__db_status_server_excluded)
{
SVN_ERR(db_read_pristine_props(&props, src_wcroot, src_relpath, FALSE,
scratch_pool, scratch_pool));
}
else
props = NULL;
blank_iwb(&iwb);
iwb.presence = dst_status;
iwb.kind = kind;
iwb.props = props;
iwb.changed_rev = changed_rev;
iwb.changed_date = changed_date;
iwb.changed_author = changed_author;
iwb.original_repos_id = copyfrom_id;
iwb.original_repos_relpath = copyfrom_relpath;
iwb.original_revnum = copyfrom_rev;
iwb.moved_here = FALSE;
iwb.op_depth = dst_op_depth;
iwb.checksum = checksum;
iwb.children = children;
iwb.depth = depth;
iwb.not_present_op_depth = dst_np_op_depth;
SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath, scratch_pool));
SVN_ERR(copy_actual(src_wcroot, src_relpath,
dst_wcroot, dst_relpath, scratch_pool));
return SVN_NO_ERROR;
}
/* Helper for scan_deletion_txn. Extracts the moved-to information, if
any, from STMT. Sets *SCAN to FALSE if moved-to was available. */
static svn_error_t *
get_moved_to(const char **moved_to_relpath_p,
const char **moved_to_op_root_relpath_p,
svn_boolean_t *scan,
svn_sqlite__stmt_t *stmt,
const char *current_relpath,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *moved_to_relpath = svn_sqlite__column_text(stmt, 3, NULL);
if (moved_to_relpath)
{
const char *moved_to_op_root_relpath = moved_to_relpath;
if (strcmp(current_relpath, local_relpath))
{
/* LOCAL_RELPATH is a child inside the move op-root. */
const char *moved_child_relpath;
/* The CURRENT_RELPATH is the op_root of the delete-half of
* the move. LOCAL_RELPATH is a child that was moved along.
* Compute the child's new location within the move target. */
moved_child_relpath = svn_relpath_skip_ancestor(current_relpath,
local_relpath);
SVN_ERR_ASSERT(moved_child_relpath &&
strlen(moved_child_relpath) > 0);
moved_to_relpath = svn_relpath_join(moved_to_op_root_relpath,
moved_child_relpath,
result_pool);
}
if (moved_to_op_root_relpath && moved_to_op_root_relpath_p)
*moved_to_op_root_relpath_p
= apr_pstrdup(result_pool, moved_to_op_root_relpath);
if (moved_to_relpath && moved_to_relpath_p)
*moved_to_relpath_p
= apr_pstrdup(result_pool, moved_to_relpath);
*scan = FALSE;
}
return SVN_NO_ERROR;
}
/* The body of svn_wc__db_scan_deletion().
*/
static svn_error_t *
scan_deletion_txn(const char **base_del_relpath,
const char **moved_to_relpath,
const char **work_del_relpath,
const char **moved_to_op_root_relpath,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *current_relpath = local_relpath;
svn_sqlite__stmt_t *stmt;
svn_wc__db_status_t work_presence;
svn_boolean_t have_row, scan, have_base;
int op_depth;
/* Initialize all the OUT parameters. */
if (base_del_relpath != NULL)
*base_del_relpath = NULL;
if (moved_to_relpath != NULL)
*moved_to_relpath = NULL;
if (work_del_relpath != NULL)
*work_del_relpath = NULL;
if (moved_to_op_root_relpath != NULL)
*moved_to_op_root_relpath = NULL;
/* If looking for moved-to info then we need to scan every path
until we find it. If not looking for moved-to we only need to
check op-roots and parents of op-roots. */
scan = (moved_to_op_root_relpath || moved_to_relpath);
SVN_ERR(svn_sqlite__get_statement(
&stmt, wcroot->sdb,
scan ? STMT_SELECT_DELETION_INFO_SCAN
: STMT_SELECT_DELETION_INFO));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, current_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (!have_row)
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, svn_sqlite__reset(stmt),
_("The node '%s' was not found."),
path_for_error_message(wcroot, local_relpath,
scratch_pool));
work_presence = svn_sqlite__column_token(stmt, 1, presence_map);
have_base = !svn_sqlite__column_is_null(stmt, 0);
if (work_presence != svn_wc__db_status_not_present
&& work_presence != svn_wc__db_status_base_deleted)
return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
svn_sqlite__reset(stmt),
_("Expected node '%s' to be deleted."),
path_for_error_message(wcroot, local_relpath,
scratch_pool));
op_depth = svn_sqlite__column_int(stmt, 2);
/* Special case: LOCAL_RELPATH not-present within a WORKING tree, we
treat this as an op-root. At commit time we need to explicitly
delete such nodes otherwise they will be present in the
repository copy. */
if (work_presence == svn_wc__db_status_not_present
&& work_del_relpath && !*work_del_relpath)
{
*work_del_relpath = apr_pstrdup(result_pool, current_relpath);
if (!scan && !base_del_relpath)
{
/* We have all we need, exit early */
SVN_ERR(svn_sqlite__reset(stmt));
return SVN_NO_ERROR;
}
}
while (TRUE)
{
svn_error_t *err;
const char *parent_relpath;
int current_depth = relpath_depth(current_relpath);
/* Step CURRENT_RELPATH to op-root */
while (TRUE)
{
if (scan)
{
err = get_moved_to(moved_to_relpath, moved_to_op_root_relpath,
&scan, stmt, current_relpath,
wcroot, local_relpath,
result_pool, scratch_pool);
if (err || (!scan
&& !base_del_relpath
&& !work_del_relpath))
{
/* We have all we need (or an error occurred) */
SVN_ERR(svn_sqlite__reset(stmt));
return svn_error_trace(err);
}
}
if (current_depth <= op_depth)
break;
current_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
--current_depth;
if (scan || current_depth == op_depth)
{
SVN_ERR(svn_sqlite__reset(stmt));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
current_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
SVN_ERR_ASSERT(have_row);
have_base = !svn_sqlite__column_is_null(stmt, 0);
}
}
SVN_ERR(svn_sqlite__reset(stmt));
/* Now CURRENT_RELPATH is an op-root, have a look at the parent. */
SVN_ERR_ASSERT(current_relpath[0] != '\0'); /* Catch invalid data */
parent_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (!have_row)
{
/* No row means no WORKING node which mean we just fell off
the WORKING tree, so CURRENT_RELPATH is the op-root
closest to the wc root. */
if (have_base && base_del_relpath)
*base_del_relpath = apr_pstrdup(result_pool, current_relpath);
break;
}
/* Still in the WORKING tree so the first time we get here
CURRENT_RELPATH is a delete op-root in the WORKING tree. */
if (work_del_relpath && !*work_del_relpath)
{
*work_del_relpath = apr_pstrdup(result_pool, current_relpath);
if (!scan && !base_del_relpath)
break; /* We have all we need */
}
current_relpath = parent_relpath;
op_depth = svn_sqlite__column_int(stmt, 2);
have_base = !svn_sqlite__column_is_null(stmt, 0);
}
SVN_ERR(svn_sqlite__reset(stmt));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_scan_deletion(const char **base_del_abspath,
const char **moved_to_abspath,
const char **work_del_abspath,
const char **moved_to_op_root_abspath,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
const char *base_del_relpath, *moved_to_relpath, *work_del_relpath;
const char *moved_to_op_root_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_WC__DB_WITH_TXN(
scan_deletion_txn(&base_del_relpath, &moved_to_relpath,
&work_del_relpath, &moved_to_op_root_relpath,
wcroot, local_relpath, result_pool, scratch_pool),
wcroot);
if (base_del_abspath)
{
*base_del_abspath = (base_del_relpath
? svn_dirent_join(wcroot->abspath,
base_del_relpath, result_pool)
: NULL);
}
if (moved_to_abspath)
{
*moved_to_abspath = (moved_to_relpath
? svn_dirent_join(wcroot->abspath,
moved_to_relpath, result_pool)
: NULL);
}
if (work_del_abspath)
{
*work_del_abspath = (work_del_relpath
? svn_dirent_join(wcroot->abspath,
work_del_relpath, result_pool)
: NULL);
}
if (moved_to_op_root_abspath)
{
*moved_to_op_root_abspath = (moved_to_op_root_relpath
? svn_dirent_join(wcroot->abspath,
moved_to_op_root_relpath,
result_pool)
: NULL);
}
return SVN_NO_ERROR;
}
/* Set *COPYFROM_ID, *COPYFROM_RELPATH, *COPYFROM_REV to the values
appropriate for the copy. Also return *STATUS, *KIND and *HAVE_WORK, *OP_ROOT
since they are available. This is a helper for
svn_wc__db_op_copy. */
static svn_error_t *
get_info_for_copy(apr_int64_t *copyfrom_id,
const char **copyfrom_relpath,
svn_revnum_t *copyfrom_rev,
svn_wc__db_status_t *status,
svn_node_kind_t *kind,
svn_boolean_t *op_root,
svn_wc__db_wcroot_t *src_wcroot,
const char *local_relpath,
svn_wc__db_wcroot_t *dst_wcroot,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *repos_relpath;
svn_revnum_t revision;
svn_wc__db_status_t node_status;
apr_int64_t repos_id;
svn_boolean_t is_op_root;
SVN_ERR(read_info(&node_status, kind, &revision, &repos_relpath, &repos_id,
NULL, NULL, NULL, NULL, NULL, NULL, copyfrom_relpath,
copyfrom_id, copyfrom_rev, NULL, NULL, NULL, NULL,
NULL, &is_op_root, NULL, NULL,
NULL /* have_base */,
NULL /* have_more_work */,
NULL /* have_work */,
src_wcroot, local_relpath, result_pool, scratch_pool));
if (op_root)
*op_root = is_op_root;
if (node_status == svn_wc__db_status_excluded)
{
/* The parent cannot be excluded, so look at the parent and then
adjust the relpath */
const char *parent_relpath, *base_name;
svn_dirent_split(&parent_relpath, &base_name, local_relpath,
scratch_pool);
SVN_ERR(get_info_for_copy(copyfrom_id, copyfrom_relpath, copyfrom_rev,
NULL, NULL, NULL,
src_wcroot, parent_relpath, dst_wcroot,
scratch_pool, scratch_pool));
if (*copyfrom_relpath)
*copyfrom_relpath = svn_relpath_join(*copyfrom_relpath, base_name,
result_pool);
}
else if (node_status == svn_wc__db_status_added)
{
SVN_ERR(scan_addition(&node_status, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, src_wcroot, local_relpath,
scratch_pool, scratch_pool));
}
else if (node_status == svn_wc__db_status_deleted && is_op_root)
{
const char *base_del_relpath, *work_del_relpath;
SVN_ERR(scan_deletion_txn(&base_del_relpath, NULL,
&work_del_relpath,
NULL, src_wcroot, local_relpath,
scratch_pool, scratch_pool));
if (work_del_relpath)
{
const char *op_root_relpath;
const char *parent_del_relpath = svn_relpath_dirname(work_del_relpath,
scratch_pool);
/* Similar to, but not the same as, the _scan_addition and
_join above. Can we use get_copyfrom here? */
SVN_ERR(scan_addition(NULL, &op_root_relpath,
NULL, NULL, /* repos_* */
copyfrom_relpath, copyfrom_id, copyfrom_rev,
NULL, NULL, NULL,
src_wcroot, parent_del_relpath,
scratch_pool, scratch_pool));
*copyfrom_relpath
= svn_relpath_join(*copyfrom_relpath,
svn_relpath_skip_ancestor(op_root_relpath,
local_relpath),
result_pool);
}
else if (base_del_relpath)
{
SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, copyfrom_rev,
copyfrom_relpath,
copyfrom_id, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
src_wcroot, local_relpath,
result_pool,
scratch_pool));
}
else
SVN_ERR_MALFUNCTION();
}
else if (node_status == svn_wc__db_status_deleted)
{
/* Keep original_* from read_info() to allow seeing the difference
between base-deleted and not present */
}
else
{
*copyfrom_relpath = repos_relpath;
*copyfrom_rev = revision;
*copyfrom_id = repos_id;
}
if (status)
*status = node_status;
if (src_wcroot != dst_wcroot && *copyfrom_relpath)
{
const char *repos_root_url;
const char *repos_uuid;
/* Pass the right repos-id for the destination db. We can't just use
the id of the source database, as this value can change after
relocation (and perhaps also when we start storing multiple
working copies in a single db)! */
SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
src_wcroot->sdb, *copyfrom_id,
scratch_pool));
SVN_ERR(create_repos_id(copyfrom_id, repos_root_url, repos_uuid,
dst_wcroot->sdb, scratch_pool));
}
return SVN_NO_ERROR;
}
/* Set *OP_DEPTH to the highest op depth of WCROOT:LOCAL_RELPATH. */
static svn_error_t *
op_depth_of(int *op_depth,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_NODE_INFO));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
SVN_ERR_ASSERT(have_row);
*op_depth = svn_sqlite__column_int(stmt, 0);
SVN_ERR(svn_sqlite__reset(stmt));
return SVN_NO_ERROR;
}
/* Determine at which OP_DEPTH a copy of COPYFROM_REPOS_ID, COPYFROM_RELPATH at
revision COPYFROM_REVISION should be inserted as LOCAL_RELPATH. Do this
by checking if this would be a direct child of a copy of its parent
directory. If it is then set *OP_DEPTH to the op_depth of its parent.
If the node is not a direct copy at the same revision of the parent
*NP_OP_DEPTH will be set to the op_depth of the parent when a not-present
node should be inserted at this op_depth. This will be the case when the
parent already defined an incomplete child with the same name. Otherwise
*NP_OP_DEPTH will be set to -1.
If the parent node is not the parent of the to be copied node, then
*OP_DEPTH will be set to the proper op_depth for a new operation root.
Set *PARENT_OP_DEPTH to the op_depth of the parent.
*/
static svn_error_t *
op_depth_for_copy(int *op_depth,
int *np_op_depth,
int *parent_op_depth,
apr_int64_t copyfrom_repos_id,
const char *copyfrom_relpath,
svn_revnum_t copyfrom_revision,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *scratch_pool)
{
const char *parent_relpath, *name;
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
int incomplete_op_depth = -1;
int min_op_depth = 1; /* Never touch BASE */
*op_depth = relpath_depth(local_relpath);
*np_op_depth = -1;
svn_relpath_split(&parent_relpath, &name, local_relpath, scratch_pool);
*parent_op_depth = relpath_depth(parent_relpath);
if (!copyfrom_relpath)
return SVN_NO_ERROR;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_WORKING_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row)
{
svn_wc__db_status_t status = svn_sqlite__column_token(stmt, 1,
presence_map);
min_op_depth = svn_sqlite__column_int(stmt, 0);
if (status == svn_wc__db_status_incomplete)
incomplete_op_depth = min_op_depth;
}
SVN_ERR(svn_sqlite__reset(stmt));
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_WORKING_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row)
{
svn_wc__db_status_t presence = svn_sqlite__column_token(stmt, 1,
presence_map);
*parent_op_depth = svn_sqlite__column_int(stmt, 0);
if (*parent_op_depth < min_op_depth)
{
/* We want to create a copy; not overwrite the lower layers */
SVN_ERR(svn_sqlite__reset(stmt));
return SVN_NO_ERROR;
}
/* You can only add children below a node that exists.
In WORKING that must be status added, which is represented
as presence normal */
SVN_ERR_ASSERT(presence == svn_wc__db_status_normal);
if ((incomplete_op_depth < 0)
|| (incomplete_op_depth == *parent_op_depth))
{
apr_int64_t parent_copyfrom_repos_id
= svn_sqlite__column_int64(stmt, 10);
const char *parent_copyfrom_relpath
= svn_sqlite__column_text(stmt, 11, NULL);
svn_revnum_t parent_copyfrom_revision
= svn_sqlite__column_revnum(stmt, 12);
if (parent_copyfrom_repos_id == copyfrom_repos_id)
{
if (copyfrom_revision == parent_copyfrom_revision
&& !strcmp(copyfrom_relpath,
svn_relpath_join(parent_copyfrom_relpath, name,
scratch_pool)))
*op_depth = *parent_op_depth;
else if (incomplete_op_depth > 0)
*np_op_depth = incomplete_op_depth;
}
}
}
SVN_ERR(svn_sqlite__reset(stmt));
return SVN_NO_ERROR;
}
/* Like svn_wc__db_op_copy(), but with WCROOT+LOCAL_RELPATH
* instead of DB+LOCAL_ABSPATH. A non-zero MOVE_OP_DEPTH implies that the
* copy operation is part of a move, and indicates the op-depth of the
* move destination op-root. */
static svn_error_t *
db_op_copy(svn_wc__db_wcroot_t *src_wcroot,
const char *src_relpath,
svn_wc__db_wcroot_t *dst_wcroot,
const char *dst_relpath,
const svn_skel_t *work_items,
int move_op_depth,
apr_pool_t *scratch_pool)
{
const char *copyfrom_relpath;
svn_revnum_t copyfrom_rev;
svn_wc__db_status_t status;
svn_wc__db_status_t dst_presence;
svn_boolean_t op_root;
apr_int64_t copyfrom_id;
int dst_op_depth;
int dst_np_op_depth;
int dst_parent_op_depth;
svn_node_kind_t kind;
const apr_array_header_t *children;
SVN_ERR(get_info_for_copy(&copyfrom_id, &copyfrom_relpath, &copyfrom_rev,
&status, &kind, &op_root,
src_wcroot, src_relpath, dst_wcroot,
scratch_pool, scratch_pool));
SVN_ERR(op_depth_for_copy(&dst_op_depth, &dst_np_op_depth,
&dst_parent_op_depth,
copyfrom_id, copyfrom_relpath, copyfrom_rev,
dst_wcroot, dst_relpath, scratch_pool));
SVN_ERR_ASSERT(kind == svn_node_file || kind == svn_node_dir);
/* ### New status, not finished, see notes/wc-ng/copying */
switch (status)
{
case svn_wc__db_status_normal:
case svn_wc__db_status_added:
case svn_wc__db_status_moved_here:
case svn_wc__db_status_copied:
dst_presence = svn_wc__db_status_normal;
break;
case svn_wc__db_status_deleted:
if (op_root)
{
/* If the lower layer is already shadowcopied we can skip adding
a not present node. */
svn_error_t *err;
svn_wc__db_status_t dst_status;
err = read_info(&dst_status, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
dst_wcroot, dst_relpath, scratch_pool, scratch_pool);
if (err)
{
if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
svn_error_clear(err);
else
return svn_error_trace(err);
}
else if (dst_status == svn_wc__db_status_deleted)
{
/* Node is already deleted; skip the NODES work, but do
install wq items if requested */
SVN_ERR(add_work_items(dst_wcroot->sdb, work_items,
scratch_pool));
return SVN_NO_ERROR;
}
}
else
{
/* This node is either a not-present node (which should be copied), or
a base-delete of some lower layer (which shouldn't).
Subversion <= 1.7 always added a not-present node here, which is
safe (as it postpones the hard work until commit time and then we
ask the repository), but it breaks some move scenarios.
*/
if (! copyfrom_relpath)
{
SVN_ERR(add_work_items(dst_wcroot->sdb, work_items,
scratch_pool));
return SVN_NO_ERROR;
}
/* Fall through. Install not present node */
}
case svn_wc__db_status_not_present:
case svn_wc__db_status_excluded:
/* These presence values should not create a new op depth */
if (dst_np_op_depth > 0)
{
dst_op_depth = dst_np_op_depth;
dst_np_op_depth = -1;
}
if (status == svn_wc__db_status_excluded)
dst_presence = svn_wc__db_status_excluded;
else
dst_presence = svn_wc__db_status_not_present;
break;
case svn_wc__db_status_server_excluded:
return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
_("Cannot copy '%s' excluded by server"),
path_for_error_message(src_wcroot,
src_relpath,
scratch_pool));
default:
/* Perhaps we should allow incomplete to incomplete? We can't
avoid incomplete working nodes as one step in copying a
directory is to add incomplete children. */
return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
_("Cannot handle status of '%s'"),
path_for_error_message(src_wcroot,
src_relpath,
scratch_pool));
}
if (kind == svn_node_dir)
{
int src_op_depth;
SVN_ERR(op_depth_of(&src_op_depth, src_wcroot, src_relpath));
SVN_ERR(gather_repo_children(&children, src_wcroot, src_relpath,
src_op_depth, scratch_pool, scratch_pool));
}
else
children = NULL;
if (src_wcroot == dst_wcroot)
{
svn_sqlite__stmt_t *stmt;
const char *dst_parent_relpath = svn_relpath_dirname(dst_relpath,
scratch_pool);
SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
STMT_INSERT_WORKING_NODE_COPY_FROM));
SVN_ERR(svn_sqlite__bindf(stmt, "issdst",
src_wcroot->wc_id, src_relpath,
dst_relpath,
dst_op_depth,
dst_parent_relpath,
presence_map, dst_presence));
if (move_op_depth > 0)
{
if (relpath_depth(dst_relpath) == move_op_depth)
{
/* We're moving the root of the move operation.
*
* When an added node or the op-root of a copy is moved,
* there is no 'moved-from' corresponding to the moved-here
* node. So the net effect is the same as copy+delete.
* Perform a normal copy operation in these cases. */
if (!(status == svn_wc__db_status_added ||
(status == svn_wc__db_status_copied && op_root)))
SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
}
else
{
svn_sqlite__stmt_t *info_stmt;
svn_boolean_t have_row;
/* We're moving a child along with the root of the move.
*
* Set moved-here depending on dst_parent, propagating the
* above decision to moved-along children at the same op_depth.
* We can't use scan_addition() to detect moved-here because
* the delete-half of the move might not yet exist. */
SVN_ERR(svn_sqlite__get_statement(&info_stmt, dst_wcroot->sdb,
STMT_SELECT_NODE_INFO));
SVN_ERR(svn_sqlite__bindf(info_stmt, "is", dst_wcroot->wc_id,
dst_parent_relpath));
SVN_ERR(svn_sqlite__step(&have_row, info_stmt));
SVN_ERR_ASSERT(have_row);
if (svn_sqlite__column_boolean(info_stmt, 15) &&
dst_op_depth == dst_parent_op_depth)
{
SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
SVN_ERR(svn_sqlite__reset(info_stmt));
}
else
{
SVN_ERR(svn_sqlite__reset(info_stmt));
/* If the child has been moved into the tree we're moving,
* keep its moved-here bit set. */
SVN_ERR(svn_sqlite__get_statement(&info_stmt,
dst_wcroot->sdb,
STMT_SELECT_NODE_INFO));
SVN_ERR(svn_sqlite__bindf(info_stmt, "is",
dst_wcroot->wc_id, src_relpath));
SVN_ERR(svn_sqlite__step(&have_row, info_stmt));
SVN_ERR_ASSERT(have_row);
if (svn_sqlite__column_boolean(info_stmt, 15))
SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
SVN_ERR(svn_sqlite__reset(info_stmt));
}
}
}
SVN_ERR(svn_sqlite__step_done(stmt));
/* ### Copying changelist is OK for a move but what about a copy? */
SVN_ERR(copy_actual(src_wcroot, src_relpath,
dst_wcroot, dst_relpath, scratch_pool));
if (dst_np_op_depth > 0)
{
/* We introduce a not-present node at the parent's op_depth to
properly start a new op-depth at our own op_depth. This marks
us as an op_root for commit and allows reverting just this
operation */
SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
STMT_INSERT_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtnt",
src_wcroot->wc_id, dst_relpath,
dst_np_op_depth, dst_parent_relpath,
copyfrom_id, copyfrom_relpath,
copyfrom_rev,
presence_map,
svn_wc__db_status_not_present,
/* NULL */
kind_map, kind));
SVN_ERR(svn_sqlite__step_done(stmt));
}
/* Insert incomplete children, if relevant.
The children are part of the same op and so have the same op_depth.
(The only time we'd want a different depth is during a recursive
simple add, but we never insert children here during a simple add.) */
if (kind == svn_node_dir
&& dst_presence == svn_wc__db_status_normal)
SVN_ERR(insert_incomplete_children(
dst_wcroot->sdb,
dst_wcroot->wc_id,
dst_relpath,
copyfrom_id,
copyfrom_relpath,
copyfrom_rev,
children,
dst_op_depth,
scratch_pool));
}
else
{
SVN_ERR(cross_db_copy(src_wcroot, src_relpath, dst_wcroot,
dst_relpath, dst_presence, dst_op_depth,
dst_np_op_depth, kind,
children, copyfrom_id, copyfrom_relpath,
copyfrom_rev, scratch_pool));
}
SVN_ERR(add_work_items(dst_wcroot->sdb, work_items, scratch_pool));
return SVN_NO_ERROR;
}
/* Baton for passing args to op_copy_txn(). */
struct op_copy_baton
{
svn_wc__db_wcroot_t *src_wcroot;
const char *src_relpath;
svn_wc__db_wcroot_t *dst_wcroot;
const char *dst_relpath;
const svn_skel_t *work_items;
svn_boolean_t is_move;
const char *dst_op_root_relpath;
};
/* Helper for svn_wc__db_op_copy().
*
* Implements svn_sqlite__transaction_callback_t. */
static svn_error_t *
op_copy_txn(void * baton,
svn_sqlite__db_t *sdb,
apr_pool_t *scratch_pool)
{
struct op_copy_baton *ocb = baton;
int move_op_depth;
if (sdb != ocb->dst_wcroot->sdb)
{
/* Source and destination databases differ; so also start a lock
in the destination database, by calling ourself in a lock. */
return svn_error_trace(
svn_sqlite__with_lock(ocb->dst_wcroot->sdb,
op_copy_txn, ocb, scratch_pool));
}
/* From this point we can assume a lock in the src and dst databases */
if (ocb->is_move)
move_op_depth = relpath_depth(ocb->dst_op_root_relpath);
else
move_op_depth = 0;
SVN_ERR(db_op_copy(ocb->src_wcroot, ocb->src_relpath,
ocb->dst_wcroot, ocb->dst_relpath,
ocb->work_items, move_op_depth, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_op_copy(svn_wc__db_t *db,
const char *src_abspath,
const char *dst_abspath,
const char *dst_op_root_abspath,
svn_boolean_t is_move,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
struct op_copy_baton ocb = {0};
SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_op_root_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.src_wcroot,
&ocb.src_relpath, db,
src_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(ocb.src_wcroot);
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.dst_wcroot,
&ocb.dst_relpath,
db, dst_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(ocb.dst_wcroot);
ocb.work_items = work_items;
ocb.is_move = is_move;
ocb.dst_op_root_relpath = svn_dirent_skip_ancestor(ocb.dst_wcroot->abspath,
dst_op_root_abspath);
/* Call with the sdb in src_wcroot. It might call itself again to
also obtain a lock in dst_wcroot */
SVN_ERR(svn_sqlite__with_lock(ocb.src_wcroot->sdb, op_copy_txn, &ocb,
scratch_pool));
return SVN_NO_ERROR;
}
/* The txn body of svn_wc__db_op_handle_move_back */
static svn_error_t *
handle_move_back(svn_boolean_t *moved_back,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
const char *moved_from_relpath,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_wc__db_status_t status;
svn_boolean_t op_root;
svn_boolean_t have_more_work;
int from_op_depth = 0;
svn_boolean_t have_row;
svn_boolean_t different = FALSE;
SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
SVN_ERR(svn_wc__db_read_info_internal(&status, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
&op_root, NULL, NULL, NULL,
&have_more_work, NULL,
wcroot, local_relpath,
scratch_pool, scratch_pool));
if (status != svn_wc__db_status_added || !op_root)
return SVN_NO_ERROR;
/* We have two cases here: BASE-move-back and WORKING-move-back */
if (have_more_work)
SVN_ERR(op_depth_of(&from_op_depth, wcroot,
svn_relpath_dirname(local_relpath, scratch_pool)));
else
from_op_depth = 0;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_MOVED_BACK));
SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id,
local_relpath,
from_op_depth,
relpath_depth(local_relpath)));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
SVN_ERR_ASSERT(have_row); /* We checked that the node is an op-root */
{
svn_boolean_t moved_here = svn_sqlite__column_boolean(stmt, 9);
const char *moved_to = svn_sqlite__column_text(stmt, 10, NULL);
if (!moved_here
|| !moved_to
|| strcmp(moved_to, moved_from_relpath))
{
different = TRUE;
have_row = FALSE;
}
}
while (have_row)
{
svn_wc__db_status_t upper_status;
svn_wc__db_status_t lower_status;
upper_status = svn_sqlite__column_token(stmt, 1, presence_map);
if (svn_sqlite__column_is_null(stmt, 5))
{
/* No lower layer replaced. */
if (upper_status != svn_wc__db_status_not_present)
{
different = TRUE;
break;
}
continue;
}
lower_status = svn_sqlite__column_token(stmt, 5, presence_map);
if (upper_status != lower_status)
{
different = TRUE;
break;
}
if (upper_status == svn_wc__db_status_not_present
|| upper_status == svn_wc__db_status_excluded)
{
SVN_ERR(svn_sqlite__step(&have_row, stmt));
continue; /* Nothing to check */
}
else if (upper_status != svn_wc__db_status_normal)
{
/* Not a normal move. Mixed revision move? */
different = TRUE;
break;
}
{
const char *upper_repos_relpath;
const char *lower_repos_relpath;
upper_repos_relpath = svn_sqlite__column_text(stmt, 3, NULL);
lower_repos_relpath = svn_sqlite__column_text(stmt, 7, NULL);
if (! upper_repos_relpath
|| strcmp(upper_repos_relpath, lower_repos_relpath))
{
different = TRUE;
break;
}
}
{
svn_revnum_t upper_rev;
svn_revnum_t lower_rev;
upper_rev = svn_sqlite__column_revnum(stmt, 4);
lower_rev = svn_sqlite__column_revnum(stmt, 8);
if (upper_rev != lower_rev)
{
different = TRUE;
break;
}
}
{
apr_int64_t upper_repos_id;
apr_int64_t lower_repos_id;
upper_repos_id = svn_sqlite__column_int64(stmt, 2);
lower_repos_id = svn_sqlite__column_int64(stmt, 6);
if (upper_repos_id != lower_repos_id)
{
different = TRUE;
break;
}
}
/* Check moved_here? */
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
SVN_ERR(svn_sqlite__reset(stmt));
if (! different)
{
/* Ok, we can now safely remove this complete move, because we
determined that it 100% matches the layer below it. */
/* ### We could copy the recorded timestamps from the higher to the
lower layer in an attempt to improve status performance, but
generally these values should be the same anyway as it was
a no-op move. */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_MOVED_BACK));
SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
local_relpath,
relpath_depth(local_relpath)));
SVN_ERR(svn_sqlite__step_done(stmt));
if (moved_back)
*moved_back = TRUE;
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_op_handle_move_back(svn_boolean_t *moved_back,
svn_wc__db_t *db,
const char *local_abspath,
const char *moved_from_abspath,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
const char *moved_from_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
if (moved_back)
*moved_back = FALSE;
moved_from_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
moved_from_abspath);
if (! local_relpath[0]
|| !moved_from_relpath)
{
/* WC-Roots can't be moved */
SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
return SVN_NO_ERROR;
}
SVN_WC__DB_WITH_TXN(handle_move_back(moved_back, wcroot, local_relpath,
moved_from_relpath, work_items,
scratch_pool),
wcroot);
SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
scratch_pool));
return SVN_NO_ERROR;
}
/* The recursive implementation of svn_wc__db_op_copy_shadowed_layer.
*
* A non-zero MOVE_OP_DEPTH implies that the copy operation is part of
* a move, and indicates the op-depth of the move destination op-root. */
static svn_error_t *
db_op_copy_shadowed_layer(svn_wc__db_wcroot_t *src_wcroot,
const char *src_relpath,
int src_op_depth,
svn_wc__db_wcroot_t *dst_wcroot,
const char *dst_relpath,
int dst_op_depth,
int del_op_depth,
apr_int64_t repos_id,
const char *repos_relpath,
svn_revnum_t revision,
int move_op_depth,
apr_pool_t *scratch_pool)
{
const apr_array_header_t *children;
apr_pool_t *iterpool;
svn_wc__db_status_t status;
svn_node_kind_t kind;
svn_revnum_t node_revision;
const char *node_repos_relpath;
apr_int64_t node_repos_id;
svn_sqlite__stmt_t *stmt;
svn_wc__db_status_t dst_presence;
int i;
{
svn_error_t *err;
err = svn_wc__db_depth_get_info(&status, &kind, &node_revision,
&node_repos_relpath, &node_repos_id,
NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL,
src_wcroot, src_relpath, src_op_depth,
scratch_pool, scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
svn_error_clear(err);
return SVN_NO_ERROR; /* There is no shadowed node at src_op_depth */
}
}
if (src_op_depth == 0)
{
/* If the node is switched or has a different revision then its parent
we shouldn't copy it. (We can't as we would have to insert it at
an unshadowed depth) */
if (status == svn_wc__db_status_not_present
|| status == svn_wc__db_status_excluded
|| status == svn_wc__db_status_server_excluded
|| node_revision != revision
|| node_repos_id != repos_id
|| strcmp(node_repos_relpath, repos_relpath))
{
/* Add a not-present node in the destination wcroot */
struct insert_working_baton_t iwb;
const char *repos_root_url;
const char *repos_uuid;
SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
src_wcroot->sdb, node_repos_id,
scratch_pool));
SVN_ERR(create_repos_id(&node_repos_id, repos_root_url, repos_uuid,
dst_wcroot->sdb, scratch_pool));
blank_iwb(&iwb);
iwb.op_depth = dst_op_depth;
if (status != svn_wc__db_status_excluded)
iwb.presence = svn_wc__db_status_not_present;
else
iwb.presence = svn_wc__db_status_excluded;
iwb.kind = kind;
iwb.original_repos_id = node_repos_id;
iwb.original_revnum = node_revision;
iwb.original_repos_relpath = node_repos_relpath;
SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
scratch_pool));
return SVN_NO_ERROR;
}
}
iterpool = svn_pool_create(scratch_pool);
switch (status)
{
case svn_wc__db_status_normal:
case svn_wc__db_status_added:
case svn_wc__db_status_moved_here:
case svn_wc__db_status_copied:
dst_presence = svn_wc__db_status_normal;
break;
case svn_wc__db_status_deleted:
case svn_wc__db_status_not_present:
dst_presence = svn_wc__db_status_not_present;
break;
case svn_wc__db_status_excluded:
dst_presence = svn_wc__db_status_excluded;
break;
case svn_wc__db_status_server_excluded:
return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
_("Cannot copy '%s' excluded by server"),
path_for_error_message(src_wcroot,
src_relpath,
scratch_pool));
default:
return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
_("Cannot handle status of '%s'"),
path_for_error_message(src_wcroot,
src_relpath,
scratch_pool));
}
if (dst_presence == svn_wc__db_status_normal
&& src_wcroot == dst_wcroot) /* ### Remove limitation */
{
SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
STMT_INSERT_WORKING_NODE_COPY_FROM_DEPTH));
SVN_ERR(svn_sqlite__bindf(stmt, "issdstd",
src_wcroot->wc_id, src_relpath,
dst_relpath,
dst_op_depth,
svn_relpath_dirname(dst_relpath, iterpool),
presence_map, dst_presence,
src_op_depth));
/* moved_here */
if (dst_op_depth == move_op_depth)
SVN_ERR(svn_sqlite__bind_int(stmt, 8, TRUE));
SVN_ERR(svn_sqlite__step_done(stmt));
{
/* And mark it deleted to allow proper shadowing */
struct insert_working_baton_t iwb;
blank_iwb(&iwb);
iwb.op_depth = del_op_depth;
iwb.presence = svn_wc__db_status_base_deleted;
iwb.kind = kind;
SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
scratch_pool));
}
}
else
{
struct insert_working_baton_t iwb;
if (dst_presence == svn_wc__db_status_normal) /* Fallback for multi-db */
dst_presence = svn_wc__db_status_not_present;
/* And mark it deleted to allow proper shadowing */
blank_iwb(&iwb);
iwb.op_depth = dst_op_depth;
iwb.presence = dst_presence;
iwb.kind = kind;
SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
scratch_pool));
}
if (dst_presence == svn_wc__db_status_not_present)
{
/* Don't create descendants of a not present node! */
/* This code is currently still triggered by copying deleted nodes
between separate working copies. See ### comment above. */
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
SVN_ERR(gather_repo_children(&children, src_wcroot, src_relpath,
src_op_depth, scratch_pool, iterpool));
for (i = 0; i < children->nelts; i++)
{
const char *name = APR_ARRAY_IDX(children, i, const char *);
const char *child_src_relpath;
const char *child_dst_relpath;
const char *child_repos_relpath = NULL;
svn_pool_clear(iterpool);
child_src_relpath = svn_relpath_join(src_relpath, name, iterpool);
child_dst_relpath = svn_relpath_join(dst_relpath, name, iterpool);
if (repos_relpath)
child_repos_relpath = svn_relpath_join(repos_relpath, name, iterpool);
SVN_ERR(db_op_copy_shadowed_layer(
src_wcroot, child_src_relpath, src_op_depth,
dst_wcroot, child_dst_relpath, dst_op_depth,
del_op_depth,
repos_id, child_repos_relpath, revision,
move_op_depth, scratch_pool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Helper for svn_wc__db_op_copy_shadowed_layer().
*
* Implements svn_sqlite__transaction_callback_t. */
static svn_error_t *
op_copy_shadowed_layer_txn(void *baton,
svn_sqlite__db_t *sdb,
apr_pool_t *scratch_pool)
{
struct op_copy_baton *ocb = baton;
const char *src_parent_relpath;
const char *dst_parent_relpath;
int src_op_depth;
int dst_op_depth;
int del_op_depth;
const char *repos_relpath = NULL;
apr_int64_t repos_id = INVALID_REPOS_ID;
svn_revnum_t revision = SVN_INVALID_REVNUM;
if (sdb != ocb->dst_wcroot->sdb)
{
/* Source and destination databases differ; so also start a lock
in the destination database, by calling ourself in a lock. */
return svn_error_trace(
svn_sqlite__with_lock(ocb->dst_wcroot->sdb,
op_copy_shadowed_layer_txn,
ocb, scratch_pool));
}
/* From this point we can assume a lock in the src and dst databases */
/* src_relpath and dst_relpath can't be wcroot as we need their parents */
SVN_ERR_ASSERT(*ocb->src_relpath && *ocb->dst_relpath);
src_parent_relpath = svn_relpath_dirname(ocb->src_relpath, scratch_pool);
dst_parent_relpath = svn_relpath_dirname(ocb->dst_relpath, scratch_pool);
/* src_parent must be status normal or added; get its op-depth */
SVN_ERR(op_depth_of(&src_op_depth, ocb->src_wcroot, src_parent_relpath));
/* dst_parent must be status added; get its op-depth */
SVN_ERR(op_depth_of(&dst_op_depth, ocb->dst_wcroot, dst_parent_relpath));
del_op_depth = relpath_depth(ocb->dst_relpath);
/* Get some information from the parent */
SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, &revision, &repos_relpath,
&repos_id, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL,
ocb->src_wcroot,
src_parent_relpath, src_op_depth,
scratch_pool, scratch_pool));
if (repos_relpath == NULL)
{
/* The node is a local addition and has no shadowed information */
return SVN_NO_ERROR;
}
/* And calculate the child repos relpath */
repos_relpath = svn_relpath_join(repos_relpath,
svn_relpath_basename(ocb->src_relpath,
NULL),
scratch_pool);
SVN_ERR(db_op_copy_shadowed_layer(
ocb->src_wcroot, ocb->src_relpath, src_op_depth,
ocb->dst_wcroot, ocb->dst_relpath, dst_op_depth,
del_op_depth,
repos_id, repos_relpath, revision,
(ocb->is_move ? dst_op_depth : 0),
scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_op_copy_shadowed_layer(svn_wc__db_t *db,
const char *src_abspath,
const char *dst_abspath,
svn_boolean_t is_move,
apr_pool_t *scratch_pool)
{
struct op_copy_baton ocb = {0};
SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.src_wcroot,
&ocb.src_relpath, db,
src_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(ocb.src_wcroot);
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.dst_wcroot,
&ocb.dst_relpath,
db, dst_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(ocb.dst_wcroot);
ocb.is_move = is_move;
ocb.dst_op_root_relpath = NULL; /* not used by op_copy_shadowed_layer_txn */
ocb.work_items = NULL;
/* Call with the sdb in src_wcroot. It might call itself again to
also obtain a lock in dst_wcroot */
SVN_ERR(svn_sqlite__with_lock(ocb.src_wcroot->sdb,
op_copy_shadowed_layer_txn,
&ocb, scratch_pool));
return SVN_NO_ERROR;
}
/* If there are any server-excluded base nodes then the copy must fail
as it's not possible to commit such a copy.
Return an error if there are any server-excluded nodes. */
static svn_error_t *
catch_copy_of_server_excluded(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
const char *server_excluded_relpath;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_HAS_SERVER_EXCLUDED_DESCENDANTS));
SVN_ERR(svn_sqlite__bindf(stmt, "is",
wcroot->wc_id,
local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row)
server_excluded_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
SVN_ERR(svn_sqlite__reset(stmt));
if (have_row)
return svn_error_createf(SVN_ERR_AUTHZ_UNREADABLE, NULL,
_("Cannot copy '%s' excluded by server"),
path_for_error_message(wcroot,
server_excluded_relpath,
scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_op_copy_dir(svn_wc__db_t *db,
const char *local_abspath,
const apr_hash_t *props,
svn_revnum_t changed_rev,
apr_time_t changed_date,
const char *changed_author,
const char *original_repos_relpath,
const char *original_root_url,
const char *original_uuid,
svn_revnum_t original_revision,
const apr_array_header_t *children,
- svn_boolean_t is_move,
svn_depth_t depth,
+ svn_boolean_t is_move,
const svn_skel_t *conflict,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
insert_working_baton_t iwb;
int parent_op_depth;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR_ASSERT(props != NULL);
/* ### any assertions for CHANGED_* ? */
/* ### any assertions for ORIGINAL_* ? */
#if 0
SVN_ERR_ASSERT(children != NULL);
#endif
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
blank_iwb(&iwb);
iwb.presence = svn_wc__db_status_normal;
iwb.kind = svn_node_dir;
- iwb.props = props;
- iwb.changed_rev = changed_rev;
- iwb.changed_date = changed_date;
- iwb.changed_author = changed_author;
-
if (original_root_url != NULL)
{
SVN_ERR(create_repos_id(&iwb.original_repos_id,
original_root_url, original_uuid,
wcroot->sdb, scratch_pool));
iwb.original_repos_relpath = original_repos_relpath;
iwb.original_revnum = original_revision;
+
+ iwb.props = props;
+ iwb.changed_rev = changed_rev;
+ iwb.changed_date = changed_date;
+ iwb.changed_author = changed_author;
}
/* ### Should we do this inside the transaction? */
SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
&parent_op_depth, iwb.original_repos_id,
original_repos_relpath, original_revision,
wcroot, local_relpath, scratch_pool));
iwb.children = children;
iwb.depth = depth;
iwb.moved_here = is_move && (parent_op_depth == 0 ||
iwb.op_depth == parent_op_depth);
iwb.work_items = work_items;
iwb.conflict = conflict;
SVN_WC__DB_WITH_TXN(
insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
wcroot);
SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_op_copy_file(svn_wc__db_t *db,
const char *local_abspath,
const apr_hash_t *props,
svn_revnum_t changed_rev,
apr_time_t changed_date,
const char *changed_author,
const char *original_repos_relpath,
const char *original_root_url,
const char *original_uuid,
svn_revnum_t original_revision,
const svn_checksum_t *checksum,
svn_boolean_t update_actual_props,
const apr_hash_t *new_actual_props,
svn_boolean_t is_move,
const svn_skel_t *conflict,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
insert_working_baton_t iwb;
int parent_op_depth;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR_ASSERT(props != NULL);
/* ### any assertions for CHANGED_* ? */
SVN_ERR_ASSERT((! original_repos_relpath && ! original_root_url
&& ! original_uuid && ! checksum
&& original_revision == SVN_INVALID_REVNUM)
|| (original_repos_relpath && original_root_url
&& original_uuid && checksum
&& original_revision != SVN_INVALID_REVNUM));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
blank_iwb(&iwb);
iwb.presence = svn_wc__db_status_normal;
iwb.kind = svn_node_file;
- iwb.props = props;
- iwb.changed_rev = changed_rev;
- iwb.changed_date = changed_date;
- iwb.changed_author = changed_author;
-
if (original_root_url != NULL)
{
SVN_ERR(create_repos_id(&iwb.original_repos_id,
original_root_url, original_uuid,
wcroot->sdb, scratch_pool));
iwb.original_repos_relpath = original_repos_relpath;
iwb.original_revnum = original_revision;
+
+ iwb.props = props;
+ iwb.changed_rev = changed_rev;
+ iwb.changed_date = changed_date;
+ iwb.changed_author = changed_author;
}
/* ### Should we do this inside the transaction? */
SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
&parent_op_depth, iwb.original_repos_id,
original_repos_relpath, original_revision,
wcroot, local_relpath, scratch_pool));
iwb.checksum = checksum;
iwb.moved_here = is_move && (parent_op_depth == 0 ||
iwb.op_depth == parent_op_depth);
if (update_actual_props)
{
iwb.update_actual_props = update_actual_props;
iwb.new_actual_props = new_actual_props;
}
iwb.work_items = work_items;
iwb.conflict = conflict;
SVN_WC__DB_WITH_TXN(
insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
wcroot);
SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_op_copy_symlink(svn_wc__db_t *db,
const char *local_abspath,
const apr_hash_t *props,
svn_revnum_t changed_rev,
apr_time_t changed_date,
const char *changed_author,
const char *original_repos_relpath,
const char *original_root_url,
const char *original_uuid,
svn_revnum_t original_revision,
const char *target,
+ svn_boolean_t is_move,
const svn_skel_t *conflict,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
insert_working_baton_t iwb;
int parent_op_depth;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR_ASSERT(props != NULL);
/* ### any assertions for CHANGED_* ? */
/* ### any assertions for ORIGINAL_* ? */
SVN_ERR_ASSERT(target != NULL);
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
blank_iwb(&iwb);
iwb.presence = svn_wc__db_status_normal;
iwb.kind = svn_node_symlink;
- iwb.props = props;
- iwb.changed_rev = changed_rev;
- iwb.changed_date = changed_date;
- iwb.changed_author = changed_author;
- iwb.moved_here = FALSE;
if (original_root_url != NULL)
{
SVN_ERR(create_repos_id(&iwb.original_repos_id,
original_root_url, original_uuid,
wcroot->sdb, scratch_pool));
iwb.original_repos_relpath = original_repos_relpath;
iwb.original_revnum = original_revision;
+
+ iwb.props = props;
+ iwb.changed_rev = changed_rev;
+ iwb.changed_date = changed_date;
+ iwb.changed_author = changed_author;
}
/* ### Should we do this inside the transaction? */
SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
&parent_op_depth, iwb.original_repos_id,
original_repos_relpath, original_revision,
wcroot, local_relpath, scratch_pool));
iwb.target = target;
+ iwb.moved_here = is_move && (parent_op_depth == 0 ||
+ iwb.op_depth == parent_op_depth);
iwb.work_items = work_items;
iwb.conflict = conflict;
SVN_WC__DB_WITH_TXN(
insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
wcroot);
SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_op_add_directory(svn_wc__db_t *db,
const char *local_abspath,
const apr_hash_t *props,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
const char *dir_abspath;
const char *name;
insert_working_baton_t iwb;
/* Resolve wcroot via parent directory to avoid obstruction handling */
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
dir_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
blank_iwb(&iwb);
local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
iwb.presence = svn_wc__db_status_normal;
iwb.kind = svn_node_dir;
iwb.op_depth = relpath_depth(local_relpath);
if (props && apr_hash_count((apr_hash_t *)props))
{
iwb.update_actual_props = TRUE;
iwb.new_actual_props = props;
}
iwb.work_items = work_items;
SVN_WC__DB_WITH_TXN(
insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
wcroot);
/* Use depth infinity to make sure we have no invalid cached information
* about children of this dir. */
SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_op_add_file(svn_wc__db_t *db,
const char *local_abspath,
const apr_hash_t *props,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
insert_working_baton_t iwb;
const char *dir_abspath;
const char *name;
/* Resolve wcroot via parent directory to avoid obstruction handling */
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
dir_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
blank_iwb(&iwb);
local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
iwb.presence = svn_wc__db_status_normal;
iwb.kind = svn_node_file;
iwb.op_depth = relpath_depth(local_relpath);
if (props && apr_hash_count((apr_hash_t *)props))
{
iwb.update_actual_props = TRUE;
iwb.new_actual_props = props;
}
iwb.work_items = work_items;
SVN_WC__DB_WITH_TXN(
insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
wcroot);
SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_op_add_symlink(svn_wc__db_t *db,
const char *local_abspath,
const char *target,
const apr_hash_t *props,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
insert_working_baton_t iwb;
const char *dir_abspath;
const char *name;
/* Resolve wcroot via parent directory to avoid obstruction handling */
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR_ASSERT(target != NULL);
svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
dir_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
blank_iwb(&iwb);
local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
iwb.presence = svn_wc__db_status_normal;
iwb.kind = svn_node_symlink;
iwb.op_depth = relpath_depth(local_relpath);
if (props && apr_hash_count((apr_hash_t *)props))
{
iwb.update_actual_props = TRUE;
iwb.new_actual_props = props;
}
iwb.target = target;
iwb.work_items = work_items;
SVN_WC__DB_WITH_TXN(
insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
wcroot);
SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
return SVN_NO_ERROR;
}
/* Record RECORDED_SIZE and RECORDED_TIME into top layer in NODES */
static svn_error_t *
db_record_fileinfo(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_int64_t recorded_size,
apr_int64_t recorded_time,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
int affected_rows;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_UPDATE_NODE_FILEINFO));
SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath,
recorded_size, recorded_time));
SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
SVN_ERR_ASSERT(affected_rows == 1);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_global_record_fileinfo(svn_wc__db_t *db,
const char *local_abspath,
svn_filesize_t recorded_size,
apr_time_t recorded_time,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
recorded_size, recorded_time, scratch_pool));
/* We *totally* monkeyed the entries. Toss 'em. */
SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
return SVN_NO_ERROR;
}
/* Set the ACTUAL_NODE properties column for (WC_ID, LOCAL_RELPATH) to
* PROPS.
*
* Note: PROPS=NULL means the actual props are the same as the pristine
* props; to indicate no properties when the pristine has some props,
* PROPS must be an empty hash. */
static svn_error_t *
set_actual_props(apr_int64_t wc_id,
const char *local_relpath,
apr_hash_t *props,
svn_sqlite__db_t *db,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
int affected_rows;
SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_UPDATE_ACTUAL_PROPS));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
if (affected_rows == 1 || !props)
return SVN_NO_ERROR; /* We are done */
/* We have to insert a row in ACTUAL */
SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_ACTUAL_PROPS));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
if (*local_relpath != '\0')
SVN_ERR(svn_sqlite__bind_text(stmt, 3,
svn_relpath_dirname(local_relpath,
scratch_pool)));
SVN_ERR(svn_sqlite__bind_properties(stmt, 4, props, scratch_pool));
return svn_error_trace(svn_sqlite__step_done(stmt));
}
/* The body of svn_wc__db_op_set_props().
Set the 'properties' column in the 'ACTUAL_NODE' table to BATON->props.
Create an entry in the ACTUAL table for the node if it does not yet
have one.
To specify no properties, BATON->props must be an empty hash, not NULL.
BATON is of type 'struct set_props_baton_t'.
*/
static svn_error_t *
set_props_txn(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_hash_t *props,
svn_boolean_t clear_recorded_info,
const svn_skel_t *conflict,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
apr_hash_t *pristine_props;
/* Check if the props are modified. If no changes, then wipe out the
ACTUAL props. PRISTINE_PROPS==NULL means that any
ACTUAL props are okay as provided, so go ahead and set them. */
SVN_ERR(db_read_pristine_props(&pristine_props, wcroot, local_relpath, FALSE,
scratch_pool, scratch_pool));
if (props && pristine_props)
{
apr_array_header_t *prop_diffs;
SVN_ERR(svn_prop_diffs(&prop_diffs, props, pristine_props,
scratch_pool));
if (prop_diffs->nelts == 0)
props = NULL;
}
SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath,
props, wcroot->sdb, scratch_pool));
if (clear_recorded_info)
{
SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
SVN_INVALID_FILESIZE, 0,
scratch_pool));
}
/* And finally. */
SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
if (conflict)
SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
conflict, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_op_set_props(svn_wc__db_t *db,
const char *local_abspath,
apr_hash_t *props,
svn_boolean_t clear_recorded_info,
const svn_skel_t *conflict,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
db, local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_WC__DB_WITH_TXN(set_props_txn(wcroot, local_relpath, props,
clear_recorded_info, conflict, work_items,
scratch_pool),
wcroot);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_op_modified(svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
NOT_IMPLEMENTED();
}
/* */
static svn_error_t *
populate_targets_tree(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
svn_depth_t depth,
const apr_array_header_t *changelist_filter,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
int affected_rows = 0;
SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
STMT_CREATE_TARGETS_LIST));
if (changelist_filter && changelist_filter->nelts > 0)
{
/* Iterate over the changelists, adding the nodes which match.
Common case: we only have one changelist, so this only
happens once. */
int i;
int stmt_idx;
switch (depth)
{
case svn_depth_empty:
stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST;
break;
case svn_depth_files:
stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_FILES;
break;
case svn_depth_immediates:
stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_IMMEDIATES;
break;
case svn_depth_infinity:
stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_INFINITY;
break;
default:
/* We don't know how to handle unknown or exclude. */
SVN_ERR_MALFUNCTION();
break;
}
for (i = 0; i < changelist_filter->nelts; i++)
{
int sub_affected;
const char *changelist = APR_ARRAY_IDX(changelist_filter, i,
const char *);
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_INSERT_TARGET_WITH_CHANGELIST));
SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
local_relpath, changelist));
SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
/* If the root is matched by the changelist, we don't have to match
the children. As that tells us the root is a file */
if (!sub_affected && depth > svn_depth_empty)
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
local_relpath, changelist));
SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
}
affected_rows += sub_affected;
}
}
else /* No changelist filtering */
{
int stmt_idx;
int sub_affected;
switch (depth)
{
case svn_depth_empty:
stmt_idx = STMT_INSERT_TARGET;
break;
case svn_depth_files:
stmt_idx = STMT_INSERT_TARGET_DEPTH_FILES;
break;
case svn_depth_immediates:
stmt_idx = STMT_INSERT_TARGET_DEPTH_IMMEDIATES;
break;
case svn_depth_infinity:
stmt_idx = STMT_INSERT_TARGET_DEPTH_INFINITY;
break;
default:
/* We don't know how to handle unknown or exclude. */
SVN_ERR_MALFUNCTION();
break;
}
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_INSERT_TARGET));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
affected_rows += sub_affected;
if (depth > svn_depth_empty)
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
affected_rows += sub_affected;
}
}
/* Does the target exist? */
if (affected_rows == 0)
{
svn_boolean_t exists;
SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
if (!exists)
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("The node '%s' was not found."),
path_for_error_message(wcroot,
local_relpath,
scratch_pool));
}
return SVN_NO_ERROR;
}
#if 0
static svn_error_t *
dump_targets(svn_wc__db_wcroot_t *wcroot,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_TARGETS));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
while (have_row)
{
const char *target = svn_sqlite__column_text(stmt, 0, NULL);
SVN_DBG(("Target: '%s'\n", target));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
SVN_ERR(svn_sqlite__reset(stmt));
return SVN_NO_ERROR;
}
#endif
struct set_changelist_baton_t
{
const char *new_changelist;
const apr_array_header_t *changelist_filter;
svn_depth_t depth;
};
/* The main part of svn_wc__db_op_set_changelist().
*
* Implements svn_wc__db_txn_callback_t. */
static svn_error_t *
set_changelist_txn(void *baton,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *scratch_pool)
{
struct set_changelist_baton_t *scb = baton;
svn_sqlite__stmt_t *stmt;
SVN_ERR(populate_targets_tree(wcroot, local_relpath, scb->depth,
scb->changelist_filter, scratch_pool));
/* Ensure we have actual nodes for our targets. */
if (scb->new_changelist)
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_INSERT_ACTUAL_EMPTIES));
SVN_ERR(svn_sqlite__step_done(stmt));
}
/* Now create our notification table. */
SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
STMT_CREATE_CHANGELIST_LIST));
SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
STMT_CREATE_CHANGELIST_TRIGGER));
/* Update our changelists. */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_UPDATE_ACTUAL_CHANGELISTS));
SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
scb->new_changelist));
SVN_ERR(svn_sqlite__step_done(stmt));
if (scb->new_changelist)
{
/* We have to notify that we skipped directories, so do that now. */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_MARK_SKIPPED_CHANGELIST_DIRS));
SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
scb->new_changelist));
SVN_ERR(svn_sqlite__step_done(stmt));
}
/* We may have left empty ACTUAL nodes, so remove them. This is only a
potential problem if we removed changelists. */
if (!scb->new_changelist)
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_ACTUAL_EMPTIES));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
}
return SVN_NO_ERROR;
}
/* Send notifications for svn_wc__db_op_set_changelist().
*
* Implements work_callback_t. */
static svn_error_t *
do_changelist_notify(void *baton,
svn_wc__db_wcroot_t *wcroot,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
apr_pool_t *iterpool;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_CHANGELIST_LIST));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
iterpool = svn_pool_create(scratch_pool);
while (have_row)
{
/* ### wc_id is column 0. use it one day... */
const char *notify_relpath = svn_sqlite__column_text(stmt, 1, NULL);
svn_wc_notify_action_t action = svn_sqlite__column_int(stmt, 2);
svn_wc_notify_t *notify;
const char *notify_abspath;
svn_pool_clear(iterpool);
if (cancel_func)
{
svn_error_t *err = cancel_func(cancel_baton);
if (err)
return svn_error_trace(svn_error_compose_create(
err,
svn_sqlite__reset(stmt)));
}
notify_abspath = svn_dirent_join(wcroot->abspath, notify_relpath,
iterpool);
notify = svn_wc_create_notify(notify_abspath, action, iterpool);
notify->changelist_name = svn_sqlite__column_text(stmt, 3, NULL);
notify_func(notify_baton, notify, iterpool);
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
svn_pool_destroy(iterpool);
return svn_error_trace(svn_sqlite__reset(stmt));
}
svn_error_t *
svn_wc__db_op_set_changelist(svn_wc__db_t *db,
const char *local_abspath,
const char *new_changelist,
const apr_array_header_t *changelist_filter,
svn_depth_t depth,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
struct set_changelist_baton_t scb;
scb.new_changelist = new_changelist;
scb.changelist_filter = changelist_filter;
scb.depth = depth;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
db, local_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
/* Flush the entries before we do the work. Even if no work is performed,
the flush isn't a problem. */
SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
/* Perform the set-changelist operation (transactionally), perform any
notifications necessary, and then clean out our temporary tables. */
return svn_error_trace(with_finalization(wcroot, local_relpath,
set_changelist_txn, &scb,
do_changelist_notify, NULL,
cancel_func, cancel_baton,
notify_func, notify_baton,
STMT_FINALIZE_CHANGELIST,
scratch_pool));
}
/* Implementation of svn_wc__db_op_mark_conflict() */
svn_error_t *
svn_wc__db_mark_conflict_internal(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
const svn_skel_t *conflict_skel,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t got_row;
svn_boolean_t is_complete;
SVN_ERR(svn_wc__conflict_skel_is_complete(&is_complete, conflict_skel));
SVN_ERR_ASSERT(is_complete);
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_ACTUAL_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&got_row, stmt));
SVN_ERR(svn_sqlite__reset(stmt));
if (got_row)
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_UPDATE_ACTUAL_CONFLICT));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
}
else
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_INSERT_ACTUAL_CONFLICT));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
if (*local_relpath != '\0')
SVN_ERR(svn_sqlite__bind_text(stmt, 4,
svn_relpath_dirname(local_relpath,
scratch_pool)));
}
{
svn_stringbuf_t *sb = svn_skel__unparse(conflict_skel, scratch_pool);
SVN_ERR(svn_sqlite__bind_blob(stmt, 3, sb->data, sb->len));
}
SVN_ERR(svn_sqlite__update(NULL, stmt));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_op_mark_conflict(svn_wc__db_t *db,
const char *local_abspath,
const svn_skel_t *conflict_skel,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
conflict_skel, scratch_pool));
/* ### Should be handled in the same transaction as setting the conflict */
if (work_items)
SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
return SVN_NO_ERROR;
}
/* The body of svn_wc__db_op_mark_resolved().
*/
static svn_error_t *
db_op_mark_resolved(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
svn_wc__db_t *db,
svn_boolean_t resolved_text,
svn_boolean_t resolved_props,
svn_boolean_t resolved_tree,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
int total_affected_rows = 0;
svn_boolean_t resolved_all;
apr_size_t conflict_len;
const void *conflict_data;
svn_skel_t *conflicts;
/* Check if we have a conflict in ACTUAL */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_ACTUAL_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (! have_row)
{
SVN_ERR(svn_sqlite__reset(stmt));
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_NODE_INFO));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
SVN_ERR(svn_sqlite__reset(stmt));
if (have_row)
return SVN_NO_ERROR;
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("The node '%s' was not found."),
path_for_error_message(wcroot,
local_relpath,
scratch_pool));
}
conflict_data = svn_sqlite__column_blob(stmt, 2, &conflict_len,
scratch_pool);
conflicts = svn_skel__parse(conflict_data, conflict_len, scratch_pool);
SVN_ERR(svn_sqlite__reset(stmt));
SVN_ERR(svn_wc__conflict_skel_resolve(&resolved_all, conflicts,
db, wcroot->abspath,
resolved_text,
resolved_props ? "" : NULL,
resolved_tree,
scratch_pool, scratch_pool));
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_UPDATE_ACTUAL_CONFLICT));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
if (! resolved_all)
{
svn_stringbuf_t *sb = svn_skel__unparse(conflicts, scratch_pool);
SVN_ERR(svn_sqlite__bind_blob(stmt, 3, sb->data, sb->len));
}
SVN_ERR(svn_sqlite__update(&total_affected_rows, stmt));
/* Now, remove the actual node if it doesn't have any more useful
information. We only need to do this if we've remove data ourselves. */
if (total_affected_rows > 0)
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_ACTUAL_EMPTY));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
}
SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_op_mark_resolved(svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t resolved_text,
svn_boolean_t resolved_props,
svn_boolean_t resolved_tree,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_WC__DB_WITH_TXN(
db_op_mark_resolved(wcroot, local_relpath, db,
resolved_text, resolved_props, resolved_tree,
work_items, scratch_pool),
wcroot);
SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
return SVN_NO_ERROR;
}
/* Clear moved-to information at the delete-half of the move which
* moved LOCAL_RELPATH here. This transforms the move into a simple delete. */
static svn_error_t *
clear_moved_to(const char *local_relpath,
svn_wc__db_wcroot_t *wcroot,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
const char *moved_from_relpath;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_MOVED_FROM_RELPATH));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (!have_row)
{
SVN_ERR(svn_sqlite__reset(stmt));
return SVN_NO_ERROR;
}
moved_from_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
SVN_ERR(svn_sqlite__reset(stmt));
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_CLEAR_MOVED_TO_RELPATH));
SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
moved_from_relpath,
relpath_depth(moved_from_relpath)));
SVN_ERR(svn_sqlite__step_done(stmt));
return SVN_NO_ERROR;
}
/* One of the two alternative bodies of svn_wc__db_op_revert().
*
* Implements svn_wc__db_txn_callback_t. */
static svn_error_t *
op_revert_txn(void *baton,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *scratch_pool)
{
svn_wc__db_t *db = baton;
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
int op_depth;
svn_boolean_t moved_here;
int affected_rows;
const char *moved_to;
/* ### Similar structure to op_revert_recursive_txn, should they be
combined? */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_NODE_INFO));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (!have_row)
{
SVN_ERR(svn_sqlite__reset(stmt));
/* There was no NODE row, so attempt to delete an ACTUAL_NODE row. */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_ACTUAL_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
if (affected_rows)
{
/* Can't do non-recursive actual-only revert if actual-only
children exist. Raise an error to cancel the transaction. */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_ACTUAL_HAS_CHILDREN));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
SVN_ERR(svn_sqlite__reset(stmt));
if (have_row)
return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
_("Can't revert '%s' without"
" reverting children"),
path_for_error_message(wcroot,
local_relpath,
scratch_pool));
return SVN_NO_ERROR;
}
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("The node '%s' was not found."),
path_for_error_message(wcroot,
local_relpath,
scratch_pool));
}
op_depth = svn_sqlite__column_int(stmt, 0);
moved_here = svn_sqlite__column_boolean(stmt, 15);
moved_to = svn_sqlite__column_text(stmt, 17, scratch_pool);
SVN_ERR(svn_sqlite__reset(stmt));
if (moved_to)
{
SVN_ERR(svn_wc__db_resolve_break_moved_away_internal(wcroot,
local_relpath,
op_depth,
scratch_pool));
}
else
{
svn_skel_t *conflict;
SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, wcroot,
local_relpath,
scratch_pool, scratch_pool));
if (conflict)
{
svn_wc_operation_t operation;
svn_boolean_t tree_conflicted;
SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL,
&tree_conflicted,
db, wcroot->abspath,
conflict,
scratch_pool, scratch_pool));
if (tree_conflicted
&& (operation == svn_wc_operation_update
|| operation == svn_wc_operation_switch))
{
svn_wc_conflict_reason_t reason;
svn_wc_conflict_action_t action;
SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action,
NULL,
db, wcroot->abspath,
conflict,
scratch_pool,
scratch_pool));
if (reason == svn_wc_conflict_reason_deleted)
SVN_ERR(svn_wc__db_resolve_delete_raise_moved_away(
db, svn_dirent_join(wcroot->abspath, local_relpath,
scratch_pool),
NULL, NULL /* ### How do we notify this? */,
scratch_pool));
}
}
}
if (op_depth > 0 && op_depth == relpath_depth(local_relpath))
{
/* Can't do non-recursive revert if children exist */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_GE_OP_DEPTH_CHILDREN));
SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
local_relpath, op_depth));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
SVN_ERR(svn_sqlite__reset(stmt));
if (have_row)
return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
_("Can't revert '%s' without"
" reverting children"),
path_for_error_message(wcroot,
local_relpath,
scratch_pool));
/* Rewrite the op-depth of all deleted children making the
direct children into roots of deletes. */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_UPDATE_OP_DEPTH_INCREASE_RECURSIVE));
SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
local_relpath,
op_depth));
SVN_ERR(svn_sqlite__step_done(stmt));
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_WORKING_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
/* ### This removes the lock, but what about the access baton? */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_WC_LOCK_ORPHAN));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
/* If this node was moved-here, clear moved-to at the move source. */
if (moved_here)
SVN_ERR(clear_moved_to(local_relpath, wcroot, scratch_pool));
}
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
if (!affected_rows)
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
}
return SVN_NO_ERROR;
}
/* One of the two alternative bodies of svn_wc__db_op_revert().
*
* Implements svn_wc__db_txn_callback_t. */
static svn_error_t *
op_revert_recursive_txn(void *baton,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
int op_depth;
int select_op_depth;
svn_boolean_t moved_here;
int affected_rows;
apr_pool_t *iterpool;
/* ### Similar structure to op_revert_txn, should they be
combined? */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_NODE_INFO));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (!have_row)
{
SVN_ERR(svn_sqlite__reset(stmt));
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_ACTUAL_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
local_relpath));
SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
if (affected_rows)
return SVN_NO_ERROR; /* actual-only revert */
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("The node '%s' was not found."),
path_for_error_message(wcroot,
local_relpath,
scratch_pool));
}
op_depth = svn_sqlite__column_int(stmt, 0);
moved_here = svn_sqlite__column_boolean(stmt, 15);
SVN_ERR(svn_sqlite__reset(stmt));
if (op_depth > 0 && op_depth != relpath_depth(local_relpath))
return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
_("Can't revert '%s' without"
" reverting parent"),
path_for_error_message(wcroot,
local_relpath,
scratch_pool));
/* Remove moved-here from move destinations outside the tree. */
SVN_ERR(svn_sqlite__get_statement(
&stmt, wcroot->sdb, STMT_SELECT_MOVED_OUTSIDE));
SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
op_depth));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
while (have_row)
{
const char *move_src_relpath = svn_sqlite__column_text(stmt, 0, NULL);
int move_op_depth = svn_sqlite__column_int(stmt, 2);
svn_error_t *err;
err = svn_wc__db_resolve_break_moved_away_internal(wcroot,
move_src_relpath,
move_op_depth,
scratch_pool);
if (err)
return svn_error_compose_create(err, svn_sqlite__reset(stmt));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
SVN_ERR(svn_sqlite__reset(stmt));
/* Don't delete BASE nodes */
select_op_depth = op_depth ? op_depth : 1;
/* Reverting any non wc-root node */
SVN_ERR(svn_sqlite__get_statement(
&stmt, wcroot->sdb,
STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
local_relpath, select_op_depth));
SVN_ERR(svn_sqlite__step_done(stmt));
SVN_ERR(svn_sqlite__get_statement(
&stmt, wcroot->sdb,
STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
SVN_ERR(svn_sqlite__get_statement(
&stmt, wcroot->sdb,
STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
/* ### This removes the locks, but what about the access batons? */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_MOVED_HERE_CHILDREN));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
iterpool = svn_pool_create(scratch_pool);
while (have_row)
{
const char *moved_here_child_relpath;
svn_error_t *err;
svn_pool_clear(iterpool);
moved_here_child_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
err = clear_moved_to(moved_here_child_relpath, wcroot, iterpool);
if (err)
return svn_error_trace(svn_error_compose_create(
err,
svn_sqlite__reset(stmt)));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
SVN_ERR(svn_sqlite__reset(stmt));
svn_pool_destroy(iterpool);
/* Clear potential moved-to pointing at the target node itself. */
if (op_depth > 0 && op_depth == relpath_depth(local_relpath)
&& moved_here)
SVN_ERR(clear_moved_to(local_relpath, wcroot, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_op_revert(svn_wc__db_t *db,
const char *local_abspath,
svn_depth_t depth,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
struct with_triggers_baton_t wtb = { STMT_CREATE_REVERT_LIST,
STMT_DROP_REVERT_LIST_TRIGGERS,
NULL, NULL};
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
switch (depth)
{
case svn_depth_empty:
wtb.cb_func = op_revert_txn;
wtb.cb_baton = db;
break;
case svn_depth_infinity:
wtb.cb_func = op_revert_recursive_txn;
break;
default:
return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Unsupported depth for revert of '%s'"),
svn_dirent_local_style(local_abspath,
scratch_pool));
}
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
db, local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_WC__DB_WITH_TXN(with_triggers(&wtb, wcroot, local_relpath, scratch_pool),
wcroot);
SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
return SVN_NO_ERROR;
}
/* The body of svn_wc__db_revert_list_read().
*/
static svn_error_t *
revert_list_read(svn_boolean_t *reverted,
const apr_array_header_t **marker_paths,
svn_boolean_t *copied_here,
svn_node_kind_t *kind,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
svn_wc__db_t *db,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
*reverted = FALSE;
*marker_paths = NULL;
*copied_here = FALSE;
*kind = svn_node_unknown;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_REVERT_LIST));
SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row)
{
svn_boolean_t is_actual = svn_sqlite__column_boolean(stmt, 0);
svn_boolean_t another_row = FALSE;
if (is_actual)
{
apr_size_t conflict_len;
const void *conflict_data;
conflict_data = svn_sqlite__column_blob(stmt, 5, &conflict_len,
scratch_pool);
if (conflict_data)
{
svn_skel_t *conflicts = svn_skel__parse(conflict_data,
conflict_len,
scratch_pool);
SVN_ERR(svn_wc__conflict_read_markers(marker_paths,
db, wcroot->abspath,
conflicts,
result_pool,
scratch_pool));
}
if (!svn_sqlite__column_is_null(stmt, 1)) /* notify */
*reverted = TRUE;
SVN_ERR(svn_sqlite__step(&another_row, stmt));
}
if (!is_actual || another_row)
{
*reverted = TRUE;
if (!svn_sqlite__column_is_null(stmt, 4)) /* repos_id */
{
int op_depth = svn_sqlite__column_int(stmt, 3);
*copied_here = (op_depth == relpath_depth(local_relpath));
}
*kind = svn_sqlite__column_token(stmt, 2, kind_map);
}
}
SVN_ERR(svn_sqlite__reset(stmt));
if (have_row)
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_REVERT_LIST));
SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_revert_list_read(svn_boolean_t *reverted,
const apr_array_header_t **marker_files,
svn_boolean_t *copied_here,
svn_node_kind_t *kind,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
db, local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_WC__DB_WITH_TXN(
revert_list_read(reverted, marker_files, copied_here, kind,
wcroot, local_relpath, db,
result_pool, scratch_pool),
wcroot);
return SVN_NO_ERROR;
}
/* The body of svn_wc__db_revert_list_read_copied_children().
*/
static svn_error_t *
revert_list_read_copied_children(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
const apr_array_header_t **children_p,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
apr_array_header_t *children;
children =
apr_array_make(result_pool, 0,
sizeof(svn_wc__db_revert_list_copied_child_info_t *));
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_REVERT_LIST_COPIED_CHILDREN));
SVN_ERR(svn_sqlite__bindf(stmt, "sd",
local_relpath, relpath_depth(local_relpath)));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
while (have_row)
{
svn_wc__db_revert_list_copied_child_info_t *child_info;
const char *child_relpath;
child_info = apr_palloc(result_pool, sizeof(*child_info));
child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
child_info->abspath = svn_dirent_join(wcroot->abspath, child_relpath,
result_pool);
child_info->kind = svn_sqlite__column_token(stmt, 1, kind_map);
APR_ARRAY_PUSH(
children,
svn_wc__db_revert_list_copied_child_info_t *) = child_info;
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
SVN_ERR(svn_sqlite__reset(stmt));
*children_p = children;
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_revert_list_read_copied_children(const apr_array_header_t **children,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
db, local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_WC__DB_WITH_TXN(
revert_list_read_copied_children(wcroot, local_relpath, children,
result_pool, scratch_pool),
wcroot);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_revert_list_notify(svn_wc_notify_func2_t notify_func,
void *notify_baton,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
db, local_abspath, scratch_pool, iterpool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_REVERT_LIST_RECURSIVE));
SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (!have_row)
return svn_error_trace(svn_sqlite__reset(stmt)); /* optimise for no row */
while (have_row)
{
const char *notify_relpath = svn_sqlite__column_text(stmt, 0, NULL);
svn_pool_clear(iterpool);
notify_func(notify_baton,
svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
notify_relpath,
iterpool),
svn_wc_notify_revert,
iterpool),
iterpool);
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
SVN_ERR(svn_sqlite__reset(stmt));
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_REVERT_LIST_RECURSIVE));
SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_revert_list_done(svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
db, local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_DROP_REVERT_LIST));
return SVN_NO_ERROR;
}
/* The body of svn_wc__db_op_remove_node().
*/
static svn_error_t *
remove_node_txn(svn_boolean_t *left_changes,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
svn_wc__db_t *db,
svn_boolean_t destroy_wc,
svn_boolean_t destroy_changes,
svn_revnum_t not_present_rev,
svn_wc__db_status_t not_present_status,
svn_node_kind_t not_present_kind,
const svn_skel_t *conflict,
const svn_skel_t *work_items,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
apr_int64_t repos_id;
const char *repos_relpath;
/* Note that unlike many similar functions it is a valid scenario for this
function to be called on a wcroot! */
/* db set when destroying wc */
SVN_ERR_ASSERT(!destroy_wc || db != NULL);
if (left_changes)
*left_changes = FALSE;
/* Need info for not_present node? */
if (SVN_IS_VALID_REVNUM(not_present_rev))
SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
&repos_relpath, &repos_id,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
wcroot, local_relpath,
scratch_pool, scratch_pool));
if (destroy_wc
&& (!destroy_changes || *local_relpath == '\0'))
{
svn_boolean_t have_row;
apr_pool_t *iterpool;
svn_error_t *err = NULL;
/* Install WQ items for deleting the unmodified files and all dirs */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_WORKING_PRESENT));
SVN_ERR(svn_sqlite__bindf(stmt, "is",
wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
iterpool = svn_pool_create(scratch_pool);
while (have_row)
{
const char *child_relpath;
const char *child_abspath;
svn_node_kind_t child_kind;
svn_boolean_t have_checksum;
svn_filesize_t recorded_size;
apr_int64_t recorded_time;
const svn_io_dirent2_t *dirent;
svn_boolean_t modified_p = TRUE;
svn_skel_t *work_item = NULL;
svn_pool_clear(iterpool);
child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
child_kind = svn_sqlite__column_token(stmt, 1, kind_map);
child_abspath = svn_dirent_join(wcroot->abspath, child_relpath,
iterpool);
if (child_kind == svn_node_file)
{
have_checksum = !svn_sqlite__column_is_null(stmt, 2);
recorded_size = get_recorded_size(stmt, 3);
recorded_time = svn_sqlite__column_int64(stmt, 4);
}
if (cancel_func)
err = cancel_func(cancel_baton);
if (err)
break;
err = svn_io_stat_dirent2(&dirent, child_abspath, FALSE, TRUE,
iterpool, iterpool);
if (err)
break;
if (destroy_changes
|| dirent->kind != svn_node_file
|| child_kind != svn_node_file)
{
/* Not interested in keeping changes */
modified_p = FALSE;
}
else if (child_kind == svn_node_file
&& dirent->kind == svn_node_file
&& dirent->filesize == recorded_size
&& dirent->mtime == recorded_time)
{
modified_p = FALSE; /* File matches recorded state */
}
else if (have_checksum)
err = svn_wc__internal_file_modified_p(&modified_p,
db, child_abspath,
FALSE, iterpool);
if (err)
break;
if (modified_p)
{
if (left_changes)
*left_changes = TRUE;
}
else if (child_kind == svn_node_dir)
{
err = svn_wc__wq_build_dir_remove(&work_item,
db, wcroot->abspath,
child_abspath, FALSE,
iterpool, iterpool);
}
else /* svn_node_file || svn_node_symlink */
{
err = svn_wc__wq_build_file_remove(&work_item,
db, wcroot->abspath,
child_abspath,
iterpool, iterpool);
}
if (err)
break;
if (work_item)
{
err = add_work_items(wcroot->sdb, work_item, iterpool);
if (err)
break;
}
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
svn_pool_destroy(iterpool);
SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
}
if (destroy_wc && *local_relpath != '\0')
{
/* Create work item for destroying the root */
svn_wc__db_status_t status;
svn_node_kind_t kind;
SVN_ERR(read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
wcroot, local_relpath,
scratch_pool, scratch_pool));
if (status == svn_wc__db_status_normal
|| status == svn_wc__db_status_added
|| status == svn_wc__db_status_incomplete)
{
svn_skel_t *work_item = NULL;
const char *local_abspath = svn_dirent_join(wcroot->abspath,
local_relpath,
scratch_pool);
if (kind == svn_node_dir)
{
SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
db, wcroot->abspath,
local_abspath,
destroy_changes
/* recursive */,
scratch_pool, scratch_pool));
}
else
{
svn_boolean_t modified_p = FALSE;
if (!destroy_changes)
{
SVN_ERR(svn_wc__internal_file_modified_p(&modified_p,
db, local_abspath,
FALSE,
scratch_pool));
}
if (!modified_p)
SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
db, wcroot->abspath,
local_abspath,
scratch_pool,
scratch_pool));
else
{
if (left_changes)
*left_changes = TRUE;
}
}
SVN_ERR(add_work_items(wcroot->sdb, work_item, scratch_pool));
}
}
/* Remove all nodes below local_relpath */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_NODE_RECURSIVE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
/* Delete the root NODE when this is not the working copy root */
if (local_relpath[0] != '\0')
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_NODE_ALL_LAYERS));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
}
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_ACTUAL_NODE_RECURSIVE));
/* Delete all actual nodes at or below local_relpath */
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
/* Should we leave a not-present node? */
if (SVN_IS_VALID_REVNUM(not_present_rev))
{
insert_base_baton_t ibb;
blank_ibb(&ibb);
ibb.repos_id = repos_id;
SVN_ERR_ASSERT(not_present_status == svn_wc__db_status_not_present
|| not_present_status == svn_wc__db_status_excluded);
ibb.status = not_present_status;
ibb.kind = not_present_kind;
ibb.repos_relpath = repos_relpath;
ibb.revision = not_present_rev;
SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
}
SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
if (conflict)
SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
conflict, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_op_remove_node(svn_boolean_t *left_changes,
svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t destroy_wc,
svn_boolean_t destroy_changes,
svn_revnum_t not_present_revision,
svn_wc__db_status_t not_present_status,
svn_node_kind_t not_present_kind,
const svn_skel_t *conflict,
const svn_skel_t *work_items,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_WC__DB_WITH_TXN(remove_node_txn(left_changes,
wcroot, local_relpath, db,
destroy_wc, destroy_changes,
not_present_revision, not_present_status,
not_present_kind, conflict, work_items,
cancel_func, cancel_baton, scratch_pool),
wcroot);
/* Flush everything below this node in all ways */
SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
scratch_pool));
return SVN_NO_ERROR;
}
/* The body of svn_wc__db_op_set_base_depth().
*/
static svn_error_t *
db_op_set_base_depth(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
svn_depth_t depth,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
int affected_rows;
/* Flush any entries before we start monkeying the database. */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_UPDATE_NODE_BASE_DEPTH));
SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
svn_token__to_word(depth_map, depth)));
SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
if (affected_rows == 0)
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
"The node '%s' is not a committed directory",
path_for_error_message(wcroot, local_relpath,
scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_op_set_base_depth(svn_wc__db_t *db,
const char *local_abspath,
svn_depth_t depth,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR_ASSERT(depth >= svn_depth_empty && depth <= svn_depth_infinity);
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
/* ### We set depth on working and base to match entry behavior.
Maybe these should be separated later? */
SVN_WC__DB_WITH_TXN(db_op_set_base_depth(wcroot, local_relpath, depth,
scratch_pool),
wcroot);
SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
info_below_working(svn_boolean_t *have_base,
svn_boolean_t *have_work,
svn_wc__db_status_t *status,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
int below_op_depth, /* < 0 is ignored */
apr_pool_t *scratch_pool);
/* Convert STATUS, the raw status obtained from the presence map, to
the status appropriate for a working (op_depth > 0) node and return
it in *WORKING_STATUS. */
static svn_error_t *
convert_to_working_status(svn_wc__db_status_t *working_status,
svn_wc__db_status_t status)
{
svn_wc__db_status_t work_status = status;
SVN_ERR_ASSERT(work_status == svn_wc__db_status_normal
|| work_status == svn_wc__db_status_not_present
|| work_status == svn_wc__db_status_base_deleted
|| work_status == svn_wc__db_status_incomplete
|| work_status == svn_wc__db_status_excluded);
if (work_status == svn_wc__db_status_excluded)
{
*working_status = svn_wc__db_status_excluded;
}
else if (work_status == svn_wc__db_status_not_present
|| work_status == svn_wc__db_status_base_deleted)
{
/* The caller should scan upwards to detect whether this
deletion has occurred because this node has been moved
away, or it is a regular deletion. Also note that the
deletion could be of the BASE tree, or a child of
something that has been copied/moved here. */
*working_status = svn_wc__db_status_deleted;
}
else /* normal or incomplete */
{
/* The caller should scan upwards to detect whether this
addition has occurred because of a simple addition,
a copy, or is the destination of a move. */
*working_status = svn_wc__db_status_added;
}
return SVN_NO_ERROR;
}
/* Return the status of the node, if any, below the "working" node (or
below BELOW_OP_DEPTH if >= 0).
Set *HAVE_BASE or *HAVE_WORK to indicate if a base node or lower
working node is present, and *STATUS to the status of the first
layer below the selected node. */
static svn_error_t *
info_below_working(svn_boolean_t *have_base,
svn_boolean_t *have_work,
svn_wc__db_status_t *status,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
int below_op_depth,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
*have_base = *have_work = FALSE;
*status = svn_wc__db_status_normal;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_NODE_INFO));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (below_op_depth >= 0)
{
while (have_row &&
(svn_sqlite__column_int(stmt, 0) > below_op_depth))
{
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
}
if (have_row)
{
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row)
*status = svn_sqlite__column_token(stmt, 3, presence_map);
while (have_row)
{
int op_depth = svn_sqlite__column_int(stmt, 0);
if (op_depth > 0)
*have_work = TRUE;
else
*have_base = TRUE;
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
}
SVN_ERR(svn_sqlite__reset(stmt));
if (*have_work)
SVN_ERR(convert_to_working_status(status, *status));
return SVN_NO_ERROR;
}
/* Helper function for op_delete_txn */
static svn_error_t *
delete_update_movedto(svn_wc__db_wcroot_t *wcroot,
const char *child_moved_from_relpath,
int op_depth,
const char *new_moved_to_relpath,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
int affected;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_UPDATE_MOVED_TO_RELPATH));
SVN_ERR(svn_sqlite__bindf(stmt, "isds",
wcroot->wc_id,
child_moved_from_relpath,
op_depth,
new_moved_to_relpath));
SVN_ERR(svn_sqlite__update(&affected, stmt));
- assert(affected == 1);
+#ifdef SVN_DEBUG
+ /* Not fatal in release mode. The move recording is broken,
+ but the rest of the working copy can handle this. */
+ SVN_ERR_ASSERT(affected == 1);
+#endif
return SVN_NO_ERROR;
}
struct op_delete_baton_t {
const char *moved_to_relpath; /* NULL if delete is not part of a move */
svn_skel_t *conflict;
svn_skel_t *work_items;
svn_boolean_t delete_dir_externals;
svn_boolean_t notify;
};
/* This structure is used while rewriting move information for nodes.
*
* The most simple case of rewriting move information happens when
* a moved-away subtree is moved again: mv A B; mv B C
* The second move requires rewriting moved-to info at or within A.
*
* Another example is a move of a subtree which had nodes moved into it:
* mv A B/F; mv B G
* This requires rewriting such that A/F is marked has having moved to G/F.
*
* Another case is where a node becomes a nested moved node.
* A nested move happens when a subtree child is moved before or after
* the subtree itself is moved. For example:
* mv A/F A/G; mv A B
* In this case, the move A/F -> A/G is rewritten to B/F -> B/G.
* Note that the following sequence results in the same DB state:
* mv A B; mv B/F B/G
* We do not care about the order the moves were performed in.
* For details, see http://wiki.apache.org/subversion/MultiLayerMoves
*/
struct moved_node_t {
/* The source of the move. */
const char *local_relpath;
/* The move destination. */
const char *moved_to_relpath;
/* The op-depth of the deleted node at the source of the move. */
int op_depth;
/* When >= 1 the op_depth at which local_relpath was moved to its
location. Used to find its original location outside the delete */
int moved_from_depth;
};
/* Helper function to resolve the original location of local_relpath at OP_DEPTH
before it was moved into the tree rooted at ROOT_RELPATH. */
static svn_error_t *
resolve_moved_from(const char **moved_from_relpath,
int *moved_from_op_depth,
svn_wc__db_wcroot_t *wcroot,
const char *root_relpath,
const char *local_relpath,
int op_depth,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *suffix = "";
svn_sqlite__stmt_t *stmt;
const char *m_from_relpath;
int m_from_op_depth;
int m_move_from_depth;
svn_boolean_t have_row;
while (relpath_depth(local_relpath) > op_depth)
{
const char *name;
svn_relpath_split(&local_relpath, &name, local_relpath, scratch_pool);
suffix = svn_relpath_join(suffix, name, scratch_pool);
}
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_MOVED_FROM_FOR_DELETE));
SVN_ERR(svn_sqlite__bindf(stmt, "is",
wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (!have_row)
{
/* assert(have_row); */
*moved_from_relpath = NULL;
*moved_from_op_depth = -1;
SVN_ERR(svn_sqlite__reset(stmt));
return SVN_NO_ERROR;
}
m_from_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
m_from_op_depth = svn_sqlite__column_int(stmt, 1);
m_move_from_depth = svn_sqlite__column_int(stmt, 2);
SVN_ERR(svn_sqlite__reset(stmt));
if (! svn_relpath_skip_ancestor(root_relpath, m_from_relpath))
{
*moved_from_relpath = svn_relpath_join(m_from_relpath, suffix,
result_pool);
*moved_from_op_depth = m_from_op_depth; /* ### Ok? */
return SVN_NO_ERROR;
}
else if (!m_move_from_depth)
{
*moved_from_relpath = NULL;
*moved_from_op_depth = -1;
return SVN_NO_ERROR;
}
return svn_error_trace(
resolve_moved_from(moved_from_relpath,
moved_from_op_depth,
wcroot,
root_relpath,
svn_relpath_join(m_from_relpath, suffix,
scratch_pool),
m_move_from_depth,
result_pool, scratch_pool));
}
static svn_error_t *
delete_node(void *baton,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *scratch_pool)
{
struct op_delete_baton_t *b = baton;
svn_wc__db_status_t status;
svn_boolean_t have_row, op_root;
svn_boolean_t add_work = FALSE;
svn_sqlite__stmt_t *stmt;
int working_op_depth; /* Depth of what is to be deleted */
int keep_op_depth = 0; /* Depth of what is below what is deleted */
svn_node_kind_t kind;
apr_array_header_t *moved_nodes = NULL;
int delete_op_depth = relpath_depth(local_relpath);
assert(*local_relpath); /* Can't delete wcroot */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_NODE_INFO));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (!have_row)
{
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
svn_sqlite__reset(stmt),
_("The node '%s' was not found."),
path_for_error_message(wcroot,
local_relpath,
scratch_pool));
}
working_op_depth = svn_sqlite__column_int(stmt, 0);
status = svn_sqlite__column_token(stmt, 3, presence_map);
kind = svn_sqlite__column_token(stmt, 4, kind_map);
if (working_op_depth < delete_op_depth)
{
op_root = FALSE;
add_work = TRUE;
keep_op_depth = working_op_depth;
}
else
{
op_root = TRUE;
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row)
{
svn_wc__db_status_t below_status;
int below_op_depth;
below_op_depth = svn_sqlite__column_int(stmt, 0);
below_status = svn_sqlite__column_token(stmt, 3, presence_map);
if (below_status != svn_wc__db_status_not_present
&& below_status != svn_wc__db_status_base_deleted)
{
add_work = TRUE;
keep_op_depth = below_op_depth;
}
else
keep_op_depth = 0;
}
else
keep_op_depth = -1;
}
SVN_ERR(svn_sqlite__reset(stmt));
if (working_op_depth != 0) /* WORKING */
SVN_ERR(convert_to_working_status(&status, status));
if (status == svn_wc__db_status_deleted
|| status == svn_wc__db_status_not_present)
return SVN_NO_ERROR;
/* Don't copy BASE directories with server excluded nodes */
if (status == svn_wc__db_status_normal && kind == svn_node_dir)
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_HAS_SERVER_EXCLUDED_DESCENDANTS));
SVN_ERR(svn_sqlite__bindf(stmt, "is",
wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row)
{
const char *absent_path = svn_sqlite__column_text(stmt, 0,
scratch_pool);
return svn_error_createf(
SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
svn_sqlite__reset(stmt),
_("Cannot delete '%s' as '%s' is excluded by server"),
path_for_error_message(wcroot, local_relpath,
scratch_pool),
path_for_error_message(wcroot, absent_path,
scratch_pool));
}
SVN_ERR(svn_sqlite__reset(stmt));
}
else if (status == svn_wc__db_status_server_excluded)
{
return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
_("Cannot delete '%s' as it is excluded by server"),
path_for_error_message(wcroot, local_relpath,
scratch_pool));
}
else if (status == svn_wc__db_status_excluded)
{
return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
_("Cannot delete '%s' as it is excluded"),
path_for_error_message(wcroot, local_relpath,
scratch_pool));
}
if (b->moved_to_relpath)
{
const char *moved_from_relpath = NULL;
struct moved_node_t *moved_node;
int move_op_depth;
moved_nodes = apr_array_make(scratch_pool, 1,
sizeof(struct moved_node_t *));
/* The node is being moved-away.
* Figure out if the node was moved-here before, or whether this
* is the first time the node is moved. */
if (status == svn_wc__db_status_added)
SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL,
&moved_from_relpath,
NULL,
&move_op_depth,
wcroot, local_relpath,
scratch_pool, scratch_pool));
if (op_root && moved_from_relpath)
{
const char *part = svn_relpath_skip_ancestor(local_relpath,
moved_from_relpath);
/* Existing move-root is moved to another location */
moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
if (!part)
moved_node->local_relpath = moved_from_relpath;
else
moved_node->local_relpath = svn_relpath_join(b->moved_to_relpath,
part, scratch_pool);
moved_node->op_depth = move_op_depth;
moved_node->moved_to_relpath = b->moved_to_relpath;
moved_node->moved_from_depth = -1;
APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node;
}
else if (!op_root && (status == svn_wc__db_status_normal
|| status == svn_wc__db_status_copied
|| status == svn_wc__db_status_moved_here))
{
/* The node is becoming a move-root for the first time,
* possibly because of a nested move operation. */
moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
moved_node->local_relpath = local_relpath;
moved_node->op_depth = delete_op_depth;
moved_node->moved_to_relpath = b->moved_to_relpath;
moved_node->moved_from_depth = -1;
APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node;
}
/* Else: We can't track history of local additions and/or of things we are
about to delete. */
/* And update all moved_to values still pointing to this location */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_UPDATE_MOVED_TO_DESCENDANTS));
SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
local_relpath,
b->moved_to_relpath));
SVN_ERR(svn_sqlite__update(NULL, stmt));
}
/* Find children that were moved out of the subtree rooted at this node.
* We'll need to update their op-depth columns because their deletion
* is now implied by the deletion of their parent (i.e. this node). */
{
apr_pool_t *iterpool;
int i;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_MOVED_FOR_DELETE));
SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
delete_op_depth));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
iterpool = svn_pool_create(scratch_pool);
while (have_row)
{
struct moved_node_t *mn;
const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
const char *mv_to_relpath = svn_sqlite__column_text(stmt, 1, NULL);
int child_op_depth = svn_sqlite__column_int(stmt, 2);
int moved_from_depth = -1;
svn_boolean_t fixup = FALSE;
if (! b->moved_to_relpath
&& ! svn_relpath_skip_ancestor(local_relpath, mv_to_relpath))
{
/* a NULL moved_here_depth will be reported as 0 */
int moved_here_depth = svn_sqlite__column_int(stmt, 3);
/* Plain delete. Fixup move information of descendants that were
moved here, or that were moved out */
if (moved_here_depth >= delete_op_depth)
{
/* The move we recorded here must be moved to the location
this node had before it was moved here.
This might contain multiple steps when the node was moved
in several places within the to be deleted tree */
/* ### TODO: Add logic */
fixup = TRUE;
moved_from_depth = moved_here_depth;
}
else
{
/* Update the op-depth of an moved away node that was
registered as moved by the records that we are about
to delete */
fixup = TRUE;
child_op_depth = delete_op_depth;
}
}
else if (b->moved_to_relpath)
{
/* The node is moved to a new location */
if (delete_op_depth == child_op_depth)
{
/* Update the op-depth of a tree shadowed by this tree */
fixup = TRUE;
/*child_op_depth = delete_depth;*/
}
else if (child_op_depth >= delete_op_depth
&& !svn_relpath_skip_ancestor(local_relpath,
mv_to_relpath))
{
/* Update the move destination of something that is now moved
away further */
child_relpath = svn_relpath_skip_ancestor(local_relpath,
child_relpath);
if (child_relpath)
{
child_relpath = svn_relpath_join(b->moved_to_relpath,
child_relpath,
scratch_pool);
if (child_op_depth > delete_op_depth
&& svn_relpath_skip_ancestor(local_relpath,
child_relpath))
child_op_depth = delete_op_depth;
else
- child_op_depth = relpath_depth(child_relpath);
+ {
+ /* Calculate depth of the shadowing at the new location */
+ child_op_depth = child_op_depth
+ - relpath_depth(local_relpath)
+ + relpath_depth(b->moved_to_relpath);
+ }
fixup = TRUE;
}
}
}
if (fixup)
{
mn = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
mn->local_relpath = apr_pstrdup(scratch_pool, child_relpath);
mn->moved_to_relpath = apr_pstrdup(scratch_pool, mv_to_relpath);
mn->op_depth = child_op_depth;
mn->moved_from_depth = moved_from_depth;
if (!moved_nodes)
moved_nodes = apr_array_make(scratch_pool, 1,
sizeof(struct moved_node_t *));
APR_ARRAY_PUSH(moved_nodes, struct moved_node_t *) = mn;
}
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
SVN_ERR(svn_sqlite__reset(stmt));
for (i = 0; moved_nodes && (i < moved_nodes->nelts); i++)
{
struct moved_node_t *mn = APR_ARRAY_IDX(moved_nodes, i,
struct moved_node_t *);
if (mn->moved_from_depth > 0)
{
svn_pool_clear(iterpool);
SVN_ERR(resolve_moved_from(&mn->local_relpath, &mn->op_depth,
wcroot, local_relpath,
mn->local_relpath,
mn->moved_from_depth,
scratch_pool, iterpool));
if (!mn->local_relpath)
svn_sort__array_delete(moved_nodes, i--, 1);
}
}
svn_pool_destroy(iterpool);
}
if (!b->moved_to_relpath)
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_CLEAR_MOVED_TO_DESCENDANTS));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
local_relpath));
SVN_ERR(svn_sqlite__update(NULL, stmt));
if (op_root)
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_CLEAR_MOVED_TO_FROM_DEST));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
local_relpath));
SVN_ERR(svn_sqlite__update(NULL, stmt));
}
}
/* ### Put actual-only nodes into the list? */
if (b->notify)
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_INSERT_DELETE_LIST));
SVN_ERR(svn_sqlite__bindf(stmt, "isd",
wcroot->wc_id, local_relpath, working_op_depth));
SVN_ERR(svn_sqlite__step_done(stmt));
}
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
SVN_ERR(svn_sqlite__bindf(stmt, "isd",
wcroot->wc_id, local_relpath, delete_op_depth));
SVN_ERR(svn_sqlite__step_done(stmt));
/* Delete ACTUAL_NODE rows, but leave those that have changelist
and a NODES row. */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
SVN_ERR(svn_sqlite__bindf(stmt, "is",
wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
SVN_ERR(svn_sqlite__bindf(stmt, "is",
wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
if (add_work)
{
/* Delete the node at LOCAL_RELPATH, and possibly mark it as moved. */
/* Delete the node and possible descendants. */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_INSERT_DELETE_FROM_NODE_RECURSIVE));
SVN_ERR(svn_sqlite__bindf(stmt, "isdd",
wcroot->wc_id, local_relpath,
keep_op_depth, delete_op_depth));
SVN_ERR(svn_sqlite__step_done(stmt));
}
if (moved_nodes)
{
int i;
for (i = 0; i < moved_nodes->nelts; ++i)
{
const struct moved_node_t *moved_node
= APR_ARRAY_IDX(moved_nodes, i, void *);
SVN_ERR(delete_update_movedto(wcroot,
moved_node->local_relpath,
moved_node->op_depth,
moved_node->moved_to_relpath,
scratch_pool));
}
}
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_FILE_EXTERNALS));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
b->delete_dir_externals
? STMT_DELETE_EXTERNAL_REGISTATIONS
: STMT_DELETE_FILE_EXTERNAL_REGISTATIONS));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
SVN_ERR(add_work_items(wcroot->sdb, b->work_items, scratch_pool));
if (b->conflict)
SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
b->conflict, scratch_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
op_delete_txn(void *baton,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *scratch_pool)
{
SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
SVN_ERR(delete_node(baton, wcroot, local_relpath, scratch_pool));
return SVN_NO_ERROR;
}
struct op_delete_many_baton_t {
apr_array_header_t *rel_targets;
svn_boolean_t delete_dir_externals;
const svn_skel_t *work_items;
} op_delete_many_baton_t;
static svn_error_t *
op_delete_many_txn(void *baton,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *scratch_pool)
{
struct op_delete_many_baton_t *odmb = baton;
struct op_delete_baton_t odb;
int i;
apr_pool_t *iterpool;
odb.moved_to_relpath = NULL;
odb.conflict = NULL;
odb.work_items = NULL;
odb.delete_dir_externals = odmb->delete_dir_externals;
odb.notify = TRUE;
SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
iterpool = svn_pool_create(scratch_pool);
for (i = 0; i < odmb->rel_targets->nelts; i++)
{
const char *target_relpath = APR_ARRAY_IDX(odmb->rel_targets, i,
const char *);
svn_pool_clear(iterpool);
SVN_ERR(delete_node(&odb, wcroot, target_relpath, iterpool));
}
svn_pool_destroy(iterpool);
SVN_ERR(add_work_items(wcroot->sdb, odmb->work_items, scratch_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
do_delete_notify(void *baton,
svn_wc__db_wcroot_t *wcroot,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
apr_pool_t *iterpool;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_DELETE_LIST));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
iterpool = svn_pool_create(scratch_pool);
while (have_row)
{
const char *notify_relpath;
const char *notify_abspath;
svn_pool_clear(iterpool);
notify_relpath = svn_sqlite__column_text(stmt, 0, NULL);
notify_abspath = svn_dirent_join(wcroot->abspath,
notify_relpath,
iterpool);
notify_func(notify_baton,
svn_wc_create_notify(notify_abspath,
svn_wc_notify_delete,
iterpool),
iterpool);
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
svn_pool_destroy(iterpool);
SVN_ERR(svn_sqlite__reset(stmt));
/* We only allow cancellation after notification for all deleted nodes
* has happened. The nodes are already deleted so we should notify for
* all of them. */
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_op_delete(svn_wc__db_t *db,
const char *local_abspath,
const char *moved_to_abspath,
svn_boolean_t delete_dir_externals,
svn_skel_t *conflict,
svn_skel_t *work_items,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
svn_wc__db_wcroot_t *moved_to_wcroot;
const char *local_relpath;
const char *moved_to_relpath;
struct op_delete_baton_t odb;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
db, local_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
if (moved_to_abspath)
{
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&moved_to_wcroot,
&moved_to_relpath,
db, moved_to_abspath,
scratch_pool,
scratch_pool));
VERIFY_USABLE_WCROOT(moved_to_wcroot);
if (strcmp(wcroot->abspath, moved_to_wcroot->abspath) != 0)
return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Cannot move '%s' to '%s' because they "
"are not in the same working copy"),
svn_dirent_local_style(local_abspath,
scratch_pool),
svn_dirent_local_style(moved_to_abspath,
scratch_pool));
}
else
moved_to_relpath = NULL;
odb.moved_to_relpath = moved_to_relpath;
odb.conflict = conflict;
odb.work_items = work_items;
odb.delete_dir_externals = delete_dir_externals;
if (notify_func)
{
/* Perform the deletion operation (transactionally), perform any
notifications necessary, and then clean out our temporary tables. */
odb.notify = TRUE;
SVN_ERR(with_finalization(wcroot, local_relpath,
op_delete_txn, &odb,
do_delete_notify, NULL,
cancel_func, cancel_baton,
notify_func, notify_baton,
STMT_FINALIZE_DELETE,
scratch_pool));
}
else
{
/* Avoid the trigger work */
odb.notify = FALSE;
SVN_WC__DB_WITH_TXN(
delete_node(&odb, wcroot, local_relpath, scratch_pool),
wcroot);
}
SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_op_delete_many(svn_wc__db_t *db,
apr_array_header_t *targets,
svn_boolean_t delete_dir_externals,
const svn_skel_t *work_items,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
struct op_delete_many_baton_t odmb;
int i;
apr_pool_t *iterpool;
odmb.rel_targets = apr_array_make(scratch_pool, targets->nelts,
sizeof(const char *));
odmb.work_items = work_items;
odmb.delete_dir_externals = delete_dir_externals;
iterpool = svn_pool_create(scratch_pool);
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
db,
APR_ARRAY_IDX(targets, 0,
const char *),
scratch_pool, iterpool));
VERIFY_USABLE_WCROOT(wcroot);
for (i = 0; i < targets->nelts; i++)
{
const char *local_abspath = APR_ARRAY_IDX(targets, i, const char*);
svn_wc__db_wcroot_t *target_wcroot;
svn_pool_clear(iterpool);
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&target_wcroot,
&local_relpath, db,
APR_ARRAY_IDX(targets, i,
const char *),
scratch_pool, iterpool));
VERIFY_USABLE_WCROOT(target_wcroot);
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
/* Assert that all targets are within the same working copy. */
SVN_ERR_ASSERT(wcroot->wc_id == target_wcroot->wc_id);
APR_ARRAY_PUSH(odmb.rel_targets, const char *) = local_relpath;
SVN_ERR(flush_entries(target_wcroot, local_abspath, svn_depth_infinity,
iterpool));
}
svn_pool_destroy(iterpool);
/* Perform the deletion operation (transactionally), perform any
notifications necessary, and then clean out our temporary tables. */
return svn_error_trace(with_finalization(wcroot, wcroot->abspath,
op_delete_many_txn, &odmb,
do_delete_notify, NULL,
cancel_func, cancel_baton,
notify_func, notify_baton,
STMT_FINALIZE_DELETE,
scratch_pool));
}
/* Like svn_wc__db_read_info(), but taking WCROOT+LOCAL_RELPATH instead of
DB+LOCAL_ABSPATH, and outputting repos ids instead of URL+UUID. */
static svn_error_t *
read_info(svn_wc__db_status_t *status,
svn_node_kind_t *kind,
svn_revnum_t *revision,
const char **repos_relpath,
apr_int64_t *repos_id,
svn_revnum_t *changed_rev,
apr_time_t *changed_date,
const char **changed_author,
svn_depth_t *depth,
const svn_checksum_t **checksum,
const char **target,
const char **original_repos_relpath,
apr_int64_t *original_repos_id,
svn_revnum_t *original_revision,
svn_wc__db_lock_t **lock,
svn_filesize_t *recorded_size,
apr_time_t *recorded_time,
const char **changelist,
svn_boolean_t *conflicted,
svn_boolean_t *op_root,
svn_boolean_t *had_props,
svn_boolean_t *props_mod,
svn_boolean_t *have_base,
svn_boolean_t *have_more_work,
svn_boolean_t *have_work,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt_info;
svn_sqlite__stmt_t *stmt_act;
svn_boolean_t have_info;
svn_boolean_t have_act;
svn_error_t *err = NULL;
/* Obtain the most likely to exist record first, to make sure we don't
have to obtain the SQLite read-lock multiple times */
SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
lock ? STMT_SELECT_NODE_INFO_WITH_LOCK
: STMT_SELECT_NODE_INFO));
SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
if (changelist || conflicted || props_mod)
{
SVN_ERR(svn_sqlite__get_statement(&stmt_act, wcroot->sdb,
STMT_SELECT_ACTUAL_NODE));
SVN_ERR(svn_sqlite__bindf(stmt_act, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_act, stmt_act));
}
else
{
have_act = FALSE;
stmt_act = NULL;
}
if (have_info)
{
int op_depth;
svn_node_kind_t node_kind;
op_depth = svn_sqlite__column_int(stmt_info, 0);
node_kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
if (status)
{
*status = svn_sqlite__column_token(stmt_info, 3, presence_map);
if (op_depth != 0) /* WORKING */
err = svn_error_compose_create(err,
convert_to_working_status(status,
*status));
}
if (kind)
{
*kind = node_kind;
}
if (op_depth != 0)
{
if (repos_id)
*repos_id = INVALID_REPOS_ID;
if (revision)
*revision = SVN_INVALID_REVNUM;
if (repos_relpath)
/* Our path is implied by our parent somewhere up the tree.
With the NULL value and status, the caller will know to
search up the tree for the base of our path. */
*repos_relpath = NULL;
}
else
{
/* Fetch repository information. If we have a
WORKING_NODE (and have been added), then the repository
we're being added to will be dependent upon a parent. The
caller can scan upwards to locate the repository. */
repos_location_from_columns(repos_id, revision, repos_relpath,
stmt_info, 1, 5, 2, result_pool);
}
if (changed_rev)
{
*changed_rev = svn_sqlite__column_revnum(stmt_info, 8);
}
if (changed_date)
{
*changed_date = svn_sqlite__column_int64(stmt_info, 9);
}
if (changed_author)
{
*changed_author = svn_sqlite__column_text(stmt_info, 10,
result_pool);
}
if (recorded_time)
{
*recorded_time = svn_sqlite__column_int64(stmt_info, 13);
}
if (depth)
{
if (node_kind != svn_node_dir)
{
*depth = svn_depth_unknown;
}
else
{
*depth = svn_sqlite__column_token_null(stmt_info, 11, depth_map,
svn_depth_unknown);
}
}
if (checksum)
{
if (node_kind != svn_node_file)
{
*checksum = NULL;
}
else
{
err = svn_error_compose_create(
err, svn_sqlite__column_checksum(checksum, stmt_info, 6,
result_pool));
}
}
if (recorded_size)
{
*recorded_size = get_recorded_size(stmt_info, 7);
}
if (target)
{
if (node_kind != svn_node_symlink)
*target = NULL;
else
*target = svn_sqlite__column_text(stmt_info, 12, result_pool);
}
if (changelist)
{
if (have_act)
*changelist = svn_sqlite__column_text(stmt_act, 0, result_pool);
else
*changelist = NULL;
}
if (op_depth == 0)
{
if (original_repos_id)
*original_repos_id = INVALID_REPOS_ID;
if (original_revision)
*original_revision = SVN_INVALID_REVNUM;
if (original_repos_relpath)
*original_repos_relpath = NULL;
}
else
{
repos_location_from_columns(original_repos_id,
original_revision,
original_repos_relpath,
stmt_info, 1, 5, 2, result_pool);
}
if (props_mod)
{
*props_mod = have_act && !svn_sqlite__column_is_null(stmt_act, 1);
}
if (had_props)
{
*had_props = SQLITE_PROPERTIES_AVAILABLE(stmt_info, 14);
}
if (conflicted)
{
if (have_act)
{
*conflicted =
!svn_sqlite__column_is_null(stmt_act, 2); /* conflict_data */
}
else
*conflicted = FALSE;
}
if (lock)
{
if (op_depth != 0)
*lock = NULL;
else
*lock = lock_from_columns(stmt_info, 17, 18, 19, 20, result_pool);
}
if (have_work)
*have_work = (op_depth != 0);
if (op_root)
{
*op_root = ((op_depth > 0)
&& (op_depth == relpath_depth(local_relpath)));
}
if (have_base || have_more_work)
{
if (have_more_work)
*have_more_work = FALSE;
while (!err && op_depth != 0)
{
err = svn_sqlite__step(&have_info, stmt_info);
if (err || !have_info)
break;
op_depth = svn_sqlite__column_int(stmt_info, 0);
if (have_more_work)
{
if (op_depth > 0)
*have_more_work = TRUE;
if (!have_base)
break;
}
}
if (have_base)
*have_base = (op_depth == 0);
}
}
else if (have_act)
{
/* A row in ACTUAL_NODE should never exist without a corresponding
node in BASE_NODE and/or WORKING_NODE unless it flags a tree conflict. */
if (svn_sqlite__column_is_null(stmt_act, 2)) /* conflict_data */
err = svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
_("Corrupt data for '%s'"),
path_for_error_message(wcroot, local_relpath,
scratch_pool));
/* ### What should we return? Should we have a separate
function for reading actual-only nodes? */
/* As a safety measure, until we decide if we want to use
read_info for actual-only nodes, make sure the caller asked
for the conflict status. */
SVN_ERR_ASSERT(conflicted);
if (status)
*status = svn_wc__db_status_normal; /* What! No it's not! */
if (kind)
*kind = svn_node_unknown;
if (revision)
*revision = SVN_INVALID_REVNUM;
if (repos_relpath)
*repos_relpath = NULL;
if (repos_id)
*repos_id = INVALID_REPOS_ID;
if (changed_rev)
*changed_rev = SVN_INVALID_REVNUM;
if (changed_date)
*changed_date = 0;
if (depth)
*depth = svn_depth_unknown;
if (checksum)
*checksum = NULL;
if (target)
*target = NULL;
if (original_repos_relpath)
*original_repos_relpath = NULL;
if (original_repos_id)
*original_repos_id = INVALID_REPOS_ID;
if (original_revision)
*original_revision = SVN_INVALID_REVNUM;
if (lock)
*lock = NULL;
if (recorded_size)
*recorded_size = 0;
if (recorded_time)
*recorded_time = 0;
if (changelist)
*changelist = svn_sqlite__column_text(stmt_act, 0, result_pool);
if (op_root)
*op_root = FALSE;
if (had_props)
*had_props = FALSE;
if (props_mod)
*props_mod = FALSE;
if (conflicted)
*conflicted = TRUE;
if (have_base)
*have_base = FALSE;
if (have_more_work)
*have_more_work = FALSE;
if (have_work)
*have_work = FALSE;
}
else
{
err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("The node '%s' was not found."),
path_for_error_message(wcroot, local_relpath,
scratch_pool));
}
if (stmt_act != NULL)
err = svn_error_compose_create(err, svn_sqlite__reset(stmt_act));
if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
err = svn_error_quick_wrap(err,
apr_psprintf(scratch_pool,
"Error reading node '%s'",
local_relpath));
SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt_info)));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_read_info_internal(svn_wc__db_status_t *status,
svn_node_kind_t *kind,
svn_revnum_t *revision,
const char **repos_relpath,
apr_int64_t *repos_id,
svn_revnum_t *changed_rev,
apr_time_t *changed_date,
const char **changed_author,
svn_depth_t *depth,
const svn_checksum_t **checksum,
const char **target,
const char **original_repos_relpath,
apr_int64_t *original_repos_id,
svn_revnum_t *original_revision,
svn_wc__db_lock_t **lock,
svn_filesize_t *recorded_size,
apr_time_t *recorded_time,
const char **changelist,
svn_boolean_t *conflicted,
svn_boolean_t *op_root,
svn_boolean_t *had_props,
svn_boolean_t *props_mod,
svn_boolean_t *have_base,
svn_boolean_t *have_more_work,
svn_boolean_t *have_work,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
return svn_error_trace(
read_info(status, kind, revision, repos_relpath, repos_id,
changed_rev, changed_date, changed_author,
depth, checksum, target, original_repos_relpath,
original_repos_id, original_revision, lock,
recorded_size, recorded_time, changelist, conflicted,
op_root, had_props, props_mod,
have_base, have_more_work, have_work,
wcroot, local_relpath, result_pool, scratch_pool));
}
svn_error_t *
svn_wc__db_read_info(svn_wc__db_status_t *status,
svn_node_kind_t *kind,
svn_revnum_t *revision,
const char **repos_relpath,
const char **repos_root_url,
const char **repos_uuid,
svn_revnum_t *changed_rev,
apr_time_t *changed_date,
const char **changed_author,
svn_depth_t *depth,
const svn_checksum_t **checksum,
const char **target,
const char **original_repos_relpath,
const char **original_root_url,
const char **original_uuid,
svn_revnum_t *original_revision,
svn_wc__db_lock_t **lock,
svn_filesize_t *recorded_size,
apr_time_t *recorded_time,
const char **changelist,
svn_boolean_t *conflicted,
svn_boolean_t *op_root,
svn_boolean_t *have_props,
svn_boolean_t *props_mod,
svn_boolean_t *have_base,
svn_boolean_t *have_more_work,
svn_boolean_t *have_work,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
apr_int64_t repos_id, original_repos_id;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
- SVN_ERR(read_info(status, kind, revision, repos_relpath, &repos_id,
+ SVN_WC__DB_WITH_TXN4(
+ read_info(status, kind, revision, repos_relpath, &repos_id,
changed_rev, changed_date, changed_author,
depth, checksum, target, original_repos_relpath,
&original_repos_id, original_revision, lock,
recorded_size, recorded_time, changelist, conflicted,
op_root, have_props, props_mod,
have_base, have_more_work, have_work,
- wcroot, local_relpath, result_pool, scratch_pool));
- SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
- wcroot->sdb, repos_id, result_pool));
- SVN_ERR(svn_wc__db_fetch_repos_info(original_root_url, original_uuid,
+ wcroot, local_relpath, result_pool, scratch_pool),
+ svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
+ wcroot->sdb, repos_id, result_pool),
+ svn_wc__db_fetch_repos_info(original_root_url, original_uuid,
wcroot->sdb, original_repos_id,
- result_pool));
+ result_pool),
+ SVN_NO_ERROR,
+ wcroot);
return SVN_NO_ERROR;
}
static svn_error_t *
is_wclocked(svn_boolean_t *locked,
svn_wc__db_wcroot_t *wcroot,
const char *dir_relpath,
apr_pool_t *scratch_pool);
/* What we really want to store about a node. This relies on the
offset of svn_wc__db_info_t being zero. */
struct read_children_info_item_t
{
struct svn_wc__db_info_t info;
int op_depth;
int nr_layers;
};
static svn_error_t *
read_children_info(svn_wc__db_wcroot_t *wcroot,
const char *dir_relpath,
apr_hash_t *conflicts,
apr_hash_t *nodes,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
const char *repos_root_url = NULL;
const char *repos_uuid = NULL;
apr_int64_t last_repos_id = INVALID_REPOS_ID;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_NODE_CHILDREN_INFO));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
while (have_row)
{
/* CHILD item points to what we have about the node. We only provide
CHILD->item to our caller. */
struct read_children_info_item_t *child_item;
const char *child_relpath = svn_sqlite__column_text(stmt, 19, NULL);
const char *name = svn_relpath_basename(child_relpath, NULL);
svn_error_t *err;
int op_depth;
svn_boolean_t new_child;
child_item = svn_hash_gets(nodes, name);
if (child_item)
new_child = FALSE;
else
{
child_item = apr_pcalloc(result_pool, sizeof(*child_item));
new_child = TRUE;
}
op_depth = svn_sqlite__column_int(stmt, 0);
/* Do we have new or better information? */
if (new_child || op_depth > child_item->op_depth)
{
struct svn_wc__db_info_t *child = &child_item->info;
child_item->op_depth = op_depth;
child->kind = svn_sqlite__column_token(stmt, 4, kind_map);
child->status = svn_sqlite__column_token(stmt, 3, presence_map);
if (op_depth != 0)
{
if (child->status == svn_wc__db_status_incomplete)
child->incomplete = TRUE;
err = convert_to_working_status(&child->status, child->status);
if (err)
SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
}
if (op_depth != 0)
child->revnum = SVN_INVALID_REVNUM;
else
child->revnum = svn_sqlite__column_revnum(stmt, 5);
if (op_depth != 0)
child->repos_relpath = NULL;
else
child->repos_relpath = svn_sqlite__column_text(stmt, 2,
result_pool);
if (op_depth != 0 || svn_sqlite__column_is_null(stmt, 1))
{
child->repos_root_url = NULL;
child->repos_uuid = NULL;
}
else
{
const char *last_repos_root_url = NULL;
apr_int64_t repos_id = svn_sqlite__column_int64(stmt, 1);
if (!repos_root_url ||
(last_repos_id != INVALID_REPOS_ID &&
repos_id != last_repos_id))
{
last_repos_root_url = repos_root_url;
err = svn_wc__db_fetch_repos_info(&repos_root_url,
&repos_uuid,
wcroot->sdb, repos_id,
result_pool);
if (err)
SVN_ERR(svn_error_compose_create(err,
svn_sqlite__reset(stmt)));
}
if (last_repos_id == INVALID_REPOS_ID)
last_repos_id = repos_id;
/* Assume working copy is all one repos_id so that a
single cached value is sufficient. */
if (repos_id != last_repos_id)
{
err= svn_error_createf(
SVN_ERR_WC_DB_ERROR, NULL,
_("The node '%s' comes from unexpected repository "
"'%s', expected '%s'; if this node is a file "
"external using the correct URL in the external "
"definition can fix the problem, see issue #4087"),
child_relpath, repos_root_url, last_repos_root_url);
return svn_error_compose_create(err, svn_sqlite__reset(stmt));
}
child->repos_root_url = repos_root_url;
child->repos_uuid = repos_uuid;
}
child->changed_rev = svn_sqlite__column_revnum(stmt, 8);
child->changed_date = svn_sqlite__column_int64(stmt, 9);
child->changed_author = svn_sqlite__column_text(stmt, 10,
result_pool);
if (child->kind != svn_node_dir)
child->depth = svn_depth_unknown;
else
{
child->depth = svn_sqlite__column_token_null(stmt, 11, depth_map,
svn_depth_unknown);
if (new_child)
SVN_ERR(is_wclocked(&child->locked, wcroot, child_relpath,
scratch_pool));
}
child->recorded_time = svn_sqlite__column_int64(stmt, 13);
child->recorded_size = get_recorded_size(stmt, 7);
child->has_checksum = !svn_sqlite__column_is_null(stmt, 6);
child->copied = op_depth > 0 && !svn_sqlite__column_is_null(stmt, 2);
child->had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 14);
#ifdef HAVE_SYMLINK
if (child->had_props)
{
apr_hash_t *properties;
err = svn_sqlite__column_properties(&properties, stmt, 14,
scratch_pool, scratch_pool);
if (err)
SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
child->special = (child->had_props
&& svn_hash_gets(properties, SVN_PROP_SPECIAL));
}
#endif
if (op_depth == 0)
child->op_root = FALSE;
else
child->op_root = (op_depth == relpath_depth(child_relpath));
if (op_depth && child->op_root)
child_item->info.moved_here = svn_sqlite__column_boolean(stmt, 20);
if (new_child)
svn_hash_sets(nodes, apr_pstrdup(result_pool, name), child);
}
if (op_depth == 0)
{
child_item->info.have_base = TRUE;
/* Get the lock info, available only at op_depth 0. */
child_item->info.lock = lock_from_columns(stmt, 15, 16, 17, 18,
result_pool);
/* FILE_EXTERNAL flag only on op_depth 0. */
child_item->info.file_external = svn_sqlite__column_boolean(stmt,
22);
}
else
{
const char *moved_to_relpath;
child_item->nr_layers++;
child_item->info.have_more_work = (child_item->nr_layers > 1);
/* A local_relpath can be moved multiple times at different op
depths and it really depends on the caller what is interesting.
We provide a simple linked list with the moved_from information */
moved_to_relpath = svn_sqlite__column_text(stmt, 21, NULL);
if (moved_to_relpath)
{
struct svn_wc__db_moved_to_info_t *moved_to;
struct svn_wc__db_moved_to_info_t **next;
const char *shadow_op_relpath;
int cur_op_depth;
moved_to = apr_pcalloc(result_pool, sizeof(*moved_to));
moved_to->moved_to_abspath = svn_dirent_join(wcroot->abspath,
moved_to_relpath,
result_pool);
cur_op_depth = relpath_depth(child_relpath);
shadow_op_relpath = child_relpath;
while (cur_op_depth > op_depth)
{
shadow_op_relpath = svn_relpath_dirname(shadow_op_relpath,
scratch_pool);
cur_op_depth--;
}
moved_to->shadow_op_root_abspath =
svn_dirent_join(wcroot->abspath, shadow_op_relpath,
result_pool);
next = &child_item->info.moved_to;
while (*next &&
0 < strcmp((*next)->shadow_op_root_abspath,
moved_to->shadow_op_root_abspath))
next = &((*next)->next);
moved_to->next = *next;
*next = moved_to;
}
}
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
SVN_ERR(svn_sqlite__reset(stmt));
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_ACTUAL_CHILDREN_INFO));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
while (have_row)
{
struct read_children_info_item_t *child_item;
struct svn_wc__db_info_t *child;
const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
const char *name = svn_relpath_basename(child_relpath, NULL);
child_item = svn_hash_gets(nodes, name);
if (!child_item)
{
child_item = apr_pcalloc(result_pool, sizeof(*child_item));
child_item->info.status = svn_wc__db_status_not_present;
}
child = &child_item->info;
child->changelist = svn_sqlite__column_text(stmt, 1, result_pool);
child->props_mod = !svn_sqlite__column_is_null(stmt, 2);
#ifdef HAVE_SYMLINK
if (child->props_mod)
{
svn_error_t *err;
apr_hash_t *properties;
err = svn_sqlite__column_properties(&properties, stmt, 2,
scratch_pool, scratch_pool);
if (err)
SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
child->special = (NULL != svn_hash_gets(properties,
SVN_PROP_SPECIAL));
}
#endif
child->conflicted = !svn_sqlite__column_is_null(stmt, 3); /* conflict */
if (child->conflicted)
svn_hash_sets(conflicts, apr_pstrdup(result_pool, name), "");
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
SVN_ERR(svn_sqlite__reset(stmt));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_read_children_info(apr_hash_t **nodes,
apr_hash_t **conflicts,
svn_wc__db_t *db,
const char *dir_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *dir_relpath;
*conflicts = apr_hash_make(result_pool);
*nodes = apr_hash_make(result_pool);
SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, db,
dir_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_WC__DB_WITH_TXN(
read_children_info(wcroot, dir_relpath, *conflicts, *nodes,
result_pool, scratch_pool),
wcroot);
return SVN_NO_ERROR;
}
static svn_error_t *
db_read_props(apr_hash_t **props,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
static svn_error_t *
read_single_info(const struct svn_wc__db_info_t **info,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
struct svn_wc__db_info_t *mtb;
apr_int64_t repos_id;
const svn_checksum_t *checksum;
const char *original_repos_relpath;
svn_boolean_t have_work;
mtb = apr_pcalloc(result_pool, sizeof(*mtb));
SVN_ERR(read_info(&mtb->status, &mtb->kind, &mtb->revnum,
&mtb->repos_relpath, &repos_id, &mtb->changed_rev,
&mtb->changed_date, &mtb->changed_author, &mtb->depth,
&checksum, NULL, &original_repos_relpath, NULL, NULL,
&mtb->lock, &mtb->recorded_size, &mtb->recorded_time,
&mtb->changelist, &mtb->conflicted, &mtb->op_root,
&mtb->had_props, &mtb->props_mod, &mtb->have_base,
&mtb->have_more_work, &have_work,
wcroot, local_relpath,
result_pool, scratch_pool));
/* Query the same rows in the database again for move information */
if (have_work && (mtb->have_base || mtb->have_more_work))
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
const char *cur_relpath = NULL;
int cur_op_depth;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_MOVED_TO_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
while (have_row)
{
struct svn_wc__db_moved_to_info_t *move;
int op_depth = svn_sqlite__column_int(stmt, 0);
const char *moved_to_relpath = svn_sqlite__column_text(stmt, 1, NULL);
move = apr_pcalloc(result_pool, sizeof(*move));
move->moved_to_abspath = svn_dirent_join(wcroot->abspath,
moved_to_relpath,
result_pool);
if (!cur_relpath)
{
cur_relpath = local_relpath;
cur_op_depth = relpath_depth(cur_relpath);
}
while (cur_op_depth > op_depth)
{
cur_relpath = svn_relpath_dirname(cur_relpath, scratch_pool);
cur_op_depth--;
}
move->shadow_op_root_abspath = svn_dirent_join(wcroot->abspath,
cur_relpath,
result_pool);
move->next = mtb->moved_to;
mtb->moved_to = move;
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
SVN_ERR(svn_sqlite__reset(stmt));
}
/* Maybe we have to get some shadowed lock from BASE to make our test suite
happy... (It might be completely unrelated, but...)
This queries the same BASE row again, joined to the lock table */
if (mtb->have_base && (have_work || mtb->kind == svn_node_file))
{
svn_boolean_t update_root;
svn_wc__db_lock_t **lock_arg = NULL;
if (have_work)
lock_arg = &mtb->lock;
SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
NULL, lock_arg, NULL, NULL,
&update_root,
wcroot, local_relpath,
result_pool, scratch_pool));
mtb->file_external = (update_root && mtb->kind == svn_node_file);
}
if (mtb->status == svn_wc__db_status_added)
{
svn_wc__db_status_t status;
SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL,
wcroot, local_relpath,
result_pool, scratch_pool));
mtb->moved_here = (status == svn_wc__db_status_moved_here);
mtb->incomplete = (status == svn_wc__db_status_incomplete);
}
#ifdef HAVE_SYMLINK
if (mtb->kind == svn_node_file
&& (mtb->had_props || mtb->props_mod))
{
apr_hash_t *properties;
if (mtb->props_mod)
SVN_ERR(db_read_props(&properties,
wcroot, local_relpath,
scratch_pool, scratch_pool));
else
SVN_ERR(db_read_pristine_props(&properties, wcroot, local_relpath,
TRUE /* deleted_ok */,
scratch_pool, scratch_pool));
mtb->special = (NULL != svn_hash_gets(properties, SVN_PROP_SPECIAL));
}
#endif
mtb->has_checksum = (checksum != NULL);
mtb->copied = (original_repos_relpath != NULL);
SVN_ERR(svn_wc__db_fetch_repos_info(&mtb->repos_root_url, &mtb->repos_uuid,
wcroot->sdb, repos_id, result_pool));
if (mtb->kind == svn_node_dir)
SVN_ERR(is_wclocked(&mtb->locked, wcroot, local_relpath, scratch_pool));
*info = mtb;
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_read_single_info(const struct svn_wc__db_info_t **info,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_WC__DB_WITH_TXN(read_single_info(info, wcroot, local_relpath,
result_pool, scratch_pool),
wcroot);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_read_pristine_info(svn_wc__db_status_t *status,
svn_node_kind_t *kind,
svn_revnum_t *changed_rev,
apr_time_t *changed_date,
const char **changed_author,
svn_depth_t *depth, /* dirs only */
const svn_checksum_t **checksum, /* files only */
const char **target, /* symlinks only */
svn_boolean_t *had_props,
apr_hash_t **props,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
svn_error_t *err = NULL;
int op_depth;
svn_wc__db_status_t raw_status;
svn_node_kind_t node_kind;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
/* Obtain the most likely to exist record first, to make sure we don't
have to obtain the SQLite read-lock multiple times */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_NODE_INFO));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (!have_row)
{
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
svn_sqlite__reset(stmt),
_("The node '%s' was not found."),
path_for_error_message(wcroot,
local_relpath,
scratch_pool));
}
op_depth = svn_sqlite__column_int(stmt, 0);
raw_status = svn_sqlite__column_token(stmt, 3, presence_map);
if (op_depth > 0 && raw_status == svn_wc__db_status_base_deleted)
{
SVN_ERR(svn_sqlite__step_row(stmt));
op_depth = svn_sqlite__column_int(stmt, 0);
raw_status = svn_sqlite__column_token(stmt, 3, presence_map);
}
node_kind = svn_sqlite__column_token(stmt, 4, kind_map);
if (status)
{
if (op_depth > 0)
{
err = svn_error_compose_create(err,
convert_to_working_status(
status,
raw_status));
}
else
*status = raw_status;
}
if (kind)
{
*kind = node_kind;
}
if (changed_rev)
{
*changed_rev = svn_sqlite__column_revnum(stmt, 8);
}
if (changed_date)
{
*changed_date = svn_sqlite__column_int64(stmt, 9);
}
if (changed_author)
{
*changed_author = svn_sqlite__column_text(stmt, 10,
result_pool);
}
if (depth)
{
if (node_kind != svn_node_dir)
{
*depth = svn_depth_unknown;
}
else
{
*depth = svn_sqlite__column_token_null(stmt, 11, depth_map,
svn_depth_unknown);
}
}
if (checksum)
{
if (node_kind != svn_node_file)
{
*checksum = NULL;
}
else
{
svn_error_t *err2;
err2 = svn_sqlite__column_checksum(checksum, stmt, 6, result_pool);
if (err2 != NULL)
{
if (err)
err = svn_error_compose_create(
err,
svn_error_createf(
err->apr_err, err2,
_("The node '%s' has a corrupt checksum value."),
path_for_error_message(wcroot, local_relpath,
scratch_pool)));
else
err = err2;
}
}
}
if (target)
{
if (node_kind != svn_node_symlink)
*target = NULL;
else
*target = svn_sqlite__column_text(stmt, 12, result_pool);
}
if (had_props)
{
*had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 14);
}
if (props)
{
if (raw_status == svn_wc__db_status_normal
|| raw_status == svn_wc__db_status_incomplete)
{
SVN_ERR(svn_sqlite__column_properties(props, stmt, 14,
result_pool, scratch_pool));
if (*props == NULL)
*props = apr_hash_make(result_pool);
}
else
{
assert(svn_sqlite__column_is_null(stmt, 14));
*props = NULL;
}
}
return svn_error_trace(
svn_error_compose_create(err,
svn_sqlite__reset(stmt)));
}
svn_error_t *
svn_wc__db_read_children_walker_info(apr_hash_t **nodes,
svn_wc__db_t *db,
const char *dir_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *dir_relpath;
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, db,
dir_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_NODE_CHILDREN_WALKER_INFO));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
*nodes = apr_hash_make(result_pool);
while (have_row)
{
struct svn_wc__db_walker_info_t *child;
const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
const char *name = svn_relpath_basename(child_relpath, NULL);
int op_depth = svn_sqlite__column_int(stmt, 1);
svn_error_t *err;
child = apr_palloc(result_pool, sizeof(*child));
child->status = svn_sqlite__column_token(stmt, 2, presence_map);
if (op_depth > 0)
{
err = convert_to_working_status(&child->status, child->status);
if (err)
SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
}
child->kind = svn_sqlite__column_token(stmt, 3, kind_map);
svn_hash_sets(*nodes, apr_pstrdup(result_pool, name), child);
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
SVN_ERR(svn_sqlite__reset(stmt));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_read_node_install_info(const char **wcroot_abspath,
const svn_checksum_t **sha1_checksum,
apr_hash_t **pristine_props,
apr_time_t *changed_date,
svn_wc__db_t *db,
const char *local_abspath,
const char *wri_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
svn_sqlite__stmt_t *stmt;
svn_error_t *err = NULL;
svn_boolean_t have_row;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
if (!wri_abspath)
wri_abspath = local_abspath;
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
wri_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
if (local_abspath != wri_abspath
&& strcmp(local_abspath, wri_abspath))
{
if (!svn_dirent_is_ancestor(wcroot->abspath, local_abspath))
return svn_error_createf(
SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("The node '%s' is not in working copy '%s'"),
svn_dirent_local_style(local_abspath, scratch_pool),
svn_dirent_local_style(wcroot->abspath, scratch_pool));
local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
}
if (wcroot_abspath != NULL)
*wcroot_abspath = apr_pstrdup(result_pool, wcroot->abspath);
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_NODE_INFO));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row)
{
if (!err && sha1_checksum)
err = svn_sqlite__column_checksum(sha1_checksum, stmt, 6, result_pool);
if (!err && pristine_props)
{
err = svn_sqlite__column_properties(pristine_props, stmt, 14,
result_pool, scratch_pool);
/* Null means no props (assuming presence normal or incomplete). */
if (*pristine_props == NULL)
*pristine_props = apr_hash_make(result_pool);
}
if (changed_date)
*changed_date = svn_sqlite__column_int64(stmt, 9);
}
else
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
svn_sqlite__reset(stmt),
_("The node '%s' is not installable"),
svn_dirent_local_style(local_abspath,
scratch_pool));
SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
return SVN_NO_ERROR;
}
/* The body of svn_wc__db_read_url().
*/
static svn_error_t *
read_url_txn(const char **url,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_status_t status;
const char *repos_relpath;
const char *repos_root_url;
apr_int64_t repos_id;
svn_boolean_t have_base;
SVN_ERR(read_info(&status, NULL, NULL, &repos_relpath, &repos_id, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
&have_base, NULL, NULL,
wcroot, local_relpath, scratch_pool, scratch_pool));
if (repos_relpath == NULL)
{
if (status == svn_wc__db_status_added)
{
SVN_ERR(scan_addition(NULL, NULL, &repos_relpath, &repos_id, NULL,
NULL, NULL, NULL, NULL, NULL,
wcroot, local_relpath,
scratch_pool, scratch_pool));
}
else if (status == svn_wc__db_status_deleted)
{
const char *base_del_relpath;
const char *work_del_relpath;
SVN_ERR(scan_deletion_txn(&base_del_relpath, NULL,
&work_del_relpath,
NULL, wcroot,
local_relpath,
scratch_pool,
scratch_pool));
if (base_del_relpath)
{
SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
&repos_relpath,
&repos_id,
NULL, NULL, NULL,
NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
wcroot,
base_del_relpath,
scratch_pool,
scratch_pool));
repos_relpath = svn_relpath_join(
repos_relpath,
svn_dirent_skip_ancestor(base_del_relpath,
local_relpath),
scratch_pool);
}
else
{
/* The parent of the WORKING delete, must be an addition */
const char *work_relpath = NULL;
/* work_del_relpath should not be NULL. However, we have
* observed instances where that assumption was not met.
* Bail out in that case instead of crashing with a segfault.
*/
SVN_ERR_ASSERT(work_del_relpath != NULL);
work_relpath = svn_relpath_dirname(work_del_relpath,
scratch_pool);
SVN_ERR(scan_addition(NULL, NULL, &repos_relpath, &repos_id,
NULL, NULL, NULL, NULL, NULL, NULL,
wcroot, work_relpath,
scratch_pool, scratch_pool));
repos_relpath = svn_relpath_join(
repos_relpath,
svn_dirent_skip_ancestor(work_relpath,
local_relpath),
scratch_pool);
}
}
else if (status == svn_wc__db_status_excluded)
{
const char *parent_relpath;
const char *name;
const char *url2;
/* Set 'url' to the *full URL* of the parent WC dir,
* and 'name' to the *single path component* that is the
* basename of this WC directory, so that joining them will result
* in the correct full URL. */
svn_relpath_split(&parent_relpath, &name, local_relpath,
scratch_pool);
SVN_ERR(read_url_txn(&url2, wcroot, parent_relpath,
scratch_pool, scratch_pool));
*url = svn_path_url_add_component2(url2, name, result_pool);
return SVN_NO_ERROR;
}
else
{
/* All working statee are explicitly handled and all base statee
have a repos_relpath */
SVN_ERR_MALFUNCTION();
}
}
SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, NULL, wcroot->sdb,
repos_id, scratch_pool));
SVN_ERR_ASSERT(repos_root_url != NULL && repos_relpath != NULL);
*url = svn_path_url_add_component2(repos_root_url, repos_relpath,
result_pool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_read_url(const char **url,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_WC__DB_WITH_TXN(read_url_txn(url, wcroot, local_relpath,
result_pool, scratch_pool),
wcroot);
return SVN_NO_ERROR;
}
/* Call RECEIVER_FUNC, passing RECEIVER_BATON, an absolute path, and
a hash table mapping <tt>char *</tt> names onto svn_string_t *
values for any properties of immediate or recursive child nodes of
LOCAL_ABSPATH, the actual query being determined by STMT_IDX.
If FILES_ONLY is true, only report properties for file child nodes.
Check for cancellation between calls of RECEIVER_FUNC.
*/
typedef struct cache_props_baton_t
{
svn_depth_t depth;
svn_boolean_t pristine;
const apr_array_header_t *changelists;
svn_cancel_func_t cancel_func;
void *cancel_baton;
} cache_props_baton_t;
static svn_error_t *
cache_props_recursive(void *cb_baton,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *scratch_pool)
{
cache_props_baton_t *baton = cb_baton;
svn_sqlite__stmt_t *stmt;
int stmt_idx;
SVN_ERR(populate_targets_tree(wcroot, local_relpath, baton->depth,
baton->changelists, scratch_pool));
SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
STMT_CREATE_TARGET_PROP_CACHE));
if (baton->pristine)
stmt_idx = STMT_CACHE_TARGET_PRISTINE_PROPS;
else
stmt_idx = STMT_CACHE_TARGET_PROPS;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
SVN_ERR(svn_sqlite__bind_int64(stmt, 1, wcroot->wc_id));
SVN_ERR(svn_sqlite__step_done(stmt));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_read_props_streamily(svn_wc__db_t *db,
const char *local_abspath,
svn_depth_t depth,
svn_boolean_t pristine,
const apr_array_header_t *changelists,
svn_wc__proplist_receiver_t receiver_func,
void *receiver_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
svn_sqlite__stmt_t *stmt;
cache_props_baton_t baton;
svn_boolean_t have_row;
apr_pool_t *iterpool;
svn_error_t *err = NULL;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR_ASSERT(receiver_func);
SVN_ERR_ASSERT((depth == svn_depth_files) ||
(depth == svn_depth_immediates) ||
(depth == svn_depth_infinity));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
db, local_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
baton.depth = depth;
baton.pristine = pristine;
baton.changelists = changelists;
baton.cancel_func = cancel_func;
baton.cancel_baton = cancel_baton;
SVN_ERR(with_finalization(wcroot, local_relpath,
cache_props_recursive, &baton,
NULL, NULL,
cancel_func, cancel_baton,
NULL, NULL,
STMT_DROP_TARGETS_LIST,
scratch_pool));
iterpool = svn_pool_create(scratch_pool);
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_ALL_TARGET_PROP_CACHE));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
while (!err && have_row)
{
apr_hash_t *props;
svn_pool_clear(iterpool);
SVN_ERR(svn_sqlite__column_properties(&props, stmt, 1, iterpool,
iterpool));
/* see if someone wants to cancel this operation. */
if (cancel_func)
err = cancel_func(cancel_baton);
if (!err && props && apr_hash_count(props) != 0)
{
const char *child_relpath;
const char *child_abspath;
child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
child_abspath = svn_dirent_join(wcroot->abspath,
child_relpath, iterpool);
err = receiver_func(receiver_baton, child_abspath, props, iterpool);
}
err = svn_error_compose_create(err, svn_sqlite__step(&have_row, stmt));
}
err = svn_error_compose_create(err, svn_sqlite__reset(stmt));
svn_pool_destroy(iterpool);
SVN_ERR(svn_error_compose_create(
err,
svn_sqlite__exec_statements(wcroot->sdb,
STMT_DROP_TARGET_PROP_CACHE)));
return SVN_NO_ERROR;
}
/* Helper for svn_wc__db_read_props().
*/
static svn_error_t *
db_read_props(apr_hash_t **props,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
svn_error_t *err = NULL;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_ACTUAL_PROPS));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row && !svn_sqlite__column_is_null(stmt, 0))
{
err = svn_sqlite__column_properties(props, stmt, 0,
result_pool, scratch_pool);
}
else
have_row = FALSE;
SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
if (have_row)
return SVN_NO_ERROR;
/* No local changes. Return the pristine props for this node. */
SVN_ERR(db_read_pristine_props(props, wcroot, local_relpath, FALSE,
result_pool, scratch_pool));
if (*props == NULL)
{
/* Pristine properties are not defined for this node.
### we need to determine whether this node is in a state that
### allows for ACTUAL properties (ie. not deleted). for now,
### just say all nodes, no matter the state, have at least an
### empty set of props. */
*props = apr_hash_make(result_pool);
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_read_props(apr_hash_t **props,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_WC__DB_WITH_TXN(db_read_props(props, wcroot, local_relpath,
result_pool, scratch_pool),
wcroot);
return SVN_NO_ERROR;
}
static svn_error_t *
db_read_pristine_props(apr_hash_t **props,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
svn_boolean_t deleted_ok,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
svn_wc__db_status_t presence;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_NODE_PROPS));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (!have_row)
{
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
svn_sqlite__reset(stmt),
_("The node '%s' was not found."),
path_for_error_message(wcroot,
local_relpath,
scratch_pool));
}
/* Examine the presence: */
presence = svn_sqlite__column_token(stmt, 1, presence_map);
/* For "base-deleted", it is obvious the pristine props are located
below the current node. Fetch the NODE from the next record. */
if (presence == svn_wc__db_status_base_deleted && deleted_ok)
{
SVN_ERR(svn_sqlite__step(&have_row, stmt));
SVN_ERR_ASSERT(have_row);
presence = svn_sqlite__column_token(stmt, 1, presence_map);
}
/* normal or copied: Fetch properties (during update we want
properties for incomplete as well) */
if (presence == svn_wc__db_status_normal
|| presence == svn_wc__db_status_incomplete)
{
svn_error_t *err;
err = svn_sqlite__column_properties(props, stmt, 0, result_pool,
scratch_pool);
SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
if (!*props)
*props = apr_hash_make(result_pool);
return SVN_NO_ERROR;
}
return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
svn_sqlite__reset(stmt),
_("The node '%s' has a status that"
" has no properties."),
path_for_error_message(wcroot,
local_relpath,
scratch_pool));
}
svn_error_t *
svn_wc__db_read_pristine_props(apr_hash_t **props,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_ERR(db_read_pristine_props(props, wcroot, local_relpath, TRUE,
result_pool, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_prop_retrieve_recursive(apr_hash_t **values,
svn_wc__db_t *db,
const char *local_abspath,
const char *propname,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
apr_pool_t *iterpool;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_CURRENT_PROPS_RECURSIVE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
*values = apr_hash_make(result_pool);
SVN_ERR(svn_sqlite__step(&have_row, stmt));
iterpool = svn_pool_create(scratch_pool);
while (have_row)
{
apr_hash_t *node_props;
svn_string_t *value;
svn_pool_clear(iterpool);
SVN_ERR(svn_sqlite__column_properties(&node_props, stmt, 0,
iterpool, iterpool));
value = (node_props
? svn_hash_gets(node_props, propname)
: NULL);
if (value)
{
svn_hash_sets(*values,
svn_dirent_join(wcroot->abspath,
svn_sqlite__column_text(stmt, 1, NULL),
result_pool),
svn_string_dup(value, result_pool));
}
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
svn_pool_destroy(iterpool);
return svn_error_trace(svn_sqlite__reset(stmt));
}
/* The body of svn_wc__db_read_cached_iprops(). */
static svn_error_t *
db_read_cached_iprops(apr_array_header_t **iprops,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_IPROPS));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (!have_row)
{
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
svn_sqlite__reset(stmt),
_("The node '%s' was not found."),
path_for_error_message(wcroot, local_relpath,
scratch_pool));
}
SVN_ERR(svn_sqlite__column_iprops(iprops, stmt, 0,
result_pool, scratch_pool));
SVN_ERR(svn_sqlite__reset(stmt));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_read_cached_iprops(apr_array_header_t **iprops,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
db, local_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
/* Don't use with_txn yet, as we perform just a single transaction */
SVN_ERR(db_read_cached_iprops(iprops, wcroot, local_relpath,
result_pool, scratch_pool));
if (!*iprops)
{
*iprops = apr_array_make(result_pool, 0,
sizeof(svn_prop_inherited_item_t *));
}
return SVN_NO_ERROR;
}
/* Remove all prop name value pairs from PROP_HASH where the property
name is not PROPNAME. */
static void
filter_unwanted_props(apr_hash_t *prop_hash,
const char * propname,
apr_pool_t *scratch_pool)
{
apr_hash_index_t *hi;
for (hi = apr_hash_first(scratch_pool, prop_hash);
hi;
hi = apr_hash_next(hi))
{
const char *ipropname = svn__apr_hash_index_key(hi);
if (strcmp(ipropname, propname) != 0)
svn_hash_sets(prop_hash, ipropname, NULL);
}
return;
}
/* Get the changed properties as stored in the ACTUAL table */
static svn_error_t *
db_get_changed_props(apr_hash_t **actual_props,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_ACTUAL_PROPS));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row && !svn_sqlite__column_is_null(stmt, 0))
SVN_ERR(svn_sqlite__column_properties(actual_props, stmt, 0,
result_pool, scratch_pool));
else
*actual_props = NULL; /* Cached when we read that record */
return svn_error_trace(svn_sqlite__reset(stmt));
}
/* The body of svn_wc__db_read_inherited_props(). */
static svn_error_t *
db_read_inherited_props(apr_array_header_t **inherited_props,
apr_hash_t **actual_props,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
const char *propname,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
int i;
apr_array_header_t *cached_iprops = NULL;
apr_array_header_t *iprops;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
svn_sqlite__stmt_t *stmt;
const char *relpath;
const char *expected_parent_repos_relpath = NULL;
const char *parent_relpath;
iprops = apr_array_make(result_pool, 1,
sizeof(svn_prop_inherited_item_t *));
*inherited_props = iprops;
if (actual_props)
*actual_props = NULL;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_NODE_INFO));
relpath = local_relpath;
/* Walk up to the root of the WC looking for inherited properties. When we
reach the WC root also check for cached inherited properties. */
for (relpath = local_relpath; relpath; relpath = parent_relpath)
{
svn_boolean_t have_row;
int op_depth;
svn_wc__db_status_t status;
apr_hash_t *node_props;
parent_relpath = relpath[0] ? svn_relpath_dirname(relpath, scratch_pool)
: NULL;
svn_pool_clear(iterpool);
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (!have_row)
return svn_error_createf(
SVN_ERR_WC_PATH_NOT_FOUND, svn_sqlite__reset(stmt),
_("The node '%s' was not found."),
path_for_error_message(wcroot, relpath,
scratch_pool));
op_depth = svn_sqlite__column_int(stmt, 0);
status = svn_sqlite__column_token(stmt, 3, presence_map);
if (status != svn_wc__db_status_normal
&& status != svn_wc__db_status_incomplete)
return svn_error_createf(
SVN_ERR_WC_PATH_UNEXPECTED_STATUS, svn_sqlite__reset(stmt),
_("The node '%s' has a status that has no properties."),
path_for_error_message(wcroot, relpath,
scratch_pool));
if (op_depth > 0)
{
/* WORKING node. Nothing to check */
}
else if (expected_parent_repos_relpath)
{
const char *repos_relpath = svn_sqlite__column_text(stmt, 2, NULL);
if (strcmp(expected_parent_repos_relpath, repos_relpath) != 0)
{
/* The child of this node has a different parent than this node
(It is "switched"), so we can stop here. Note that switched
with the same parent is not interesting for us here. */
SVN_ERR(svn_sqlite__reset(stmt));
break;
}
expected_parent_repos_relpath =
svn_relpath_dirname(expected_parent_repos_relpath, scratch_pool);
}
else
{
const char *repos_relpath = svn_sqlite__column_text(stmt, 2, NULL);
expected_parent_repos_relpath =
svn_relpath_dirname(repos_relpath, scratch_pool);
}
if (op_depth == 0
&& !svn_sqlite__column_is_null(stmt, 16))
{
/* The node contains a cache. No reason to look further */
SVN_ERR(svn_sqlite__column_iprops(&cached_iprops, stmt, 16,
result_pool, iterpool));
parent_relpath = NULL; /* Stop after this */
}
SVN_ERR(svn_sqlite__column_properties(&node_props, stmt, 14,
iterpool, iterpool));
SVN_ERR(svn_sqlite__reset(stmt));
/* If PARENT_ABSPATH is a parent of LOCAL_ABSPATH, then LOCAL_ABSPATH
can inherit properties from it. */
if (relpath != local_relpath)
{
apr_hash_t *changed_props;
SVN_ERR(db_get_changed_props(&changed_props, wcroot, relpath,
result_pool, iterpool));
if (changed_props)
node_props = changed_props;
else if (node_props)
node_props = svn_prop_hash_dup(node_props, result_pool);
if (node_props && apr_hash_count(node_props))
{
/* If we only want PROPNAME filter out any other properties. */
if (propname)
filter_unwanted_props(node_props, propname, iterpool);
if (apr_hash_count(node_props))
{
svn_prop_inherited_item_t *iprop_elt =
apr_pcalloc(result_pool,
sizeof(svn_prop_inherited_item_t));
iprop_elt->path_or_url = svn_dirent_join(wcroot->abspath,
relpath,
result_pool);
iprop_elt->prop_hash = node_props;
/* Build the output array in depth-first order. */
svn_sort__array_insert(&iprop_elt, iprops, 0);
}
}
}
else if (actual_props)
{
apr_hash_t *changed_props;
SVN_ERR(db_get_changed_props(&changed_props, wcroot, relpath,
result_pool, iterpool));
if (changed_props)
*actual_props = changed_props;
else if (node_props)
*actual_props = svn_prop_hash_dup(node_props, result_pool);
}
}
if (cached_iprops)
{
for (i = cached_iprops->nelts - 1; i >= 0; i--)
{
svn_prop_inherited_item_t *cached_iprop =
APR_ARRAY_IDX(cached_iprops, i, svn_prop_inherited_item_t *);
/* An empty property hash in the iprops cache means there are no
inherited properties. */
if (apr_hash_count(cached_iprop->prop_hash) == 0)
continue;
if (propname)
filter_unwanted_props(cached_iprop->prop_hash, propname,
scratch_pool);
/* If we didn't filter everything then keep this iprop. */
if (apr_hash_count(cached_iprop->prop_hash))
svn_sort__array_insert(&cached_iprop, iprops, 0);
}
}
if (actual_props && !*actual_props)
*actual_props = apr_hash_make(result_pool);
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_read_inherited_props(apr_array_header_t **iprops,
apr_hash_t **actual_props,
svn_wc__db_t *db,
const char *local_abspath,
const char *propname,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
db, local_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_WC__DB_WITH_TXN(db_read_inherited_props(iprops, actual_props,
wcroot, local_relpath, propname,
result_pool, scratch_pool),
wcroot);
return SVN_NO_ERROR;
}
/* The body of svn_wc__db_get_children_with_cached_iprops().
*/
static svn_error_t *
get_children_with_cached_iprops(apr_hash_t **iprop_paths,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
svn_depth_t depth,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
*iprop_paths = apr_hash_make(result_pool);
/* First check if LOCAL_RELPATH itself has iprops */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_IPROPS_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row)
{
const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
NULL);
const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
relpath_with_cache,
result_pool);
svn_hash_sets(*iprop_paths, abspath_with_cache,
svn_sqlite__column_text(stmt, 1, result_pool));
}
SVN_ERR(svn_sqlite__reset(stmt));
if (depth == svn_depth_empty)
return SVN_NO_ERROR;
/* Now fetch information for children or all descendants */
if (depth == svn_depth_files
|| depth == svn_depth_immediates)
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_IPROPS_CHILDREN));
}
else /* Default to svn_depth_infinity. */
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_IPROPS_RECURSIVE));
}
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
while (have_row)
{
const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
NULL);
const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
relpath_with_cache,
result_pool);
svn_hash_sets(*iprop_paths, abspath_with_cache,
svn_sqlite__column_text(stmt, 1, result_pool));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
SVN_ERR(svn_sqlite__reset(stmt));
/* For depth files we should filter non files */
if (depth == svn_depth_files)
{
apr_hash_index_t *hi;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
for (hi = apr_hash_first(scratch_pool, *iprop_paths);
hi;
hi = apr_hash_next(hi))
{
const char *child_abspath = svn__apr_hash_index_key(hi);
const char *child_relpath;
svn_node_kind_t child_kind;
svn_pool_clear(iterpool);
child_relpath = svn_dirent_is_child(local_relpath, child_abspath,
NULL);
if (! child_relpath)
{
continue; /* local_relpath itself */
}
SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &child_kind, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
wcroot, child_relpath,
scratch_pool,
scratch_pool));
/* Filter if not a file */
if (child_kind != svn_node_file)
{
svn_hash_sets(*iprop_paths, child_abspath, NULL);
}
}
svn_pool_destroy(iterpool);
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_get_children_with_cached_iprops(apr_hash_t **iprop_paths,
svn_depth_t depth,
const char *local_abspath,
svn_wc__db_t *db,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool,
scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_WC__DB_WITH_TXN(
get_children_with_cached_iprops(iprop_paths, wcroot, local_relpath,
depth, result_pool, scratch_pool),
wcroot);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_read_children_of_working_node(const apr_array_header_t **children,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
return gather_children2(children, wcroot, local_relpath,
result_pool, scratch_pool);
}
/* Helper for svn_wc__db_node_check_replace().
*/
static svn_error_t *
check_replace_txn(svn_boolean_t *is_replace_root_p,
svn_boolean_t *base_replace_p,
svn_boolean_t *is_replace_p,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
svn_boolean_t is_replace = FALSE;
int replaced_op_depth;
svn_wc__db_status_t replaced_status;
/* Our caller initialized the output values to FALSE */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_NODE_INFO));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (!have_row)
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
svn_sqlite__reset(stmt),
_("The node '%s' was not found."),
path_for_error_message(wcroot, local_relpath,
scratch_pool));
{
svn_wc__db_status_t status;
status = svn_sqlite__column_token(stmt, 3, presence_map);
if (status != svn_wc__db_status_normal)
return svn_error_trace(svn_sqlite__reset(stmt));
}
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (!have_row)
return svn_error_trace(svn_sqlite__reset(stmt));
replaced_status = svn_sqlite__column_token(stmt, 3, presence_map);
/* If the layer below the add describes a not present or a deleted node,
this is not a replacement. Deleted can only occur if an ancestor is
the delete root. */
if (replaced_status != svn_wc__db_status_not_present
&& replaced_status != svn_wc__db_status_excluded
&& replaced_status != svn_wc__db_status_server_excluded
&& replaced_status != svn_wc__db_status_base_deleted)
{
is_replace = TRUE;
if (is_replace_p)
*is_replace_p = TRUE;
}
replaced_op_depth = svn_sqlite__column_int(stmt, 0);
if (base_replace_p)
{
int op_depth = svn_sqlite__column_int(stmt, 0);
while (op_depth != 0 && have_row)
{
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row)
op_depth = svn_sqlite__column_int(stmt, 0);
}
if (have_row && op_depth == 0)
{
svn_wc__db_status_t base_status;
base_status = svn_sqlite__column_token(stmt, 3, presence_map);
*base_replace_p = (base_status != svn_wc__db_status_not_present);
}
}
SVN_ERR(svn_sqlite__reset(stmt));
if (!is_replace_root_p || !is_replace)
return SVN_NO_ERROR;
if (replaced_status != svn_wc__db_status_base_deleted)
{
int parent_op_depth;
/* Check the current op-depth of the parent to see if we are a replacement
root */
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
svn_relpath_dirname(local_relpath,
scratch_pool)));
SVN_ERR(svn_sqlite__step_row(stmt)); /* Parent must exist as 'normal' */
parent_op_depth = svn_sqlite__column_int(stmt, 0);
if (parent_op_depth >= replaced_op_depth)
{
/* Did we replace inside our directory? */
*is_replace_root_p = (parent_op_depth == replaced_op_depth);
SVN_ERR(svn_sqlite__reset(stmt));
return SVN_NO_ERROR;
}
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row)
parent_op_depth = svn_sqlite__column_int(stmt, 0);
SVN_ERR(svn_sqlite__reset(stmt));
if (!have_row)
*is_replace_root_p = TRUE; /* Parent is no replacement */
else if (parent_op_depth < replaced_op_depth)
*is_replace_root_p = TRUE; /* Parent replaces a lower layer */
/*else // No replacement root */
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_node_check_replace(svn_boolean_t *is_replace_root,
svn_boolean_t *base_replace,
svn_boolean_t *is_replace,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
if (is_replace_root)
*is_replace_root = FALSE;
if (base_replace)
*base_replace = FALSE;
if (is_replace)
*is_replace = FALSE;
if (local_relpath[0] == '\0')
return SVN_NO_ERROR; /* Working copy root can't be replaced */
SVN_WC__DB_WITH_TXN(
check_replace_txn(is_replace_root, base_replace, is_replace,
wcroot, local_relpath, scratch_pool),
wcroot);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_read_children(const apr_array_header_t **children,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
return gather_children(children, wcroot, local_relpath,
result_pool, scratch_pool);
}
/* */
static svn_error_t *
relocate_txn(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
const char *repos_root_url,
const char *repos_uuid,
svn_boolean_t have_base_node,
apr_int64_t old_repos_id,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
apr_int64_t new_repos_id;
/* This function affects all the children of the given local_relpath,
but the way that it does this is through the repos inheritance mechanism.
So, we only need to rewrite the repos_id of the given local_relpath,
as well as any children with a non-null repos_id, as well as various
repos_id fields in the locks and working_node tables.
*/
/* Get the repos_id for the new repository. */
SVN_ERR(create_repos_id(&new_repos_id, repos_root_url, repos_uuid,
wcroot->sdb, scratch_pool));
/* Set the (base and working) repos_ids and clear the dav_caches */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_RECURSIVE_UPDATE_NODE_REPO));
SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath,
old_repos_id, new_repos_id));
SVN_ERR(svn_sqlite__step_done(stmt));
if (have_base_node)
{
/* Update any locks for the root or its children. */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_UPDATE_LOCK_REPOS_ID));
SVN_ERR(svn_sqlite__bindf(stmt, "ii", old_repos_id, new_repos_id));
SVN_ERR(svn_sqlite__step_done(stmt));
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_global_relocate(svn_wc__db_t *db,
const char *local_dir_abspath,
const char *repos_root_url,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
const char *local_dir_relpath;
svn_wc__db_status_t status;
const char *repos_uuid;
svn_boolean_t have_base_node;
apr_int64_t old_repos_id;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
/* ### assert that we were passed a directory? */
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_dir_relpath,
db, local_dir_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
local_relpath = local_dir_relpath;
SVN_ERR(read_info(&status,
NULL, NULL, NULL, &old_repos_id,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL,
&have_base_node, NULL, NULL,
wcroot, local_relpath,
scratch_pool, scratch_pool));
if (status == svn_wc__db_status_excluded)
{
/* The parent cannot be excluded, so look at the parent and then
adjust the relpath */
const char *parent_relpath = svn_relpath_dirname(local_dir_relpath,
scratch_pool);
SVN_ERR(read_info(&status,
NULL, NULL, NULL, &old_repos_id,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL,
NULL, NULL, NULL,
wcroot, parent_relpath,
scratch_pool, scratch_pool));
local_dir_relpath = parent_relpath;
}
if (old_repos_id == INVALID_REPOS_ID)
{
/* Do we need to support relocating something that is
added/deleted/excluded without relocating the parent? If not
then perhaps relpath, root_url and uuid should be passed down
to the children so that they don't have to scan? */
if (status == svn_wc__db_status_deleted)
{
const char *work_del_relpath;
SVN_ERR(scan_deletion_txn(NULL, NULL,
&work_del_relpath, NULL,
wcroot, local_dir_relpath,
scratch_pool,
scratch_pool));
if (work_del_relpath)
{
/* Deleted within a copy/move */
/* The parent of the delete is added. */
status = svn_wc__db_status_added;
local_dir_relpath = svn_relpath_dirname(work_del_relpath,
scratch_pool);
}
}
if (status == svn_wc__db_status_added)
{
SVN_ERR(scan_addition(NULL, NULL, NULL, &old_repos_id,
NULL, NULL, NULL, NULL, NULL, NULL,
wcroot, local_dir_relpath,
scratch_pool, scratch_pool));
}
else
SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, NULL,
&old_repos_id,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
wcroot, local_dir_relpath,
scratch_pool, scratch_pool));
}
SVN_ERR(svn_wc__db_fetch_repos_info(NULL, &repos_uuid, wcroot->sdb,
old_repos_id, scratch_pool));
SVN_ERR_ASSERT(repos_uuid);
SVN_WC__DB_WITH_TXN(
relocate_txn(wcroot, local_relpath, repos_root_url, repos_uuid,
have_base_node, old_repos_id, scratch_pool),
wcroot);
return SVN_NO_ERROR;
}
-/* Set *REPOS_ID and *REPOS_RELPATH to the BASE repository location of
+/* Helper for commit_node()
+ Set *REPOS_ID and *REPOS_RELPATH to the BASE repository location of
(WCROOT, LOCAL_RELPATH), directly if its BASE row exists or implied from
its parent's BASE row if not. In the latter case, error if the parent
BASE row does not exist. */
static svn_error_t *
-determine_repos_info(apr_int64_t *repos_id,
- const char **repos_relpath,
- svn_wc__db_wcroot_t *wcroot,
- const char *local_relpath,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
+determine_commit_repos_info(apr_int64_t *repos_id,
+ const char **repos_relpath,
+ svn_wc__db_wcroot_t *wcroot,
+ const char *local_relpath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
- const char *repos_parent_relpath;
- const char *local_parent_relpath, *name;
+ int op_depth;
- /* ### is it faster to fetch fewer columns? */
-
/* Prefer the current node's repository information. */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
- STMT_SELECT_BASE_NODE));
+ STMT_SELECT_NODE_INFO));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
- if (have_row)
+ if (!have_row)
+ return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
+ svn_sqlite__reset(stmt),
+ _("The node '%s' was not found."),
+ path_for_error_message(wcroot, local_relpath,
+ scratch_pool));
+
+ op_depth = svn_sqlite__column_int(stmt, 0);
+
+ if (op_depth > 0)
{
- SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 0));
- SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 1));
+ svn_wc__db_status_t presence = svn_sqlite__column_token(stmt, 3,
+ presence_map);
- *repos_id = svn_sqlite__column_int64(stmt, 0);
- *repos_relpath = svn_sqlite__column_text(stmt, 1, result_pool);
+ if (presence == svn_wc__db_status_base_deleted)
+ {
+ SVN_ERR(svn_sqlite__step_row(stmt)); /* There must be a row */
+ op_depth = svn_sqlite__column_int(stmt, 0);
+ }
+ else
+ {
+ const char *parent_repos_relpath;
+ const char *parent_relpath;
+ const char *name;
- return svn_error_trace(svn_sqlite__reset(stmt));
+ SVN_ERR(svn_sqlite__reset(stmt));
+
+ /* The repository relative path of an add/copy is based on its
+ ancestor, not on the shadowed base layer.
+
+ As this function is only used from the commit processing we know
+ the parent directory has only a BASE row, so we can just obtain
+ the information directly by recursing (once!) */
+
+ svn_relpath_split(&parent_relpath, &name, local_relpath,
+ scratch_pool);
+
+ SVN_ERR(determine_commit_repos_info(repos_id, &parent_repos_relpath,
+ wcroot, parent_relpath,
+ scratch_pool, scratch_pool));
+
+ *repos_relpath = svn_relpath_join(parent_repos_relpath, name,
+ result_pool);
+ return SVN_NO_ERROR;
+ }
}
- SVN_ERR(svn_sqlite__reset(stmt));
- /* This was a child node within this wcroot. We want to look at the
- BASE node of the directory. */
- svn_relpath_split(&local_parent_relpath, &name, local_relpath, scratch_pool);
+ SVN_ERR_ASSERT(op_depth == 0); /* And that row must be BASE */
- /* The REPOS_ID will be the same (### until we support mixed-repos) */
- SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
- &repos_parent_relpath, repos_id,
- NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL,
- wcroot, local_parent_relpath,
- scratch_pool, scratch_pool));
+ SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 1));
+ SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 2));
- *repos_relpath = svn_relpath_join(repos_parent_relpath, name, result_pool);
+ *repos_id = svn_sqlite__column_int64(stmt, 1);
+ *repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
- return SVN_NO_ERROR;
+ return svn_error_trace(svn_sqlite__reset(stmt));
}
/* Helper for svn_wc__db_global_commit()
Makes local_relpath and all its descendants at the same op-depth represent
the copy origin repos_id:repos_relpath@revision.
This code is only valid to fix-up a move from an old location, to a new
location during a commit.
Assumptions:
* local_relpath is not the working copy root (can't be moved)
* repos_relpath is not the repository root (can't be moved)
*/
static svn_error_t *
moved_descendant_commit(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
int op_depth,
apr_int64_t repos_id,
const char *repos_relpath,
svn_revnum_t revision,
apr_pool_t *scratch_pool)
{
apr_hash_t *children;
apr_pool_t *iterpool;
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
apr_hash_index_t *hi;
SVN_ERR_ASSERT(*local_relpath != '\0'
&& *repos_relpath != '\0');
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_MOVED_DESCENDANTS));
SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
local_relpath,
op_depth));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (! have_row)
return svn_error_trace(svn_sqlite__reset(stmt));
children = apr_hash_make(scratch_pool);
/* First, obtain all moved children */
/* To keep error handling simple, first cache them in a hashtable */
while (have_row)
{
const char *src_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
const char *to_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool);
svn_hash_sets(children, src_relpath, to_relpath);
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
SVN_ERR(svn_sqlite__reset(stmt));
/* Then update them */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_COMMIT_UPDATE_ORIGIN));
iterpool = svn_pool_create(scratch_pool);
for (hi = apr_hash_first(scratch_pool, children); hi; hi = apr_hash_next(hi))
{
const char *src_relpath = svn__apr_hash_index_key(hi);
const char *to_relpath = svn__apr_hash_index_val(hi);
const char *new_repos_relpath;
int to_op_depth = relpath_depth(to_relpath);
int affected;
svn_pool_clear(iterpool);
SVN_ERR_ASSERT(to_op_depth > 0);
new_repos_relpath = svn_relpath_join(
repos_relpath,
svn_relpath_skip_ancestor(local_relpath,
src_relpath),
iterpool);
SVN_ERR(svn_sqlite__bindf(stmt, "isdisr", wcroot->wc_id,
to_relpath,
to_op_depth,
repos_id,
new_repos_relpath,
revision));
SVN_ERR(svn_sqlite__update(&affected, stmt));
#ifdef SVN_DEBUG
/* Enable in release code?
Broken moves are not fatal yet, but this assertion would break
committing them */
SVN_ERR_ASSERT(affected >= 1); /* If this fails there is no move dest */
#endif
SVN_ERR(moved_descendant_commit(wcroot, to_relpath, to_op_depth,
repos_id, new_repos_relpath, revision,
iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Helper for svn_wc__db_global_commit()
Moves all nodes below LOCAL_RELPATH from op-depth OP_DEPTH to op-depth 0
(BASE), setting their presence to 'not-present' if their presence wasn't
'normal'.
Makes all nodes below LOCAL_RELPATH represent the descendants of repository
location repos_id:repos_relpath@revision.
Assumptions:
* local_relpath is not the working copy root (can't be replaced)
* repos_relpath is not the repository root (can't be replaced)
*/
static svn_error_t *
descendant_commit(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
int op_depth,
apr_int64_t repos_id,
const char *repos_relpath,
svn_revnum_t revision,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
SVN_ERR_ASSERT(*local_relpath != '\0'
&& *repos_relpath != '\0');
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_COMMIT_DESCENDANTS_TO_BASE));
SVN_ERR(svn_sqlite__bindf(stmt, "isdisr", wcroot->wc_id,
local_relpath,
op_depth,
repos_id,
repos_relpath,
revision));
SVN_ERR(svn_sqlite__update(NULL, stmt));
return SVN_NO_ERROR;
}
/* The body of svn_wc__db_global_commit().
*/
static svn_error_t *
commit_node(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
svn_revnum_t new_revision,
svn_revnum_t changed_rev,
apr_time_t changed_date,
const char *changed_author,
const svn_checksum_t *new_checksum,
const apr_array_header_t *new_children,
apr_hash_t *new_dav_cache,
svn_boolean_t keep_changelist,
svn_boolean_t no_unlock,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt_info;
svn_sqlite__stmt_t *stmt_act;
svn_boolean_t have_act;
svn_string_t prop_blob = { 0 };
svn_string_t inherited_prop_blob = { 0 };
const char *changelist = NULL;
const char *parent_relpath;
svn_wc__db_status_t new_presence;
svn_node_kind_t new_kind;
const char *new_depth_str = NULL;
svn_sqlite__stmt_t *stmt;
apr_int64_t repos_id;
const char *repos_relpath;
int op_depth;
svn_wc__db_status_t old_presence;
/* If we are adding a file or directory, then we need to get
repository information from the parent node since "this node" does
not have a BASE).
For existing nodes, we should retain the (potentially-switched)
repository information. */
- SVN_ERR(determine_repos_info(&repos_id, &repos_relpath,
- wcroot, local_relpath,
- scratch_pool, scratch_pool));
+ SVN_ERR(determine_commit_repos_info(&repos_id, &repos_relpath,
+ wcroot, local_relpath,
+ scratch_pool, scratch_pool));
/* ### is it better to select only the data needed? */
SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
STMT_SELECT_NODE_INFO));
SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_row(stmt_info));
SVN_ERR(svn_sqlite__get_statement(&stmt_act, wcroot->sdb,
STMT_SELECT_ACTUAL_NODE));
SVN_ERR(svn_sqlite__bindf(stmt_act, "is",
wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_act, stmt_act));
/* There should be something to commit! */
op_depth = svn_sqlite__column_int(stmt_info, 0);
/* Figure out the new node's kind. It will be whatever is in WORKING_NODE,
or there will be a BASE_NODE that has it. */
new_kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
/* What will the new depth be? */
if (new_kind == svn_node_dir)
new_depth_str = svn_sqlite__column_text(stmt_info, 11, scratch_pool);
/* Check that the repository information is not being changed. */
if (op_depth == 0)
{
SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt_info, 1));
SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt_info, 2));
/* A commit cannot change these values. */
SVN_ERR_ASSERT(repos_id == svn_sqlite__column_int64(stmt_info, 1));
SVN_ERR_ASSERT(strcmp(repos_relpath,
svn_sqlite__column_text(stmt_info, 2, NULL)) == 0);
}
/* Find the appropriate new properties -- ACTUAL overrides any properties
in WORKING that arrived as part of a copy/move.
Note: we'll keep them as a big blob of data, rather than
deserialize/serialize them. */
if (have_act)
prop_blob.data = svn_sqlite__column_blob(stmt_act, 1, &prop_blob.len,
scratch_pool);
if (prop_blob.data == NULL)
prop_blob.data = svn_sqlite__column_blob(stmt_info, 14, &prop_blob.len,
scratch_pool);
inherited_prop_blob.data = svn_sqlite__column_blob(stmt_info, 16,
&inherited_prop_blob.len,
scratch_pool);
if (keep_changelist && have_act)
changelist = svn_sqlite__column_text(stmt_act, 0, scratch_pool);
old_presence = svn_sqlite__column_token(stmt_info, 3, presence_map);
/* ### other stuff? */
SVN_ERR(svn_sqlite__reset(stmt_info));
SVN_ERR(svn_sqlite__reset(stmt_act));
if (op_depth > 0)
{
int affected_rows;
/* This removes all layers of this node and at the same time determines
if we need to remove shadowed layers below our descendants. */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_NODE_ALL_LAYERS));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
if (affected_rows > 1)
{
/* We commit a shadowing operation
1) Remove all shadowed nodes
2) And remove all nodes that have a base-deleted as lowest layer,
because 1) removed that layer */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_SHADOWED_RECURSIVE));
SVN_ERR(svn_sqlite__bindf(stmt,
"isd",
wcroot->wc_id,
local_relpath,
op_depth));
SVN_ERR(svn_sqlite__step_done(stmt));
}
/* Note that while these two calls look so similar that they might
be integrated, they really affect a different op-depth and
completely different nodes (via a different recursion pattern). */
/* Collapse descendants of the current op_depth in layer 0 */
SVN_ERR(descendant_commit(wcroot, local_relpath, op_depth,
repos_id, repos_relpath, new_revision,
scratch_pool));
/* And make the recorded local moves represent moves of the node we just
committed. */
SVN_ERR(moved_descendant_commit(wcroot, local_relpath, 0,
repos_id, repos_relpath, new_revision,
scratch_pool));
/* This node is no longer modified, so no node was moved here */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_CLEAR_MOVED_TO_FROM_DEST));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
}
/* Update or add the BASE_NODE row with all the new information. */
if (*local_relpath == '\0')
parent_relpath = NULL;
else
parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
/* Preserve any incomplete status */
new_presence = (old_presence == svn_wc__db_status_incomplete
? svn_wc__db_status_incomplete
: svn_wc__db_status_normal);
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_APPLY_CHANGES_TO_BASE_NODE));
/* symlink_target not yet used */
SVN_ERR(svn_sqlite__bindf(stmt, "issisrtstrisnbn",
wcroot->wc_id, local_relpath,
parent_relpath,
repos_id,
repos_relpath,
new_revision,
presence_map, new_presence,
new_depth_str,
kind_map, new_kind,
changed_rev,
changed_date,
changed_author,
prop_blob.data, prop_blob.len));
SVN_ERR(svn_sqlite__bind_checksum(stmt, 13, new_checksum,
scratch_pool));
SVN_ERR(svn_sqlite__bind_properties(stmt, 15, new_dav_cache,
scratch_pool));
if (inherited_prop_blob.data != NULL)
{
SVN_ERR(svn_sqlite__bind_blob(stmt, 17, inherited_prop_blob.data,
inherited_prop_blob.len));
}
SVN_ERR(svn_sqlite__step_done(stmt));
if (have_act)
{
if (keep_changelist && changelist != NULL)
{
/* The user told us to keep the changelist. Replace the row in
ACTUAL_NODE with the basic keys and the changelist. */
SVN_ERR(svn_sqlite__get_statement(
&stmt, wcroot->sdb,
STMT_RESET_ACTUAL_WITH_CHANGELIST));
SVN_ERR(svn_sqlite__bindf(stmt, "isss",
wcroot->wc_id, local_relpath,
svn_relpath_dirname(local_relpath,
scratch_pool),
changelist));
SVN_ERR(svn_sqlite__step_done(stmt));
}
else
{
/* Toss the ACTUAL_NODE row. */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_ACTUAL_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
}
}
if (new_kind == svn_node_dir)
{
/* When committing a directory, we should have its new children. */
/* ### one day. just not today. */
#if 0
SVN_ERR_ASSERT(new_children != NULL);
#endif
/* ### process the children */
}
if (!no_unlock)
{
svn_sqlite__stmt_t *lock_stmt;
SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb,
STMT_DELETE_LOCK_RECURSIVELY));
SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath));
SVN_ERR(svn_sqlite__step_done(lock_stmt));
}
/* Install any work items into the queue, as part of this transaction. */
SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_global_commit(svn_wc__db_t *db,
const char *local_abspath,
svn_revnum_t new_revision,
svn_revnum_t changed_revision,
apr_time_t changed_date,
const char *changed_author,
const svn_checksum_t *new_checksum,
const apr_array_header_t *new_children,
apr_hash_t *new_dav_cache,
svn_boolean_t keep_changelist,
svn_boolean_t no_unlock,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
const char *local_relpath;
svn_wc__db_wcroot_t *wcroot;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision));
SVN_ERR_ASSERT(new_checksum == NULL || new_children == NULL);
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_WC__DB_WITH_TXN(
commit_node(wcroot, local_relpath,
new_revision, changed_revision, changed_date, changed_author,
new_checksum, new_children, new_dav_cache, keep_changelist,
no_unlock, work_items, scratch_pool),
wcroot);
/* We *totally* monkeyed the entries. Toss 'em. */
SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_global_update(svn_wc__db_t *db,
const char *local_abspath,
svn_node_kind_t new_kind,
const char *new_repos_relpath,
svn_revnum_t new_revision,
const apr_hash_t *new_props,
svn_revnum_t new_changed_rev,
apr_time_t new_changed_date,
const char *new_changed_author,
const apr_array_header_t *new_children,
const svn_checksum_t *new_checksum,
const char *new_target,
const apr_hash_t *new_dav_cache,
const svn_skel_t *conflict,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
NOT_IMPLEMENTED();
#if 0
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
/* ### allow NULL for NEW_REPOS_RELPATH to indicate "no change"? */
SVN_ERR_ASSERT(svn_relpath_is_canonical(new_repos_relpath));
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision));
SVN_ERR_ASSERT(new_props != NULL);
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_changed_rev));
SVN_ERR_ASSERT((new_children != NULL
&& new_checksum == NULL
&& new_target == NULL)
|| (new_children == NULL
&& new_checksum != NULL
&& new_target == NULL)
|| (new_children == NULL
&& new_checksum == NULL
&& new_target != NULL));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_WC__DB_WITH_TXN(
update_node(wcroot, local_relpath,
new_repos_relpath, new_revision, new_props,
new_changed_rev, new_changed_date, new_changed_author,
new_children, new_checksum, new_target,
conflict, work_items, scratch_pool),
wcroot);
/* We *totally* monkeyed the entries. Toss 'em. */
SVN_ERR(flush_entries(wcroot, local_abspath, scratch_pool));
return SVN_NO_ERROR;
#endif
}
/* Sets a base nodes revision, repository relative path, and/or inherited
propertis. If LOCAL_ABSPATH's rev (REV) is valid, set its revision. If
SET_REPOS_RELPATH is TRUE set its repository relative path to REPOS_RELPATH
(and make sure its REPOS_ID is still valid). If IPROPS is not NULL set its
inherited properties to IPROPS, if IPROPS is NULL then clear any the iprops
cache for the base node.
*/
static svn_error_t *
db_op_set_rev_repos_relpath_iprops(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_array_header_t *iprops,
svn_revnum_t rev,
svn_boolean_t set_repos_relpath,
const char *repos_relpath,
apr_int64_t repos_id,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
SVN_ERR(flush_entries(wcroot,
svn_dirent_join(wcroot->abspath, local_relpath,
scratch_pool),
svn_depth_empty, scratch_pool));
if (SVN_IS_VALID_REVNUM(rev))
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_UPDATE_BASE_REVISION));
SVN_ERR(svn_sqlite__bindf(stmt, "isr", wcroot->wc_id, local_relpath,
rev));
SVN_ERR(svn_sqlite__step_done(stmt));
}
if (set_repos_relpath)
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_UPDATE_BASE_REPOS));
SVN_ERR(svn_sqlite__bindf(stmt, "isis", wcroot->wc_id, local_relpath,
repos_id, repos_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
}
/* Set or clear iprops. */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_UPDATE_IPROP));
SVN_ERR(svn_sqlite__bindf(stmt, "is",
wcroot->wc_id,
local_relpath));
SVN_ERR(svn_sqlite__bind_iprops(stmt, 3, iprops, scratch_pool));
SVN_ERR(svn_sqlite__step_done(stmt));
return SVN_NO_ERROR;
}
/* The main body of bump_revisions_post_update().
*
* Tweak the information for LOCAL_RELPATH in WCROOT. If NEW_REPOS_RELPATH is
* non-NULL update the entry to the new url specified by NEW_REPOS_RELPATH,
* NEW_REPOS_ID. If NEW_REV is valid, make this the node's working revision.
*
* If WCROOT_IPROPS is not NULL it is a hash mapping const char * absolute
* working copy paths to depth-first ordered arrays of
* svn_prop_inherited_item_t * structures. If the absolute path equivalent
* of LOCAL_RELPATH exists in WCROOT_IPROPS, then set the hashed value as the
* node's inherited properties.
*
* Unless S_ROOT is TRUE the tweaks might cause the node for LOCAL_ABSPATH to
* be removed from the WC; if IS_ROOT is TRUE this will not happen.
*/
static svn_error_t *
bump_node_revision(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_int64_t new_repos_id,
const char *new_repos_relpath,
svn_revnum_t new_rev,
svn_depth_t depth,
apr_hash_t *exclude_relpaths,
apr_hash_t *wcroot_iprops,
svn_boolean_t is_root,
svn_boolean_t skip_when_dir,
svn_wc__db_t *db,
apr_pool_t *scratch_pool)
{
apr_pool_t *iterpool;
const apr_array_header_t *children;
int i;
svn_wc__db_status_t status;
svn_node_kind_t db_kind;
svn_revnum_t revision;
const char *repos_relpath;
apr_int64_t repos_id;
svn_boolean_t set_repos_relpath = FALSE;
svn_boolean_t update_root;
svn_depth_t depth_below_here = depth;
apr_array_header_t *iprops = NULL;
/* Skip an excluded path and its descendants. */
if (svn_hash_gets(exclude_relpaths, local_relpath))
return SVN_NO_ERROR;
SVN_ERR(svn_wc__db_base_get_info_internal(&status, &db_kind, &revision,
&repos_relpath, &repos_id,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, &update_root,
wcroot, local_relpath,
scratch_pool, scratch_pool));
/* Skip file externals */
if (update_root
&& db_kind == svn_node_file
&& !is_root)
return SVN_NO_ERROR;
if (skip_when_dir && db_kind == svn_node_dir)
return SVN_NO_ERROR;
/* If the node is still marked 'not-present', then the server did not
re-add it. So it's really gone in this revision, thus we remove the node.
If the node is still marked 'server-excluded' and yet is not the same
revision as new_rev, then the server did not re-add it, nor
re-server-exclude it, so we can remove the node. */
if (!is_root
&& (status == svn_wc__db_status_not_present
|| (status == svn_wc__db_status_server_excluded &&
revision != new_rev)))
{
return svn_error_trace(db_base_remove(wcroot, local_relpath,
db, FALSE, FALSE, FALSE,
SVN_INVALID_REVNUM,
NULL, NULL, scratch_pool));
}
if (new_repos_relpath != NULL && strcmp(repos_relpath, new_repos_relpath))
set_repos_relpath = TRUE;
if (wcroot_iprops)
iprops = svn_hash_gets(wcroot_iprops,
svn_dirent_join(wcroot->abspath, local_relpath,
scratch_pool));
if (iprops
|| set_repos_relpath
|| (SVN_IS_VALID_REVNUM(new_rev) && new_rev != revision))
{
SVN_ERR(db_op_set_rev_repos_relpath_iprops(wcroot, local_relpath,
iprops, new_rev,
set_repos_relpath,
new_repos_relpath,
new_repos_id,
scratch_pool));
}
/* Early out */
if (depth <= svn_depth_empty
|| db_kind != svn_node_dir
|| status == svn_wc__db_status_server_excluded
|| status == svn_wc__db_status_excluded
|| status == svn_wc__db_status_not_present)
return SVN_NO_ERROR;
/* And now recurse over the children */
depth_below_here = depth;
if (depth == svn_depth_immediates || depth == svn_depth_files)
depth_below_here = svn_depth_empty;
iterpool = svn_pool_create(scratch_pool);
SVN_ERR(gather_repo_children(&children, wcroot, local_relpath, 0,
scratch_pool, iterpool));
for (i = 0; i < children->nelts; i++)
{
const char *child_basename = APR_ARRAY_IDX(children, i, const char *);
const char *child_local_relpath;
const char *child_repos_relpath = NULL;
svn_pool_clear(iterpool);
/* Derive the new URL for the current (child) entry */
if (new_repos_relpath)
child_repos_relpath = svn_relpath_join(new_repos_relpath,
child_basename, iterpool);
child_local_relpath = svn_relpath_join(local_relpath, child_basename,
iterpool);
SVN_ERR(bump_node_revision(wcroot, child_local_relpath, new_repos_id,
child_repos_relpath, new_rev,
depth_below_here,
exclude_relpaths, wcroot_iprops,
FALSE /* is_root */,
(depth < svn_depth_immediates), db,
iterpool));
}
/* Cleanup */
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Helper for svn_wc__db_op_bump_revisions_post_update().
*/
static svn_error_t *
bump_revisions_post_update(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
svn_wc__db_t *db,
svn_depth_t depth,
const char *new_repos_relpath,
const char *new_repos_root_url,
const char *new_repos_uuid,
svn_revnum_t new_revision,
apr_hash_t *exclude_relpaths,
apr_hash_t *wcroot_iprops,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool)
{
svn_wc__db_status_t status;
svn_node_kind_t kind;
svn_error_t *err;
apr_int64_t new_repos_id = INVALID_REPOS_ID;
err = svn_wc__db_base_get_info_internal(&status, &kind, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
wcroot, local_relpath,
scratch_pool, scratch_pool);
if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
{
svn_error_clear(err);
return SVN_NO_ERROR;
}
else
SVN_ERR(err);
switch (status)
{
case svn_wc__db_status_excluded:
case svn_wc__db_status_server_excluded:
case svn_wc__db_status_not_present:
return SVN_NO_ERROR;
/* Explicitly ignore other statii */
default:
break;
}
if (new_repos_root_url != NULL)
SVN_ERR(create_repos_id(&new_repos_id, new_repos_root_url,
new_repos_uuid,
wcroot->sdb, scratch_pool));
SVN_ERR(bump_node_revision(wcroot, local_relpath, new_repos_id,
new_repos_relpath, new_revision,
depth, exclude_relpaths,
wcroot_iprops,
TRUE /* is_root */, FALSE, db,
scratch_pool));
SVN_ERR(svn_wc__db_bump_moved_away(wcroot, local_relpath, depth, db,
scratch_pool));
SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, SVN_INVALID_REVNUM,
SVN_INVALID_REVNUM, notify_func,
notify_baton, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_op_bump_revisions_post_update(svn_wc__db_t *db,
const char *local_abspath,
svn_depth_t depth,
const char *new_repos_relpath,
const char *new_repos_root_url,
const char *new_repos_uuid,
svn_revnum_t new_revision,
apr_hash_t *exclude_relpaths,
apr_hash_t *wcroot_iprops,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool)
{
const char *local_relpath;
svn_wc__db_wcroot_t *wcroot;
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
if (svn_hash_gets(exclude_relpaths, local_relpath))
return SVN_NO_ERROR;
if (depth == svn_depth_unknown)
depth = svn_depth_infinity;
SVN_WC__DB_WITH_TXN(
bump_revisions_post_update(wcroot, local_relpath, db,
depth, new_repos_relpath, new_repos_root_url,
new_repos_uuid, new_revision,
exclude_relpaths, wcroot_iprops,
notify_func, notify_baton, scratch_pool),
wcroot);
return SVN_NO_ERROR;
}
/* The body of svn_wc__db_lock_add().
*/
static svn_error_t *
lock_add_txn(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
const svn_wc__db_lock_t *lock,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
const char *repos_relpath;
apr_int64_t repos_id;
SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
&repos_relpath, &repos_id,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
wcroot, local_relpath,
scratch_pool, scratch_pool));
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_LOCK));
SVN_ERR(svn_sqlite__bindf(stmt, "iss",
repos_id, repos_relpath, lock->token));
if (lock->owner != NULL)
SVN_ERR(svn_sqlite__bind_text(stmt, 4, lock->owner));
if (lock->comment != NULL)
SVN_ERR(svn_sqlite__bind_text(stmt, 5, lock->comment));
if (lock->date != 0)
SVN_ERR(svn_sqlite__bind_int64(stmt, 6, lock->date));
SVN_ERR(svn_sqlite__insert(NULL, stmt));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_lock_add(svn_wc__db_t *db,
const char *local_abspath,
const svn_wc__db_lock_t *lock,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR_ASSERT(lock != NULL);
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_WC__DB_WITH_TXN(
lock_add_txn(wcroot, local_relpath, lock, scratch_pool),
wcroot);
/* There may be some entries, and the lock info is now out of date. */
SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
return SVN_NO_ERROR;
}
/* The body of svn_wc__db_lock_remove().
*/
static svn_error_t *
lock_remove_txn(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *scratch_pool)
{
const char *repos_relpath;
apr_int64_t repos_id;
svn_sqlite__stmt_t *stmt;
SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
&repos_relpath, &repos_id,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
wcroot, local_relpath,
scratch_pool, scratch_pool));
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_LOCK));
SVN_ERR(svn_sqlite__bindf(stmt, "is", repos_id, repos_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_lock_remove(svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_WC__DB_WITH_TXN(
lock_remove_txn(wcroot, local_relpath, scratch_pool),
wcroot);
/* There may be some entries, and the lock info is now out of date. */
SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_scan_base_repos(const char **repos_relpath,
const char **repos_root_url,
const char **repos_uuid,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
apr_int64_t repos_id;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
repos_relpath, &repos_id,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
wcroot, local_relpath,
result_pool, scratch_pool));
SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, wcroot->sdb,
repos_id, result_pool));
return SVN_NO_ERROR;
}
/* A helper for scan_addition().
* Compute moved-from information for the node at LOCAL_RELPATH which
* has been determined as having been moved-here.
* If MOVED_FROM_RELPATH is not NULL, set *MOVED_FROM_RELPATH to the
* path of the move-source node in *MOVED_FROM_RELPATH.
* If DELETE_OP_ROOT_RELPATH is not NULL, set *DELETE_OP_ROOT_RELPATH
* to the path of the op-root of the delete-half of the move.
* If moved-from information cannot be derived, set both *MOVED_FROM_RELPATH
* and *DELETE_OP_ROOT_RELPATH to NULL, and return a "copied" status.
* COPY_OPT_ROOT_RELPATH is the relpath of the op-root of the copied-half
* of the move. */
static svn_error_t *
get_moved_from_info(const char **moved_from_relpath,
const char **moved_from_op_root_relpath,
const char *moved_to_op_root_relpath,
int *op_depth,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
/* Run a query to get the moved-from path from the DB. */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_MOVED_FROM_RELPATH));
SVN_ERR(svn_sqlite__bindf(stmt, "is",
wcroot->wc_id, moved_to_op_root_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (!have_row)
{
/* The move was only recorded at the copy-half, possibly because
* the move operation was interrupted mid-way between the copy
* and the delete. Treat this node as a normal copy. */
if (moved_from_relpath)
*moved_from_relpath = NULL;
if (moved_from_op_root_relpath)
*moved_from_op_root_relpath = NULL;
SVN_ERR(svn_sqlite__reset(stmt));
return SVN_NO_ERROR;
}
if (op_depth)
*op_depth = svn_sqlite__column_int(stmt, 1);
if (moved_from_relpath || moved_from_op_root_relpath)
{
const char *db_delete_op_root_relpath;
/* The moved-from path from the DB is the relpath of
* the op_root of the delete-half of the move. */
db_delete_op_root_relpath = svn_sqlite__column_text(stmt, 0,
result_pool);
if (moved_from_op_root_relpath)
*moved_from_op_root_relpath = db_delete_op_root_relpath;
if (moved_from_relpath)
{
if (strcmp(moved_to_op_root_relpath, local_relpath) == 0)
{
/* LOCAL_RELPATH is the op_root of the copied-half of the
* move, so the correct MOVED_FROM_ABSPATH is the op-root
* of the delete-half. */
*moved_from_relpath = db_delete_op_root_relpath;
}
else
{
const char *child_relpath;
/* LOCAL_RELPATH is a child that was copied along with the
* op_root of the copied-half of the move. Construct the
* corresponding path beneath the op_root of the delete-half. */
/* Grab the child path relative to the op_root of the move
* destination. */
child_relpath = svn_relpath_skip_ancestor(
moved_to_op_root_relpath, local_relpath);
SVN_ERR_ASSERT(child_relpath && strlen(child_relpath) > 0);
/* This join is valid because LOCAL_RELPATH has not been moved
* within the copied-half of the move yet -- else, it would
* be its own op_root. */
*moved_from_relpath = svn_relpath_join(db_delete_op_root_relpath,
child_relpath,
result_pool);
}
}
}
SVN_ERR(svn_sqlite__reset(stmt));
return SVN_NO_ERROR;
}
/* The body of scan_addition().
*/
static svn_error_t *
scan_addition_txn(svn_wc__db_status_t *status,
const char **op_root_relpath_p,
const char **repos_relpath,
apr_int64_t *repos_id,
const char **original_repos_relpath,
apr_int64_t *original_repos_id,
svn_revnum_t *original_revision,
const char **moved_from_relpath,
const char **moved_from_op_root_relpath,
int *moved_from_op_depth,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *op_root_relpath;
const char *build_relpath = "";
/* Initialize most of the OUT parameters. Generally, we'll only be filling
in a subset of these, so it is easier to init all up front. Note that
the STATUS parameter will be initialized once we read the status of
the specified node. */
if (op_root_relpath_p)
*op_root_relpath_p = NULL;
if (original_repos_relpath)
*original_repos_relpath = NULL;
if (original_repos_id)
*original_repos_id = INVALID_REPOS_ID;
if (original_revision)
*original_revision = SVN_INVALID_REVNUM;
if (moved_from_relpath)
*moved_from_relpath = NULL;
if (moved_from_op_root_relpath)
*moved_from_op_root_relpath = NULL;
if (moved_from_op_depth)
*moved_from_op_depth = 0;
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
svn_wc__db_status_t presence;
int op_depth;
const char *repos_prefix_path = "";
int i;
/* ### is it faster to fetch fewer columns? */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_WORKING_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (!have_row)
{
/* Reset statement before returning */
SVN_ERR(svn_sqlite__reset(stmt));
/* ### maybe we should return a usage error instead? */
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("The node '%s' was not found."),
path_for_error_message(wcroot,
local_relpath,
scratch_pool));
}
presence = svn_sqlite__column_token(stmt, 1, presence_map);
/* The starting node should exist normally. */
op_depth = svn_sqlite__column_int(stmt, 0);
if (op_depth == 0 || (presence != svn_wc__db_status_normal
&& presence != svn_wc__db_status_incomplete))
/* reset the statement as part of the error generation process */
return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
svn_sqlite__reset(stmt),
_("Expected node '%s' to be added."),
path_for_error_message(wcroot,
local_relpath,
scratch_pool));
if (original_revision)
*original_revision = svn_sqlite__column_revnum(stmt, 12);
/* Provide the default status; we'll override as appropriate. */
if (status)
{
if (presence == svn_wc__db_status_normal)
*status = svn_wc__db_status_added;
else
*status = svn_wc__db_status_incomplete;
}
/* Calculate the op root local path components */
op_root_relpath = local_relpath;
for (i = relpath_depth(local_relpath); i > op_depth; --i)
{
/* Calculate the path of the operation root */
repos_prefix_path =
svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
repos_prefix_path,
scratch_pool);
op_root_relpath = svn_relpath_dirname(op_root_relpath, scratch_pool);
}
if (op_root_relpath_p)
*op_root_relpath_p = apr_pstrdup(result_pool, op_root_relpath);
/* ### This if-statement is quite redundant.
* ### We're checking all these values again within the body anyway.
* ### The body should be broken up appropriately and move into the
* ### outer scope. */
if (original_repos_relpath
|| original_repos_id
|| (original_revision
&& *original_revision == SVN_INVALID_REVNUM)
|| status
|| moved_from_relpath || moved_from_op_root_relpath)
{
if (local_relpath != op_root_relpath)
/* requery to get the add/copy root */
{
SVN_ERR(svn_sqlite__reset(stmt));
SVN_ERR(svn_sqlite__bindf(stmt, "is",
wcroot->wc_id, op_root_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (!have_row)
{
/* Reset statement before returning */
SVN_ERR(svn_sqlite__reset(stmt));
/* ### maybe we should return a usage error instead? */
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("The node '%s' was not found."),
path_for_error_message(wcroot,
op_root_relpath,
scratch_pool));
}
if (original_revision
&& *original_revision == SVN_INVALID_REVNUM)
*original_revision = svn_sqlite__column_revnum(stmt, 12);
}
if (original_repos_relpath)
*original_repos_relpath = svn_sqlite__column_text(stmt, 11,
result_pool);
if (!svn_sqlite__column_is_null(stmt, 10)
&& (status
|| original_repos_id
|| moved_from_relpath || moved_from_op_root_relpath))
/* If column 10 (original_repos_id) is NULL,
this is a plain add, not a copy or a move */
{
svn_boolean_t moved_here;
if (original_repos_id)
*original_repos_id = svn_sqlite__column_int64(stmt, 10);
moved_here = svn_sqlite__column_boolean(stmt, 13 /* moved_here */);
if (status)
*status = moved_here ? svn_wc__db_status_moved_here
: svn_wc__db_status_copied;
if (moved_here
&& (moved_from_relpath || moved_from_op_root_relpath))
{
svn_error_t *err;
err = get_moved_from_info(moved_from_relpath,
moved_from_op_root_relpath,
op_root_relpath,
moved_from_op_depth,
wcroot, local_relpath,
result_pool,
scratch_pool);
if (err)
return svn_error_compose_create(
err, svn_sqlite__reset(stmt));
}
}
}
/* ### This loop here is to skip up to the first node which is a BASE node,
because base_get_info() doesn't accommodate the scenario that
we're looking at here; we found the true op_root, which may be inside
further changed trees. */
if (repos_relpath || repos_id)
{
const char *base_relpath;
while (TRUE)
{
SVN_ERR(svn_sqlite__reset(stmt));
/* Pointing at op_depth, look at the parent */
repos_prefix_path =
svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
repos_prefix_path,
scratch_pool);
op_root_relpath = svn_relpath_dirname(op_root_relpath, scratch_pool);
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, op_root_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (! have_row)
break;
op_depth = svn_sqlite__column_int(stmt, 0);
/* Skip to op_depth */
for (i = relpath_depth(op_root_relpath); i > op_depth; i--)
{
/* Calculate the path of the operation root */
repos_prefix_path =
svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
repos_prefix_path,
scratch_pool);
op_root_relpath =
svn_relpath_dirname(op_root_relpath, scratch_pool);
}
}
SVN_ERR(svn_sqlite__reset(stmt));
build_relpath = repos_prefix_path;
/* If we're here, then we have an added/copied/moved (start) node, and
CURRENT_ABSPATH now points to a BASE node. Figure out the repository
information for the current node, and use that to compute the start
node's repository information. */
SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
&base_relpath, repos_id,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
wcroot, op_root_relpath,
scratch_pool, scratch_pool));
if (repos_relpath)
*repos_relpath = svn_relpath_join(base_relpath, build_relpath,
result_pool);
}
else
SVN_ERR(svn_sqlite__reset(stmt));
}
/* Postconditions */
#ifdef SVN_DEBUG
if (status)
{
SVN_ERR_ASSERT(*status == svn_wc__db_status_added
|| *status == svn_wc__db_status_copied
|| *status == svn_wc__db_status_incomplete
|| *status == svn_wc__db_status_moved_here);
if (*status == svn_wc__db_status_added)
{
SVN_ERR_ASSERT(!original_repos_relpath
|| *original_repos_relpath == NULL);
SVN_ERR_ASSERT(!original_revision
|| *original_revision == SVN_INVALID_REVNUM);
SVN_ERR_ASSERT(!original_repos_id
|| *original_repos_id == INVALID_REPOS_ID);
}
/* An upgrade with a missing directory can leave INCOMPLETE working
op-roots. See upgrade_tests.py 29: upgrade with missing replaced dir
*/
else if (*status != svn_wc__db_status_incomplete)
{
SVN_ERR_ASSERT(!original_repos_relpath
|| *original_repos_relpath != NULL);
SVN_ERR_ASSERT(!original_revision
|| *original_revision != SVN_INVALID_REVNUM);
SVN_ERR_ASSERT(!original_repos_id
|| *original_repos_id != INVALID_REPOS_ID);
}
}
SVN_ERR_ASSERT(!op_root_relpath_p || *op_root_relpath_p != NULL);
#endif
return SVN_NO_ERROR;
}
/* Like svn_wc__db_scan_addition(), but with WCROOT+LOCAL_RELPATH instead of
DB+LOCAL_ABSPATH.
The output value of *ORIGINAL_REPOS_ID will be INVALID_REPOS_ID if there
is no 'copy-from' repository. */
static svn_error_t *
scan_addition(svn_wc__db_status_t *status,
const char **op_root_relpath,
const char **repos_relpath,
apr_int64_t *repos_id,
const char **original_repos_relpath,
apr_int64_t *original_repos_id,
svn_revnum_t *original_revision,
const char **moved_from_relpath,
const char **moved_from_op_root_relpath,
int *moved_from_op_depth,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
SVN_WC__DB_WITH_TXN(
scan_addition_txn(status, op_root_relpath, repos_relpath, repos_id,
original_repos_relpath, original_repos_id,
original_revision, moved_from_relpath,
moved_from_op_root_relpath, moved_from_op_depth,
wcroot, local_relpath, result_pool, scratch_pool),
wcroot);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_scan_addition(svn_wc__db_status_t *status,
const char **op_root_abspath,
const char **repos_relpath,
const char **repos_root_url,
const char **repos_uuid,
const char **original_repos_relpath,
const char **original_root_url,
const char **original_uuid,
svn_revnum_t *original_revision,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
const char *op_root_relpath = NULL;
apr_int64_t repos_id = INVALID_REPOS_ID;
apr_int64_t original_repos_id = INVALID_REPOS_ID;
apr_int64_t *repos_id_p
= (repos_root_url || repos_uuid) ? &repos_id : NULL;
apr_int64_t *original_repos_id_p
= (original_root_url || original_uuid) ? &original_repos_id : NULL;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_ERR(scan_addition(status,
op_root_abspath
? &op_root_relpath
: NULL,
repos_relpath, repos_id_p,
original_repos_relpath, original_repos_id_p,
original_revision,
NULL, NULL, NULL,
wcroot, local_relpath, result_pool, scratch_pool));
if (op_root_abspath)
*op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
result_pool);
/* REPOS_ID must be valid if requested; ORIGINAL_REPOS_ID need not be. */
SVN_ERR_ASSERT(repos_id_p == NULL || repos_id != INVALID_REPOS_ID);
SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, wcroot->sdb,
repos_id, result_pool));
SVN_ERR(svn_wc__db_fetch_repos_info(original_root_url, original_uuid,
wcroot->sdb, original_repos_id,
result_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_scan_moved(const char **moved_from_abspath,
const char **op_root_abspath,
const char **op_root_moved_from_abspath,
const char **moved_from_delete_abspath,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
svn_wc__db_status_t status;
const char *op_root_relpath = NULL;
const char *moved_from_relpath = NULL;
const char *moved_from_op_root_relpath = NULL;
int moved_from_op_depth = -1;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_ERR(scan_addition(&status,
op_root_abspath
? &op_root_relpath
: NULL,
NULL, NULL,
NULL, NULL, NULL,
moved_from_abspath
? &moved_from_relpath
: NULL,
(op_root_moved_from_abspath
|| moved_from_delete_abspath)
? &moved_from_op_root_relpath
: NULL,
moved_from_delete_abspath
? &moved_from_op_depth
: NULL,
wcroot, local_relpath, scratch_pool, scratch_pool));
if (status != svn_wc__db_status_moved_here || !moved_from_relpath)
return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
_("Path '%s' was not moved here"),
path_for_error_message(wcroot, local_relpath,
scratch_pool));
if (op_root_abspath)
*op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
result_pool);
if (moved_from_abspath)
*moved_from_abspath = svn_dirent_join(wcroot->abspath, moved_from_relpath,
result_pool);
if (op_root_moved_from_abspath)
*op_root_moved_from_abspath = svn_dirent_join(wcroot->abspath,
moved_from_op_root_relpath,
result_pool);
/* The deleted node is either where we moved from, or one of its ancestors */
if (moved_from_delete_abspath)
{
const char *tmp = moved_from_op_root_relpath;
SVN_ERR_ASSERT(moved_from_op_depth >= 0);
while (relpath_depth(tmp) > moved_from_op_depth)
tmp = svn_relpath_dirname(tmp, scratch_pool);
*moved_from_delete_abspath = svn_dirent_join(wcroot->abspath, tmp,
scratch_pool);
}
return SVN_NO_ERROR;
}
/* ###
*/
static svn_error_t *
follow_moved_to(apr_array_header_t **moved_tos,
int op_depth,
const char *repos_path,
svn_revnum_t revision,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
int working_op_depth;
const char *ancestor_relpath, *node_moved_to = NULL;
int i;
SVN_ERR_ASSERT((!op_depth && !repos_path) || (op_depth && repos_path));
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_OP_DEPTH_MOVED_TO));
SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
op_depth));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row)
{
working_op_depth = svn_sqlite__column_int(stmt, 0);
node_moved_to = svn_sqlite__column_text(stmt, 1, result_pool);
if (!repos_path)
{
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (!have_row || svn_sqlite__column_revnum(stmt, 0))
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
svn_sqlite__reset(stmt),
_("The base node '%s' was not found."),
path_for_error_message(wcroot,
local_relpath,
scratch_pool));
repos_path = svn_sqlite__column_text(stmt, 2, scratch_pool);
revision = svn_sqlite__column_revnum(stmt, 3);
}
}
SVN_ERR(svn_sqlite__reset(stmt));
if (node_moved_to)
{
svn_boolean_t have_row2;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_MOVED_HERE));
SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, node_moved_to,
relpath_depth(node_moved_to)));
SVN_ERR(svn_sqlite__step(&have_row2, stmt));
if (!have_row2 || !svn_sqlite__column_int(stmt, 0)
|| revision != svn_sqlite__column_revnum(stmt, 3)
|| strcmp(repos_path, svn_sqlite__column_text(stmt, 2, NULL)))
node_moved_to = NULL;
SVN_ERR(svn_sqlite__reset(stmt));
}
if (node_moved_to)
{
struct svn_wc__db_moved_to_t *moved_to;
moved_to = apr_palloc(result_pool, sizeof(*moved_to));
moved_to->op_depth = working_op_depth;
moved_to->local_relpath = node_moved_to;
APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to;
}
/* A working row with moved_to, or no working row, and we are done. */
if (node_moved_to || !have_row)
return SVN_NO_ERROR;
/* Need to handle being moved via an ancestor. */
ancestor_relpath = local_relpath;
for (i = relpath_depth(local_relpath); i > working_op_depth; --i)
{
const char *ancestor_moved_to;
ancestor_relpath = svn_relpath_dirname(ancestor_relpath, scratch_pool);
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_MOVED_TO));
SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, ancestor_relpath,
working_op_depth));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
SVN_ERR_ASSERT(have_row);
ancestor_moved_to = svn_sqlite__column_text(stmt, 0, scratch_pool);
SVN_ERR(svn_sqlite__reset(stmt));
if (ancestor_moved_to)
{
node_moved_to
= svn_relpath_join(ancestor_moved_to,
svn_relpath_skip_ancestor(ancestor_relpath,
local_relpath),
result_pool);
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_MOVED_HERE));
SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, node_moved_to,
relpath_depth(ancestor_moved_to)));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (!have_row)
ancestor_moved_to = NULL;
else if (!svn_sqlite__column_int(stmt, 0))
{
svn_wc__db_status_t presence
= svn_sqlite__column_token(stmt, 1, presence_map);
if (presence != svn_wc__db_status_not_present)
ancestor_moved_to = NULL;
else
{
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (!have_row && !svn_sqlite__column_int(stmt, 0))
ancestor_moved_to = NULL;
}
}
SVN_ERR(svn_sqlite__reset(stmt));
if (!ancestor_moved_to)
break;
/* verify repos_path points back? */
}
if (ancestor_moved_to)
{
struct svn_wc__db_moved_to_t *moved_to;
moved_to = apr_palloc(result_pool, sizeof(*moved_to));
moved_to->op_depth = working_op_depth;
moved_to->local_relpath = node_moved_to;
APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to;
SVN_ERR(follow_moved_to(moved_tos, relpath_depth(ancestor_moved_to),
repos_path, revision, wcroot, node_moved_to,
result_pool, scratch_pool));
break;
}
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_follow_moved_to(apr_array_header_t **moved_tos,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
*moved_tos = apr_array_make(result_pool, 0,
sizeof(struct svn_wc__db_moved_to_t *));
/* ### Wrap in a transaction */
SVN_ERR(follow_moved_to(moved_tos, 0, NULL, SVN_INVALID_REVNUM,
wcroot, local_relpath,
result_pool, scratch_pool));
/* ### Convert moved_to to abspath */
return SVN_NO_ERROR;
}
/* Extract the moved-to information for LOCAL_RELPATH at OP-DEPTH by
examining the lowest working node above OP_DEPTH. The output paths
are NULL if there is no move, otherwise:
*MOVE_DST_RELPATH: the moved-to destination of LOCAL_RELPATH.
*MOVE_DST_OP_ROOT_RELPATH: the moved-to destination of the root of
the move of LOCAL_RELPATH. This may be equal to *MOVE_DST_RELPATH
if LOCAL_RELPATH is the root of the move.
*MOVE_SRC_ROOT_RELPATH: the root of the move source. For moves
inside a delete this will be different from *MOVE_SRC_OP_ROOT_RELPATH.
*MOVE_SRC_OP_ROOT_RELPATH: the root of the source layer that
contains the move. For moves inside deletes this is the root of
the delete, for other moves this is the root of the move.
Given a path A/B/C with A/B moved to X then for A/B/C
MOVE_DST_RELPATH is X/C
MOVE_DST_OP_ROOT_RELPATH is X
MOVE_SRC_ROOT_RELPATH is A/B
MOVE_SRC_OP_ROOT_RELPATH is A/B
If A is then deleted the MOVE_DST_RELPATH, MOVE_DST_OP_ROOT_RELPATH
and MOVE_SRC_ROOT_RELPATH remain the same but MOVE_SRC_OP_ROOT_RELPATH
changes to A.
### Think about combining with scan_deletion? Also with
### scan_addition to get moved-to for replaces? Do we need to
### return the op-root of the move source, i.e. A/B in the example
### above? */
svn_error_t *
svn_wc__db_op_depth_moved_to(const char **move_dst_relpath,
const char **move_dst_op_root_relpath,
const char **move_src_root_relpath,
const char **move_src_op_root_relpath,
int op_depth,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
int delete_op_depth;
const char *relpath = local_relpath;
*move_dst_relpath = *move_dst_op_root_relpath = NULL;
*move_src_root_relpath = *move_src_op_root_relpath = NULL;
do
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_LOWEST_WORKING_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, relpath, op_depth));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row)
{
delete_op_depth = svn_sqlite__column_int(stmt, 0);
*move_dst_op_root_relpath = svn_sqlite__column_text(stmt, 3,
result_pool);
if (*move_dst_op_root_relpath)
*move_src_root_relpath = apr_pstrdup(result_pool, relpath);
}
SVN_ERR(svn_sqlite__reset(stmt));
if (!*move_dst_op_root_relpath)
relpath = svn_relpath_dirname(relpath, scratch_pool);
}
while (!*move_dst_op_root_relpath
&& have_row && delete_op_depth <= relpath_depth(relpath));
if (*move_dst_op_root_relpath)
{
*move_dst_relpath
= svn_relpath_join(*move_dst_op_root_relpath,
svn_relpath_skip_ancestor(relpath, local_relpath),
result_pool);
while (delete_op_depth < relpath_depth(relpath))
relpath = svn_relpath_dirname(relpath, scratch_pool);
*move_src_op_root_relpath = apr_pstrdup(result_pool, relpath);
}
return SVN_NO_ERROR;
}
/* Public (within libsvn_wc) absolute path version of
svn_wc__db_op_depth_moved_to with the op-depth hard-coded to
BASE. */
svn_error_t *
svn_wc__db_base_moved_to(const char **move_dst_abspath,
const char **move_dst_op_root_abspath,
const char **move_src_root_abspath,
const char **move_src_op_root_abspath,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
const char *move_dst_relpath, *move_dst_op_root_relpath;
const char *move_src_root_relpath, *move_src_op_root_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_WC__DB_WITH_TXN(svn_wc__db_op_depth_moved_to(&move_dst_relpath,
&move_dst_op_root_relpath,
&move_src_root_relpath,
&move_src_op_root_relpath,
0 /* BASE op-depth */,
wcroot, local_relpath,
scratch_pool, scratch_pool),
wcroot);
if (move_dst_abspath)
*move_dst_abspath
= move_dst_relpath
? svn_dirent_join(wcroot->abspath, move_dst_relpath, result_pool)
: NULL;
if (move_dst_op_root_abspath)
*move_dst_op_root_abspath
= move_dst_op_root_relpath
? svn_dirent_join(wcroot->abspath, move_dst_op_root_relpath, result_pool)
: NULL;
if (move_src_root_abspath)
*move_src_root_abspath
= move_src_root_relpath
? svn_dirent_join(wcroot->abspath, move_src_root_relpath, result_pool)
: NULL;
if (move_src_op_root_abspath)
*move_src_op_root_abspath
= move_src_op_root_relpath
? svn_dirent_join(wcroot->abspath, move_src_op_root_relpath, result_pool)
: NULL;
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_upgrade_begin(svn_sqlite__db_t **sdb,
apr_int64_t *repos_id,
apr_int64_t *wc_id,
svn_wc__db_t *wc_db,
const char *dir_abspath,
const char *repos_root_url,
const char *repos_uuid,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
/* Upgrade is inherently exclusive so specify exclusive locking. */
SVN_ERR(create_db(sdb, repos_id, wc_id, dir_abspath,
repos_root_url, repos_uuid,
SDB_FILE,
NULL, SVN_INVALID_REVNUM, svn_depth_unknown,
TRUE /* exclusive */,
wc_db->state_pool, scratch_pool));
SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot,
apr_pstrdup(wc_db->state_pool,
dir_abspath),
*sdb, *wc_id, FORMAT_FROM_SDB,
FALSE /* auto-upgrade */,
FALSE /* enforce_empty_wq */,
wc_db->state_pool, scratch_pool));
/* The WCROOT is complete. Stash it into DB. */
svn_hash_sets(wc_db->dir_data, wcroot->abspath, wcroot);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_upgrade_apply_dav_cache(svn_sqlite__db_t *sdb,
const char *dir_relpath,
apr_hash_t *cache_values,
apr_pool_t *scratch_pool)
{
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
apr_int64_t wc_id;
apr_hash_index_t *hi;
svn_sqlite__stmt_t *stmt;
SVN_ERR(svn_wc__db_util_fetch_wc_id(&wc_id, sdb, iterpool));
SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
STMT_UPDATE_BASE_NODE_DAV_CACHE));
/* Iterate over all the wcprops, writing each one to the wc_db. */
for (hi = apr_hash_first(scratch_pool, cache_values);
hi;
hi = apr_hash_next(hi))
{
const char *name = svn__apr_hash_index_key(hi);
apr_hash_t *props = svn__apr_hash_index_val(hi);
const char *local_relpath;
svn_pool_clear(iterpool);
local_relpath = svn_relpath_join(dir_relpath, name, iterpool);
SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, iterpool));
SVN_ERR(svn_sqlite__step_done(stmt));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_upgrade_apply_props(svn_sqlite__db_t *sdb,
const char *dir_abspath,
const char *local_relpath,
apr_hash_t *base_props,
apr_hash_t *revert_props,
apr_hash_t *working_props,
int original_format,
apr_int64_t wc_id,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
int top_op_depth = -1;
int below_op_depth = -1;
svn_wc__db_status_t top_presence;
svn_wc__db_status_t below_presence;
int affected_rows;
/* ### working_props: use set_props_txn.
### if working_props == NULL, then skip. what if they equal the
### pristine props? we should probably do the compare here.
###
### base props go into WORKING_NODE if avail, otherwise BASE.
###
### revert only goes into BASE. (and WORKING better be there!)
Prior to 1.4.0 (ORIGINAL_FORMAT < 8), REVERT_PROPS did not exist. If a
file was deleted, then a copy (potentially with props) was disallowed
and could not replace the deletion. An addition *could* be performed,
but that would never bring its own props.
1.4.0 through 1.4.5 created the concept of REVERT_PROPS, but had a
bug in svn_wc_add_repos_file2() whereby a copy-with-props did NOT
construct a REVERT_PROPS if the target had no props. Thus, reverting
the delete/copy would see no REVERT_PROPS to restore, leaving the
props from the copy source intact, and appearing as if they are (now)
the base props for the previously-deleted file. (wc corruption)
1.4.6 ensured that an empty REVERT_PROPS would be established at all
times. See issue 2530, and r861670 as starting points.
We will use ORIGINAL_FORMAT and SVN_WC__NO_REVERT_FILES to determine
the handling of our inputs, relative to the state of this node.
*/
SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_NODE_INFO));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row)
{
top_op_depth = svn_sqlite__column_int(stmt, 0);
top_presence = svn_sqlite__column_token(stmt, 3, presence_map);
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row)
{
below_op_depth = svn_sqlite__column_int(stmt, 0);
below_presence = svn_sqlite__column_token(stmt, 3, presence_map);
}
}
SVN_ERR(svn_sqlite__reset(stmt));
/* Detect the buggy scenario described above. We cannot upgrade this
working copy if we have no idea where BASE_PROPS should go. */
if (original_format > SVN_WC__NO_REVERT_FILES
&& revert_props == NULL
&& top_op_depth != -1
&& top_presence == svn_wc__db_status_normal
&& below_op_depth != -1
&& below_presence != svn_wc__db_status_not_present)
{
/* There should be REVERT_PROPS, so it appears that we just ran into
the described bug. Sigh. */
return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
_("The properties of '%s' are in an "
"indeterminate state and cannot be "
"upgraded. See issue #2530."),
svn_dirent_local_style(
svn_dirent_join(dir_abspath, local_relpath,
scratch_pool), scratch_pool));
}
/* Need at least one row, or two rows if there are revert props */
if (top_op_depth == -1
|| (below_op_depth == -1 && revert_props))
return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
_("Insufficient NODES rows for '%s'"),
svn_dirent_local_style(
svn_dirent_join(dir_abspath, local_relpath,
scratch_pool), scratch_pool));
/* one row, base props only: upper row gets base props
two rows, base props only: lower row gets base props
two rows, revert props only: lower row gets revert props
two rows, base and revert props: upper row gets base, lower gets revert */
if (revert_props || below_op_depth == -1)
{
SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
STMT_UPDATE_NODE_PROPS));
SVN_ERR(svn_sqlite__bindf(stmt, "isd",
wc_id, local_relpath, top_op_depth));
SVN_ERR(svn_sqlite__bind_properties(stmt, 4, base_props, scratch_pool));
SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
SVN_ERR_ASSERT(affected_rows == 1);
}
if (below_op_depth != -1)
{
apr_hash_t *props = revert_props ? revert_props : base_props;
SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
STMT_UPDATE_NODE_PROPS));
SVN_ERR(svn_sqlite__bindf(stmt, "isd",
wc_id, local_relpath, below_op_depth));
SVN_ERR(svn_sqlite__bind_properties(stmt, 4, props, scratch_pool));
SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
SVN_ERR_ASSERT(affected_rows == 1);
}
/* If there are WORKING_PROPS, then they always go into ACTUAL_NODE. */
if (working_props != NULL
&& base_props != NULL)
{
apr_array_header_t *diffs;
SVN_ERR(svn_prop_diffs(&diffs, working_props, base_props, scratch_pool));
if (diffs->nelts == 0)
working_props = NULL; /* No differences */
}
if (working_props != NULL)
{
SVN_ERR(set_actual_props(wc_id, local_relpath, working_props,
sdb, scratch_pool));
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_upgrade_insert_external(svn_wc__db_t *db,
const char *local_abspath,
svn_node_kind_t kind,
const char *parent_abspath,
const char *def_local_abspath,
const char *repos_relpath,
const char *repos_root_url,
const char *repos_uuid,
svn_revnum_t def_peg_revision,
svn_revnum_t def_revision,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *def_local_relpath;
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
apr_int64_t repos_id;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
/* We know only of DEF_LOCAL_ABSPATH that it definitely belongs to "this"
* WC, i.e. where the svn:externals prop is set. The external target path
* itself may be "hidden behind" other working copies. */
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &def_local_relpath,
db, def_local_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_REPOSITORY));
SVN_ERR(svn_sqlite__bindf(stmt, "s", repos_root_url));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row)
repos_id = svn_sqlite__column_int64(stmt, 0);
SVN_ERR(svn_sqlite__reset(stmt));
if (!have_row)
{
/* Need to set up a new repository row. */
SVN_ERR(create_repos_id(&repos_id, repos_root_url, repos_uuid,
wcroot->sdb, scratch_pool));
}
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_INSERT_EXTERNAL));
/* wc_id, local_relpath, parent_relpath, presence, kind, def_local_relpath,
* repos_id, def_repos_relpath, def_operational_revision, def_revision */
SVN_ERR(svn_sqlite__bindf(stmt, "issstsis",
wcroot->wc_id,
svn_dirent_skip_ancestor(wcroot->abspath,
local_abspath),
svn_dirent_skip_ancestor(wcroot->abspath,
parent_abspath),
"normal",
kind_map, kind,
def_local_relpath,
repos_id,
repos_relpath));
if (SVN_IS_VALID_REVNUM(def_peg_revision))
SVN_ERR(svn_sqlite__bind_revnum(stmt, 9, def_peg_revision));
if (SVN_IS_VALID_REVNUM(def_revision))
SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, def_revision));
SVN_ERR(svn_sqlite__insert(NULL, stmt));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_upgrade_get_repos_id(apr_int64_t *repos_id,
svn_sqlite__db_t *sdb,
const char *repos_root_url,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_REPOSITORY));
SVN_ERR(svn_sqlite__bindf(stmt, "s", repos_root_url));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (!have_row)
return svn_error_createf(SVN_ERR_WC_DB_ERROR, svn_sqlite__reset(stmt),
_("Repository '%s' not found in the database"),
repos_root_url);
*repos_id = svn_sqlite__column_int64(stmt, 0);
return svn_error_trace(svn_sqlite__reset(stmt));
}
svn_error_t *
svn_wc__db_wq_add(svn_wc__db_t *db,
const char *wri_abspath,
const svn_skel_t *work_item,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
/* Quick exit, if there are no work items to queue up. */
if (work_item == NULL)
return SVN_NO_ERROR;
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
wri_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
/* Add the work item(s) to the WORK_QUEUE. */
return svn_error_trace(add_work_items(wcroot->sdb, work_item,
scratch_pool));
}
/* The body of svn_wc__db_wq_fetch_next().
*/
static svn_error_t *
wq_fetch_next(apr_uint64_t *id,
svn_skel_t **work_item,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_uint64_t completed_id,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
if (completed_id != 0)
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_WORK_ITEM));
SVN_ERR(svn_sqlite__bind_int64(stmt, 1, completed_id));
SVN_ERR(svn_sqlite__step_done(stmt));
}
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_WORK_ITEM));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (!have_row)
{
*id = 0;
*work_item = NULL;
}
else
{
apr_size_t len;
const void *val;
*id = svn_sqlite__column_int64(stmt, 0);
val = svn_sqlite__column_blob(stmt, 1, &len, result_pool);
*work_item = svn_skel__parse(val, len, result_pool);
}
return svn_error_trace(svn_sqlite__reset(stmt));
}
svn_error_t *
svn_wc__db_wq_fetch_next(apr_uint64_t *id,
svn_skel_t **work_item,
svn_wc__db_t *db,
const char *wri_abspath,
apr_uint64_t completed_id,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(id != NULL);
SVN_ERR_ASSERT(work_item != NULL);
SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
wri_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_WC__DB_WITH_TXN(
wq_fetch_next(id, work_item,
wcroot, local_relpath, completed_id,
result_pool, scratch_pool),
wcroot);
return SVN_NO_ERROR;
}
/* Records timestamp and date for one or more files in wcroot */
static svn_error_t *
wq_record(svn_wc__db_wcroot_t *wcroot,
apr_hash_t *record_map,
apr_pool_t *scratch_pool)
{
apr_hash_index_t *hi;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
for (hi = apr_hash_first(scratch_pool, record_map); hi;
hi = apr_hash_next(hi))
{
const char *local_abspath = svn__apr_hash_index_key(hi);
const svn_io_dirent2_t *dirent = svn__apr_hash_index_val(hi);
const char *local_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
local_abspath);
svn_pool_clear(iterpool);
if (! local_relpath)
continue;
SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
dirent->filesize, dirent->mtime,
iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_wq_record_and_fetch_next(apr_uint64_t *id,
svn_skel_t **work_item,
svn_wc__db_t *db,
const char *wri_abspath,
apr_uint64_t completed_id,
apr_hash_t *record_map,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(id != NULL);
SVN_ERR_ASSERT(work_item != NULL);
SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
wri_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_WC__DB_WITH_TXN(
svn_error_compose_create(
wq_fetch_next(id, work_item,
wcroot, local_relpath, completed_id,
result_pool, scratch_pool),
wq_record(wcroot, record_map, scratch_pool)),
wcroot);
return SVN_NO_ERROR;
}
/* ### temporary API. remove before release. */
svn_error_t *
svn_wc__db_temp_get_format(int *format,
svn_wc__db_t *db,
const char *local_dir_abspath,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
svn_error_t *err;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
/* ### assert that we were passed a directory? */
err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_dir_abspath, scratch_pool, scratch_pool);
/* If we hit an error examining this directory, then declare this
directory to not be a working copy. */
if (err)
{
if (err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
return svn_error_trace(err);
svn_error_clear(err);
/* Remap the returned error. */
*format = 0;
return svn_error_createf(SVN_ERR_WC_MISSING, NULL,
_("'%s' is not a working copy"),
svn_dirent_local_style(local_dir_abspath,
scratch_pool));
}
SVN_ERR_ASSERT(wcroot != NULL);
SVN_ERR_ASSERT(wcroot->format >= 1);
*format = wcroot->format;
return SVN_NO_ERROR;
}
/* ### temporary API. remove before release. */
svn_wc_adm_access_t *
svn_wc__db_temp_get_access(svn_wc__db_t *db,
const char *local_dir_abspath,
apr_pool_t *scratch_pool)
{
const char *local_relpath;
svn_wc__db_wcroot_t *wcroot;
svn_error_t *err;
SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
/* ### we really need to assert that we were passed a directory. sometimes
### adm_retrieve_internal is asked about a file, and then it asks us
### for an access baton for it. we should definitely return NULL, but
### ideally: the caller would never ask us about a non-directory. */
err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
db, local_dir_abspath, scratch_pool, scratch_pool);
if (err)
{
svn_error_clear(err);
return NULL;
}
if (!wcroot)
return NULL;
return svn_hash_gets(wcroot->access_cache, local_dir_abspath);
}
/* ### temporary API. remove before release. */
void
svn_wc__db_temp_set_access(svn_wc__db_t *db,
const char *local_dir_abspath,
svn_wc_adm_access_t *adm_access,
apr_pool_t *scratch_pool)
{
const char *local_relpath;
svn_wc__db_wcroot_t *wcroot;
svn_error_t *err;
SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
/* ### assert that we were passed a directory? */
err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
db, local_dir_abspath, scratch_pool, scratch_pool);
if (err)
{
/* We don't even have a wcroot, so just bail. */
svn_error_clear(err);
return;
}
/* Better not override something already there. */
SVN_ERR_ASSERT_NO_RETURN(
svn_hash_gets(wcroot->access_cache, local_dir_abspath) == NULL
);
svn_hash_sets(wcroot->access_cache, local_dir_abspath, adm_access);
}
/* ### temporary API. remove before release. */
svn_error_t *
svn_wc__db_temp_close_access(svn_wc__db_t *db,
const char *local_dir_abspath,
svn_wc_adm_access_t *adm_access,
apr_pool_t *scratch_pool)
{
const char *local_relpath;
svn_wc__db_wcroot_t *wcroot;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
/* ### assert that we were passed a directory? */
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_dir_abspath, scratch_pool, scratch_pool));
svn_hash_sets(wcroot->access_cache, local_dir_abspath, NULL);
return SVN_NO_ERROR;
}
/* ### temporary API. remove before release. */
void
svn_wc__db_temp_clear_access(svn_wc__db_t *db,
const char *local_dir_abspath,
apr_pool_t *scratch_pool)
{
const char *local_relpath;
svn_wc__db_wcroot_t *wcroot;
svn_error_t *err;
SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
/* ### assert that we were passed a directory? */
err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
db, local_dir_abspath, scratch_pool, scratch_pool);
if (err)
{
svn_error_clear(err);
return;
}
svn_hash_sets(wcroot->access_cache, local_dir_abspath, NULL);
}
apr_hash_t *
svn_wc__db_temp_get_all_access(svn_wc__db_t *db,
apr_pool_t *result_pool)
{
apr_hash_t *result = apr_hash_make(result_pool);
apr_hash_index_t *hi;
for (hi = apr_hash_first(result_pool, db->dir_data);
hi;
hi = apr_hash_next(hi))
{
const svn_wc__db_wcroot_t *wcroot = svn__apr_hash_index_val(hi);
/* This is highly redundant, 'cause the same WCROOT will appear many
times in dir_data. */
result = apr_hash_overlay(result_pool, result, wcroot->access_cache);
}
return result;
}
svn_error_t *
svn_wc__db_temp_borrow_sdb(svn_sqlite__db_t **sdb,
svn_wc__db_t *db,
const char *local_dir_abspath,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_dir_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
*sdb = wcroot->sdb;
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_read_conflict_victims(const apr_array_header_t **victims,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
apr_array_header_t *new_victims;
/* The parent should be a working copy directory. */
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
/* ### This will be much easier once we have all conflicts in one
field of actual*/
/* Look for text, tree and property conflicts in ACTUAL */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_CONFLICT_VICTIMS));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
new_victims = apr_array_make(result_pool, 0, sizeof(const char *));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
while (have_row)
{
const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
APR_ARRAY_PUSH(new_victims, const char *) =
svn_relpath_basename(child_relpath, result_pool);
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
SVN_ERR(svn_sqlite__reset(stmt));
*victims = new_victims;
return SVN_NO_ERROR;
}
/* The body of svn_wc__db_get_conflict_marker_files().
*/
static svn_error_t *
get_conflict_marker_files(apr_hash_t **marker_files_p,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
svn_wc__db_t *db,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
apr_hash_t *marker_files = apr_hash_make(result_pool);
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_ACTUAL_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row && !svn_sqlite__column_is_null(stmt, 2))
{
apr_size_t len;
const void *data = svn_sqlite__column_blob(stmt, 2, &len, NULL);
svn_skel_t *conflicts;
const apr_array_header_t *markers;
int i;
conflicts = svn_skel__parse(data, len, scratch_pool);
/* ### ADD markers to *marker_files */
SVN_ERR(svn_wc__conflict_read_markers(&markers, db, wcroot->abspath,
conflicts,
result_pool, scratch_pool));
for (i = 0; markers && (i < markers->nelts); i++)
{
const char *marker_abspath = APR_ARRAY_IDX(markers, i, const char*);
svn_hash_sets(marker_files, marker_abspath, "");
}
}
SVN_ERR(svn_sqlite__reset(stmt));
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_CONFLICT_VICTIMS));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
while (have_row)
{
apr_size_t len;
const void *data = svn_sqlite__column_blob(stmt, 1, &len, NULL);
const apr_array_header_t *markers;
int i;
if (data)
{
svn_skel_t *conflicts;
conflicts = svn_skel__parse(data, len, scratch_pool);
SVN_ERR(svn_wc__conflict_read_markers(&markers, db, wcroot->abspath,
conflicts,
result_pool, scratch_pool));
for (i = 0; markers && (i < markers->nelts); i++)
{
const char *marker_abspath = APR_ARRAY_IDX(markers, i, const char*);
svn_hash_sets(marker_files, marker_abspath, "");
}
}
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
if (apr_hash_count(marker_files))
*marker_files_p = marker_files;
else
*marker_files_p = NULL;
return svn_error_trace(svn_sqlite__reset(stmt));
}
svn_error_t *
svn_wc__db_get_conflict_marker_files(apr_hash_t **marker_files,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
/* The parent should be a working copy directory. */
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_WC__DB_WITH_TXN(
get_conflict_marker_files(marker_files, wcroot, local_relpath, db,
result_pool, scratch_pool),
wcroot);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_read_conflict(svn_skel_t **conflict,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
/* The parent should be a working copy directory. */
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
return svn_error_trace(svn_wc__db_read_conflict_internal(conflict, wcroot,
local_relpath,
result_pool,
scratch_pool));
}
svn_error_t *
svn_wc__db_read_conflict_internal(svn_skel_t **conflict,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
/* Check if we have a conflict in ACTUAL */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_ACTUAL_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (! have_row)
{
/* Do this while stmt is still open to avoid closing the sqlite
transaction and then reopening. */
svn_sqlite__stmt_t *stmt_node;
svn_error_t *err;
err = svn_sqlite__get_statement(&stmt_node, wcroot->sdb,
STMT_SELECT_NODE_INFO);
if (err)
stmt_node = NULL;
else
err = svn_sqlite__bindf(stmt_node, "is", wcroot->wc_id,
local_relpath);
if (!err)
err = svn_sqlite__step(&have_row, stmt_node);
if (stmt_node)
err = svn_error_compose_create(err,
svn_sqlite__reset(stmt_node));
SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
if (have_row)
{
*conflict = NULL;
return SVN_NO_ERROR;
}
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("The node '%s' was not found."),
path_for_error_message(wcroot,
local_relpath,
scratch_pool));
}
{
apr_size_t cfl_len;
const void *cfl_data;
/* svn_skel__parse doesn't copy data, so store in result_pool */
cfl_data = svn_sqlite__column_blob(stmt, 2, &cfl_len, result_pool);
if (cfl_data)
*conflict = svn_skel__parse(cfl_data, cfl_len, result_pool);
else
*conflict = NULL;
return svn_error_trace(svn_sqlite__reset(stmt));
}
}
svn_error_t *
svn_wc__db_read_kind(svn_node_kind_t *kind,
svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t allow_missing,
svn_boolean_t show_deleted,
svn_boolean_t show_hidden,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
svn_sqlite__stmt_t *stmt_info;
svn_boolean_t have_info;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
STMT_SELECT_NODE_INFO));
SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
if (!have_info)
{
if (allow_missing)
{
*kind = svn_node_unknown;
SVN_ERR(svn_sqlite__reset(stmt_info));
return SVN_NO_ERROR;
}
else
{
SVN_ERR(svn_sqlite__reset(stmt_info));
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("The node '%s' was not found."),
path_for_error_message(wcroot,
local_relpath,
scratch_pool));
}
}
if (!(show_deleted && show_hidden))
{
int op_depth = svn_sqlite__column_int(stmt_info, 0);
svn_boolean_t report_none = FALSE;
svn_wc__db_status_t status = svn_sqlite__column_token(stmt_info, 3,
presence_map);
if (op_depth > 0)
SVN_ERR(convert_to_working_status(&status, status));
switch (status)
{
case svn_wc__db_status_not_present:
if (! (show_hidden && show_deleted))
report_none = TRUE;
break;
case svn_wc__db_status_excluded:
case svn_wc__db_status_server_excluded:
if (! show_hidden)
report_none = TRUE;
break;
case svn_wc__db_status_deleted:
if (! show_deleted)
report_none = TRUE;
break;
default:
break;
}
if (report_none)
{
*kind = svn_node_none;
return svn_error_trace(svn_sqlite__reset(stmt_info));
}
}
*kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
return svn_error_trace(svn_sqlite__reset(stmt_info));
}
svn_error_t *
svn_wc__db_node_hidden(svn_boolean_t *hidden,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
svn_wc__db_status_t status;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_ERR(read_info(&status, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL,
wcroot, local_relpath,
scratch_pool, scratch_pool));
*hidden = (status == svn_wc__db_status_server_excluded
|| status == svn_wc__db_status_not_present
|| status == svn_wc__db_status_excluded);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_is_wcroot(svn_boolean_t *is_wcroot,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
if (*local_relpath != '\0')
{
*is_wcroot = FALSE; /* Node is a file, or has a parent directory within
the same wcroot */
return SVN_NO_ERROR;
}
*is_wcroot = TRUE;
return SVN_NO_ERROR;
}
/* Find a node's kind and whether it is switched, putting the outputs in
* *IS_SWITCHED and *KIND. Either of the outputs may be NULL if not wanted.
*/
static svn_error_t *
db_is_switched(svn_boolean_t *is_switched,
svn_node_kind_t *kind,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *scratch_pool)
{
svn_wc__db_status_t status;
apr_int64_t repos_id;
const char *repos_relpath;
const char *name;
const char *parent_local_relpath;
apr_int64_t parent_repos_id;
const char *parent_repos_relpath;
SVN_ERR_ASSERT(*local_relpath != '\0'); /* Handled in wrapper */
SVN_ERR(read_info(&status, kind, NULL, &repos_relpath, &repos_id, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
wcroot, local_relpath, scratch_pool, scratch_pool));
if (status == svn_wc__db_status_server_excluded
|| status == svn_wc__db_status_excluded
|| status == svn_wc__db_status_not_present)
{
return svn_error_createf(
SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("The node '%s' was not found."),
path_for_error_message(wcroot, local_relpath,
scratch_pool));
}
else if (! repos_relpath)
{
/* Node is shadowed; easy out */
if (is_switched)
*is_switched = FALSE;
return SVN_NO_ERROR;
}
if (! is_switched)
return SVN_NO_ERROR;
svn_relpath_split(&parent_local_relpath, &name, local_relpath, scratch_pool);
SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
&parent_repos_relpath,
&parent_repos_id, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL,
wcroot, parent_local_relpath,
scratch_pool, scratch_pool));
if (repos_id != parent_repos_id)
*is_switched = TRUE;
else
{
const char *expected_relpath;
expected_relpath = svn_relpath_join(parent_repos_relpath, name,
scratch_pool);
*is_switched = (strcmp(expected_relpath, repos_relpath) != 0);
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_is_switched(svn_boolean_t *is_wcroot,
svn_boolean_t *is_switched,
svn_node_kind_t *kind,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
if (is_switched)
*is_switched = FALSE;
if (*local_relpath == '\0')
{
/* Easy out */
if (is_wcroot)
*is_wcroot = TRUE;
if (kind)
*kind = svn_node_dir;
return SVN_NO_ERROR;
}
if (is_wcroot)
*is_wcroot = FALSE;
if (! is_switched && ! kind)
return SVN_NO_ERROR;
SVN_WC__DB_WITH_TXN(
db_is_switched(is_switched, kind, wcroot, local_relpath, scratch_pool),
wcroot);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_temp_wcroot_tempdir(const char **temp_dir_abspath,
svn_wc__db_t *db,
const char *wri_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(temp_dir_abspath != NULL);
SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
wri_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
*temp_dir_abspath = svn_dirent_join_many(result_pool,
wcroot->abspath,
svn_wc_get_adm_dir(scratch_pool),
WCROOT_TEMPDIR_RELPATH,
NULL);
return SVN_NO_ERROR;
}
/* Helper for wclock_obtain_cb() to steal an existing lock */
static svn_error_t *
wclock_steal(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_WC_LOCK));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
return SVN_NO_ERROR;
}
/* The body of svn_wc__db_wclock_obtain().
*/
static svn_error_t *
wclock_obtain_cb(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
int levels_to_lock,
svn_boolean_t steal_lock,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_error_t *err;
const char *lock_relpath;
int max_depth;
int lock_depth;
svn_boolean_t got_row;
svn_wc__db_wclock_t lock;
/* Upgrade locks the root before the node exists. Apart from that
the root node always exists so we will just skip the check.
### Perhaps the lock for upgrade should be created when the db is
created? 1.6 used to lock .svn on creation. */
if (local_relpath[0])
{
svn_boolean_t exists;
SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
if (!exists)
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("The node '%s' was not found."),
path_for_error_message(wcroot,
local_relpath,
scratch_pool));
}
/* Check if there are nodes locked below the new lock root */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_FIND_WC_LOCK));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
lock_depth = relpath_depth(local_relpath);
max_depth = lock_depth + levels_to_lock;
SVN_ERR(svn_sqlite__step(&got_row, stmt));
while (got_row)
{
svn_boolean_t own_lock;
lock_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
/* If we are not locking with depth infinity, check if this lock
voids our lock request */
if (levels_to_lock >= 0
&& relpath_depth(lock_relpath) > max_depth)
{
SVN_ERR(svn_sqlite__step(&got_row, stmt));
continue;
}
/* Check if we are the lock owner, because we should be able to
extend our lock. */
err = wclock_owns_lock(&own_lock, wcroot, lock_relpath,
TRUE, scratch_pool);
if (err)
SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
if (!own_lock && !steal_lock)
{
SVN_ERR(svn_sqlite__reset(stmt));
err = svn_error_createf(SVN_ERR_WC_LOCKED, NULL,
_("'%s' is already locked."),
path_for_error_message(wcroot,
lock_relpath,
scratch_pool));
return svn_error_createf(SVN_ERR_WC_LOCKED, err,
_("Working copy '%s' locked."),
path_for_error_message(wcroot,
local_relpath,
scratch_pool));
}
else if (!own_lock)
{
err = wclock_steal(wcroot, lock_relpath, scratch_pool);
if (err)
SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
}
SVN_ERR(svn_sqlite__step(&got_row, stmt));
}
SVN_ERR(svn_sqlite__reset(stmt));
if (steal_lock)
SVN_ERR(wclock_steal(wcroot, local_relpath, scratch_pool));
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_WC_LOCK));
lock_relpath = local_relpath;
while (TRUE)
{
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, lock_relpath));
SVN_ERR(svn_sqlite__step(&got_row, stmt));
if (got_row)
{
int levels = svn_sqlite__column_int(stmt, 0);
if (levels >= 0)
levels += relpath_depth(lock_relpath);
SVN_ERR(svn_sqlite__reset(stmt));
if (levels == -1 || levels >= lock_depth)
{
err = svn_error_createf(
SVN_ERR_WC_LOCKED, NULL,
_("'%s' is already locked."),
svn_dirent_local_style(
svn_dirent_join(wcroot->abspath,
lock_relpath,
scratch_pool),
scratch_pool));
return svn_error_createf(
SVN_ERR_WC_LOCKED, err,
_("Working copy '%s' locked."),
path_for_error_message(wcroot,
local_relpath,
scratch_pool));
}
break; /* There can't be interesting locks on higher nodes */
}
else
SVN_ERR(svn_sqlite__reset(stmt));
if (!*lock_relpath)
break;
lock_relpath = svn_relpath_dirname(lock_relpath, scratch_pool);
}
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_WC_LOCK));
SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
levels_to_lock));
err = svn_sqlite__insert(NULL, stmt);
if (err)
return svn_error_createf(SVN_ERR_WC_LOCKED, err,
_("Working copy '%s' locked"),
path_for_error_message(wcroot,
local_relpath,
scratch_pool));
/* And finally store that we obtained the lock */
lock.local_relpath = apr_pstrdup(wcroot->owned_locks->pool, local_relpath);
lock.levels = levels_to_lock;
APR_ARRAY_PUSH(wcroot->owned_locks, svn_wc__db_wclock_t) = lock;
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_wclock_obtain(svn_wc__db_t *db,
const char *local_abspath,
int levels_to_lock,
svn_boolean_t steal_lock,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(levels_to_lock >= -1);
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
db, local_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
if (!steal_lock)
{
int i;
int depth = relpath_depth(local_relpath);
for (i = 0; i < wcroot->owned_locks->nelts; i++)
{
svn_wc__db_wclock_t* lock = &APR_ARRAY_IDX(wcroot->owned_locks,
i, svn_wc__db_wclock_t);
if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
&& (lock->levels == -1
|| (lock->levels + relpath_depth(lock->local_relpath))
>= depth))
{
return svn_error_createf(
SVN_ERR_WC_LOCKED, NULL,
_("'%s' is already locked via '%s'."),
svn_dirent_local_style(local_abspath, scratch_pool),
path_for_error_message(wcroot, lock->local_relpath,
scratch_pool));
}
}
}
SVN_WC__DB_WITH_TXN(
wclock_obtain_cb(wcroot, local_relpath, levels_to_lock, steal_lock,
scratch_pool),
wcroot);
return SVN_NO_ERROR;
}
/* The body of svn_wc__db_wclock_find_root() and svn_wc__db_wclocked(). */
static svn_error_t *
find_wclock(const char **lock_relpath,
svn_wc__db_wcroot_t *wcroot,
const char *dir_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
int dir_depth = relpath_depth(dir_relpath);
const char *first_relpath;
/* Check for locks on all directories that might be ancestors.
As our new apis only use recursive locks the number of locks stored
in the DB will be very low */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_ANCESTOR_WCLOCKS));
/* Get the top level relpath to reduce the worst case number of results
to the number of directories below this node plus two.
(1: the node itself and 2: the wcroot). */
first_relpath = strchr(dir_relpath, '/');
if (first_relpath != NULL)
first_relpath = apr_pstrndup(scratch_pool, dir_relpath,
first_relpath - dir_relpath);
else
first_relpath = dir_relpath;
SVN_ERR(svn_sqlite__bindf(stmt, "iss",
wcroot->wc_id,
dir_relpath,
first_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
while (have_row)
{
const char *relpath = svn_sqlite__column_text(stmt, 0, NULL);
if (svn_relpath_skip_ancestor(relpath, dir_relpath))
{
int locked_levels = svn_sqlite__column_int(stmt, 1);
int row_depth = relpath_depth(relpath);
if (locked_levels == -1
|| locked_levels + row_depth >= dir_depth)
{
*lock_relpath = apr_pstrdup(result_pool, relpath);
SVN_ERR(svn_sqlite__reset(stmt));
return SVN_NO_ERROR;
}
}
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
*lock_relpath = NULL;
return svn_error_trace(svn_sqlite__reset(stmt));
}
static svn_error_t *
is_wclocked(svn_boolean_t *locked,
svn_wc__db_wcroot_t *wcroot,
const char *dir_relpath,
apr_pool_t *scratch_pool)
{
const char *lock_relpath;
SVN_ERR(find_wclock(&lock_relpath, wcroot, dir_relpath,
scratch_pool, scratch_pool));
*locked = (lock_relpath != NULL);
return SVN_NO_ERROR;
}
svn_error_t*
svn_wc__db_wclock_find_root(const char **lock_abspath,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
const char *lock_relpath;
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_WC__DB_WITH_TXN(
find_wclock(&lock_relpath, wcroot, local_relpath,
scratch_pool, scratch_pool),
wcroot);
if (!lock_relpath)
*lock_abspath = NULL;
else
SVN_ERR(svn_wc__db_from_relpath(lock_abspath, db, wcroot->abspath,
lock_relpath, result_pool, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_wclocked(svn_boolean_t *locked,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_WC__DB_WITH_TXN(
is_wclocked(locked, wcroot, local_relpath, scratch_pool),
wcroot);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_wclock_release(svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
int i;
apr_array_header_t *owned_locks;
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
/* First check and remove the owns-lock information as failure in
removing the db record implies that we have to steal the lock later. */
owned_locks = wcroot->owned_locks;
for (i = 0; i < owned_locks->nelts; i++)
{
svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
svn_wc__db_wclock_t);
if (strcmp(lock->local_relpath, local_relpath) == 0)
break;
}
if (i >= owned_locks->nelts)
return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
_("Working copy not locked at '%s'."),
svn_dirent_local_style(local_abspath,
scratch_pool));
if (i < owned_locks->nelts)
{
owned_locks->nelts--;
/* Move the last item in the array to the deleted place */
if (owned_locks->nelts > 0)
APR_ARRAY_IDX(owned_locks, i, svn_wc__db_wclock_t) =
APR_ARRAY_IDX(owned_locks, owned_locks->nelts, svn_wc__db_wclock_t);
}
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_WC_LOCK));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
return SVN_NO_ERROR;
}
/* Like svn_wc__db_wclock_owns_lock() but taking WCROOT+LOCAL_RELPATH instead
of DB+LOCAL_ABSPATH. */
static svn_error_t *
wclock_owns_lock(svn_boolean_t *own_lock,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
svn_boolean_t exact,
apr_pool_t *scratch_pool)
{
apr_array_header_t *owned_locks;
int lock_level;
int i;
*own_lock = FALSE;
owned_locks = wcroot->owned_locks;
lock_level = relpath_depth(local_relpath);
if (exact)
{
for (i = 0; i < owned_locks->nelts; i++)
{
svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
svn_wc__db_wclock_t);
if (strcmp(lock->local_relpath, local_relpath) == 0)
{
*own_lock = TRUE;
return SVN_NO_ERROR;
}
}
}
else
{
for (i = 0; i < owned_locks->nelts; i++)
{
svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
svn_wc__db_wclock_t);
if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
&& (lock->levels == -1
|| ((relpath_depth(lock->local_relpath) + lock->levels)
>= lock_level)))
{
*own_lock = TRUE;
return SVN_NO_ERROR;
}
}
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_wclock_owns_lock(svn_boolean_t *own_lock,
svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t exact,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
if (!wcroot)
return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
_("The node '%s' was not found."),
svn_dirent_local_style(local_abspath,
scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_ERR(wclock_owns_lock(own_lock, wcroot, local_relpath, exact,
scratch_pool));
return SVN_NO_ERROR;
}
/* The body of svn_wc__db_temp_op_end_directory_update().
*/
static svn_error_t *
end_directory_update(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_wc__db_status_t base_status;
SVN_ERR(svn_wc__db_base_get_info_internal(&base_status, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
wcroot, local_relpath,
scratch_pool, scratch_pool));
if (base_status == svn_wc__db_status_normal)
return SVN_NO_ERROR;
SVN_ERR_ASSERT(base_status == svn_wc__db_status_incomplete);
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_UPDATE_NODE_BASE_PRESENCE));
SVN_ERR(svn_sqlite__bindf(stmt, "ist", wcroot->wc_id, local_relpath,
presence_map, svn_wc__db_status_normal));
SVN_ERR(svn_sqlite__step_done(stmt));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_temp_op_end_directory_update(svn_wc__db_t *db,
const char *local_dir_abspath,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_dir_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_WC__DB_WITH_TXN(
end_directory_update(wcroot, local_relpath, scratch_pool),
wcroot);
SVN_ERR(flush_entries(wcroot, local_dir_abspath, svn_depth_empty,
scratch_pool));
return SVN_NO_ERROR;
}
/* The body of svn_wc__db_temp_op_start_directory_update().
*/
static svn_error_t *
start_directory_update_txn(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
const char *new_repos_relpath,
svn_revnum_t new_rev,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
/* Note: In the majority of calls, the repos_relpath is unchanged. */
/* ### TODO: Maybe check if we can make repos_relpath NULL. */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_UPDATE_BASE_NODE_PRESENCE_REVNUM_AND_REPOS_PATH));
SVN_ERR(svn_sqlite__bindf(stmt, "istrs",
wcroot->wc_id,
local_relpath,
presence_map, svn_wc__db_status_incomplete,
new_rev,
new_repos_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_temp_op_start_directory_update(svn_wc__db_t *db,
const char *local_abspath,
const char *new_repos_relpath,
svn_revnum_t new_rev,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_rev));
SVN_ERR_ASSERT(svn_relpath_is_canonical(new_repos_relpath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_WC__DB_WITH_TXN(
start_directory_update_txn(wcroot, local_relpath,
new_repos_relpath, new_rev, scratch_pool),
wcroot);
SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
return SVN_NO_ERROR;
}
/* The body of svn_wc__db_temp_op_make_copy(). This is
used by the update editor when deleting a base node tree would be a
tree-conflict because there are changes to subtrees. This function
inserts a copy of the base node tree below any existing working
subtrees. Given a tree:
0 1 2 3
/ normal -
A normal -
A/B normal - normal
A/B/C normal - base-del normal
A/F normal - normal
A/F/G normal - normal
A/F/H normal - base-deleted normal
A/F/E normal - not-present
A/X normal -
A/X/Y incomplete -
This function adds layers to A and some of its descendants in an attempt
to make the working copy look like as if it were a copy of the BASE nodes.
0 1 2 3
/ normal -
A normal norm
A/B normal norm norm
A/B/C normal norm base-del normal
A/F normal norm norm
A/F/G normal norm norm
A/F/H normal norm not-pres
A/F/E normal norm base-del
A/X normal norm
A/X/Y incomplete incomplete
*/
static svn_error_t *
make_copy_txn(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
int op_depth,
const svn_skel_t *conflicts,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
svn_boolean_t add_working_base_deleted = FALSE;
svn_boolean_t remove_working = FALSE;
const apr_array_header_t *children;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
int i;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_LOWEST_WORKING_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 0));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row)
{
svn_wc__db_status_t working_status;
int working_op_depth;
working_status = svn_sqlite__column_token(stmt, 1, presence_map);
working_op_depth = svn_sqlite__column_int(stmt, 0);
SVN_ERR(svn_sqlite__reset(stmt));
SVN_ERR_ASSERT(working_status == svn_wc__db_status_normal
|| working_status == svn_wc__db_status_base_deleted
|| working_status == svn_wc__db_status_not_present
|| working_status == svn_wc__db_status_incomplete);
/* Only change nodes in the layers where we are creating the copy.
Deletes in higher layers will just apply to the copy */
if (working_op_depth <= op_depth)
{
add_working_base_deleted = TRUE;
if (working_status == svn_wc__db_status_base_deleted)
remove_working = TRUE;
}
}
else
SVN_ERR(svn_sqlite__reset(stmt));
if (remove_working)
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_DELETE_LOWEST_WORKING_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_done(stmt));
}
if (add_working_base_deleted)
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_INSERT_DELETE_FROM_BASE));
SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
op_depth));
SVN_ERR(svn_sqlite__step_done(stmt));
}
else
{
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_INSERT_WORKING_NODE_FROM_BASE_COPY));
SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
op_depth));
SVN_ERR(svn_sqlite__step_done(stmt));
}
/* Get the BASE children, as WORKING children don't need modifications */
SVN_ERR(gather_repo_children(&children, wcroot, local_relpath,
0, scratch_pool, iterpool));
for (i = 0; i < children->nelts; i++)
{
const char *name = APR_ARRAY_IDX(children, i, const char *);
const char *copy_relpath;
svn_pool_clear(iterpool);
copy_relpath = svn_relpath_join(local_relpath, name, iterpool);
SVN_ERR(make_copy_txn(wcroot, copy_relpath, op_depth, NULL, NULL,
iterpool));
}
SVN_ERR(flush_entries(wcroot, svn_dirent_join(wcroot->abspath, local_relpath,
iterpool),
svn_depth_empty, iterpool));
if (conflicts)
SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
conflicts, iterpool));
SVN_ERR(add_work_items(wcroot->sdb, work_items, iterpool));
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_op_make_copy(svn_wc__db_t *db,
const char *local_abspath,
const svn_skel_t *conflicts,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
/* The update editor is supposed to call this function when there is
no working node for LOCAL_ABSPATH. */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_WORKING_NODE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
SVN_ERR(svn_sqlite__reset(stmt));
if (have_row)
return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
_("Modification of '%s' already exists"),
path_for_error_message(wcroot,
local_relpath,
scratch_pool));
/* We don't allow copies to contain server-excluded nodes;
the update editor is going to have to bail out. */
SVN_ERR(catch_copy_of_server_excluded(wcroot, local_relpath, scratch_pool));
SVN_WC__DB_WITH_TXN(
make_copy_txn(wcroot, local_relpath,
relpath_depth(local_relpath), conflicts, work_items,
scratch_pool),
wcroot);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_info_below_working(svn_boolean_t *have_base,
svn_boolean_t *have_work,
svn_wc__db_status_t *status,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_ERR(info_below_working(have_base, have_work, status,
wcroot, local_relpath, -1, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_get_not_present_descendants(const apr_array_header_t **descendants,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
local_abspath, scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_NOT_PRESENT_DESCENDANTS));
SVN_ERR(svn_sqlite__bindf(stmt, "isd",
wcroot->wc_id,
local_relpath,
relpath_depth(local_relpath)));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row)
{
apr_array_header_t *paths;
paths = apr_array_make(result_pool, 4, sizeof(const char*));
while (have_row)
{
const char *found_relpath = svn_sqlite__column_text(stmt, 0, NULL);
APR_ARRAY_PUSH(paths, const char *)
= apr_pstrdup(result_pool, svn_relpath_skip_ancestor(
local_relpath, found_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
*descendants = paths;
}
else
*descendants = apr_array_make(result_pool, 0, sizeof(const char*));
return svn_error_trace(svn_sqlite__reset(stmt));
}
/* Like svn_wc__db_min_max_revisions(),
* but accepts a WCROOT/LOCAL_RELPATH pair. */
static svn_error_t *
get_min_max_revisions(svn_revnum_t *min_revision,
svn_revnum_t *max_revision,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
svn_boolean_t committed,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_revnum_t min_rev, max_rev;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_MIN_MAX_REVISIONS));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step_row(stmt));
if (committed)
{
min_rev = svn_sqlite__column_revnum(stmt, 2);
max_rev = svn_sqlite__column_revnum(stmt, 3);
}
else
{
min_rev = svn_sqlite__column_revnum(stmt, 0);
max_rev = svn_sqlite__column_revnum(stmt, 1);
}
/* The statement returns exactly one row. */
SVN_ERR(svn_sqlite__reset(stmt));
if (min_revision)
*min_revision = min_rev;
if (max_revision)
*max_revision = max_rev;
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_min_max_revisions(svn_revnum_t *min_revision,
svn_revnum_t *max_revision,
svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t committed,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
db, local_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
return svn_error_trace(get_min_max_revisions(min_revision, max_revision,
wcroot, local_relpath,
committed, scratch_pool));
}
/* Set *IS_SPARSE_CHECKOUT TRUE if LOCAL_RELPATH or any of the nodes
* within LOCAL_RELPATH is sparse, FALSE otherwise. */
static svn_error_t *
is_sparse_checkout_internal(svn_boolean_t *is_sparse_checkout,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_HAS_SPARSE_NODES));
SVN_ERR(svn_sqlite__bindf(stmt, "is",
wcroot->wc_id,
local_relpath));
/* If this query returns a row, the working copy is sparse. */
SVN_ERR(svn_sqlite__step(&have_row, stmt));
*is_sparse_checkout = have_row;
SVN_ERR(svn_sqlite__reset(stmt));
return SVN_NO_ERROR;
}
/* Like svn_wc__db_has_switched_subtrees(),
* but accepts a WCROOT/LOCAL_RELPATH pair. */
static svn_error_t *
has_switched_subtrees(svn_boolean_t *is_switched,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
const char *trail_url,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
apr_int64_t repos_id;
const char *repos_relpath;
/* Optional argument handling for caller */
if (!is_switched)
return SVN_NO_ERROR;
*is_switched = FALSE;
SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
&repos_relpath, &repos_id,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
wcroot, local_relpath,
scratch_pool, scratch_pool));
/* First do the cheap check where we only need info on the origin itself */
if (trail_url != NULL)
{
const char *repos_root_url;
const char *url;
apr_size_t len1, len2;
/* If the trailing part of the URL of the working copy directory
does not match the given trailing URL then the whole working
copy is switched. */
SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, NULL, wcroot->sdb,
repos_id, scratch_pool));
url = svn_path_url_add_component2(repos_root_url, repos_relpath,
scratch_pool);
len1 = strlen(trail_url);
len2 = strlen(url);
if ((len1 > len2) || strcmp(url + len2 - len1, trail_url))
{
*is_switched = TRUE;
return SVN_NO_ERROR;
}
}
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_HAS_SWITCHED));
SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath, repos_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row)
*is_switched = TRUE;
SVN_ERR(svn_sqlite__reset(stmt));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_has_switched_subtrees(svn_boolean_t *is_switched,
svn_wc__db_t *db,
const char *local_abspath,
const char *trail_url,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
db, local_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
return svn_error_trace(has_switched_subtrees(is_switched, wcroot,
local_relpath, trail_url,
scratch_pool));
}
svn_error_t *
svn_wc__db_get_excluded_subtrees(apr_hash_t **excluded_subtrees,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
db, local_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_ALL_EXCLUDED_DESCENDANTS));
SVN_ERR(svn_sqlite__bindf(stmt, "is",
wcroot->wc_id,
local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row)
*excluded_subtrees = apr_hash_make(result_pool);
else
*excluded_subtrees = NULL;
while (have_row)
{
const char *abs_path =
svn_dirent_join(wcroot->abspath,
svn_sqlite__column_text(stmt, 0, NULL),
result_pool);
svn_hash_sets(*excluded_subtrees, abs_path, abs_path);
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
SVN_ERR(svn_sqlite__reset(stmt));
return SVN_NO_ERROR;
}
/* Like svn_wc__db_has_local_mods(),
* but accepts a WCROOT/LOCAL_RELPATH pair.
* ### This needs a DB as well as a WCROOT/RELPATH pair... */
static svn_error_t *
has_local_mods(svn_boolean_t *is_modified,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
svn_wc__db_t *db,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
/* Check for additions or deletions. */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SUBTREE_HAS_TREE_MODIFICATIONS));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
/* If this query returns a row, the working copy is modified. */
SVN_ERR(svn_sqlite__step(is_modified, stmt));
SVN_ERR(svn_sqlite__reset(stmt));
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
if (! *is_modified)
{
/* Check for property modifications. */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SUBTREE_HAS_PROP_MODIFICATIONS));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
/* If this query returns a row, the working copy is modified. */
SVN_ERR(svn_sqlite__step(is_modified, stmt));
SVN_ERR(svn_sqlite__reset(stmt));
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
}
if (! *is_modified)
{
apr_pool_t *iterpool = NULL;
svn_boolean_t have_row;
/* Check for text modifications. */
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_BASE_FILES_RECURSIVE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (have_row)
iterpool = svn_pool_create(scratch_pool);
while (have_row)
{
const char *node_abspath;
svn_filesize_t recorded_size;
apr_time_t recorded_time;
svn_boolean_t skip_check = FALSE;
svn_error_t *err;
if (cancel_func)
{
err = cancel_func(cancel_baton);
if (err)
return svn_error_trace(svn_error_compose_create(
err,
svn_sqlite__reset(stmt)));
}
svn_pool_clear(iterpool);
node_abspath = svn_dirent_join(wcroot->abspath,
svn_sqlite__column_text(stmt, 0,
iterpool),
iterpool);
recorded_size = get_recorded_size(stmt, 1);
recorded_time = svn_sqlite__column_int64(stmt, 2);
if (recorded_size != SVN_INVALID_FILESIZE
&& recorded_time != 0)
{
const svn_io_dirent2_t *dirent;
err = svn_io_stat_dirent2(&dirent, node_abspath, FALSE, TRUE,
iterpool, iterpool);
if (err)
return svn_error_trace(svn_error_compose_create(
err,
svn_sqlite__reset(stmt)));
if (dirent->kind != svn_node_file)
{
*is_modified = TRUE; /* Missing or obstruction */
break;
}
else if (dirent->filesize == recorded_size
&& dirent->mtime == recorded_time)
{
/* The file is not modified */
skip_check = TRUE;
}
}
if (! skip_check)
{
err = svn_wc__internal_file_modified_p(is_modified,
db, node_abspath,
FALSE, iterpool);
if (err)
return svn_error_trace(svn_error_compose_create(
err,
svn_sqlite__reset(stmt)));
if (*is_modified)
break;
}
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
if (iterpool)
svn_pool_destroy(iterpool);
SVN_ERR(svn_sqlite__reset(stmt));
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_has_local_mods(svn_boolean_t *is_modified,
svn_wc__db_t *db,
const char *local_abspath,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
db, local_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
return svn_error_trace(has_local_mods(is_modified, wcroot, local_relpath,
db, cancel_func, cancel_baton,
scratch_pool));
}
/* The body of svn_wc__db_revision_status().
*/
static svn_error_t *
revision_status_txn(svn_revnum_t *min_revision,
svn_revnum_t *max_revision,
svn_boolean_t *is_sparse_checkout,
svn_boolean_t *is_modified,
svn_boolean_t *is_switched,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
svn_wc__db_t *db,
const char *trail_url,
svn_boolean_t committed,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
svn_error_t *err;
svn_boolean_t exists;
SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
if (!exists)
{
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("The node '%s' was not found."),
path_for_error_message(wcroot, local_relpath,
scratch_pool));
}
/* Determine mixed-revisionness. */
SVN_ERR(get_min_max_revisions(min_revision, max_revision, wcroot,
local_relpath, committed, scratch_pool));
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
/* Determine sparseness. */
SVN_ERR(is_sparse_checkout_internal(is_sparse_checkout, wcroot,
local_relpath, scratch_pool));
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
/* Check for switched nodes. */
{
err = has_switched_subtrees(is_switched, wcroot, local_relpath,
trail_url, scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
svn_error_clear(err); /* No Base node, but no fatal error */
*is_switched = FALSE;
}
}
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
/* Check for local mods. */
SVN_ERR(has_local_mods(is_modified, wcroot, local_relpath, db,
cancel_func, cancel_baton, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_revision_status(svn_revnum_t *min_revision,
svn_revnum_t *max_revision,
svn_boolean_t *is_sparse_checkout,
svn_boolean_t *is_modified,
svn_boolean_t *is_switched,
svn_wc__db_t *db,
const char *local_abspath,
const char *trail_url,
svn_boolean_t committed,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
db, local_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_WC__DB_WITH_TXN(
revision_status_txn(min_revision, max_revision,
is_sparse_checkout, is_modified, is_switched,
wcroot, local_relpath, db,
trail_url, committed, cancel_func, cancel_baton,
scratch_pool),
wcroot);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_base_get_lock_tokens_recursive(apr_hash_t **lock_tokens,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
apr_int64_t last_repos_id = INVALID_REPOS_ID;
const char *last_repos_root_url = NULL;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
db, local_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
*lock_tokens = apr_hash_make(result_pool);
/* Fetch all the lock tokens in and under LOCAL_RELPATH. */
SVN_ERR(svn_sqlite__get_statement(
&stmt, wcroot->sdb,
STMT_SELECT_BASE_NODE_LOCK_TOKENS_RECURSIVE));
SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
while (have_row)
{
apr_int64_t child_repos_id = svn_sqlite__column_int64(stmt, 0);
const char *child_relpath = svn_sqlite__column_text(stmt, 1, NULL);
const char *lock_token = svn_sqlite__column_text(stmt, 2, result_pool);
if (child_repos_id != last_repos_id)
{
svn_error_t *err = svn_wc__db_fetch_repos_info(&last_repos_root_url,
NULL, wcroot->sdb,
child_repos_id,
scratch_pool);
if (err)
{
return svn_error_trace(
svn_error_compose_create(err,
svn_sqlite__reset(stmt)));
}
last_repos_id = child_repos_id;
}
SVN_ERR_ASSERT(last_repos_root_url != NULL);
svn_hash_sets(*lock_tokens,
svn_path_url_add_component2(last_repos_root_url,
child_relpath, result_pool),
lock_token);
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
return svn_sqlite__reset(stmt);
}
/* If EXPRESSION is false, cause the caller to return an SVN_ERR_WC_CORRUPT
* error, showing EXPRESSION and the caller's LOCAL_RELPATH in the message. */
#define VERIFY(expression) \
do { \
if (! (expression)) \
return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, \
_("database inconsistency at local_relpath='%s' verifying " \
"expression '%s'"), local_relpath, #expression); \
} while (0)
/* Verify consistency of the metadata concerning WCROOT. This is intended
* for use only during testing and debugging, so is not intended to be
* blazingly fast.
*
* This code is a complement to any verification that we can do in SQLite
* triggers. See, for example, 'wc-checks.sql'.
*
* Some more verification steps we might want to add are:
*
* * on every ACTUAL row (except root): a NODES row exists at its parent path
* * the op-depth root must always exist and every intermediate too
*/
static svn_error_t *
verify_wcroot(svn_wc__db_wcroot_t *wcroot,
apr_pool_t *scratch_pool)
{
svn_sqlite__stmt_t *stmt;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
STMT_SELECT_ALL_NODES));
SVN_ERR(svn_sqlite__bindf(stmt, "i", wcroot->wc_id));
while (TRUE)
{
svn_boolean_t have_row;
const char *local_relpath, *parent_relpath;
int op_depth;
svn_pool_clear(iterpool);
SVN_ERR(svn_sqlite__step(&have_row, stmt));
if (!have_row)
break;
op_depth = svn_sqlite__column_int(stmt, 0);
local_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
parent_relpath = svn_sqlite__column_text(stmt, 2, iterpool);
/* Verify parent_relpath is the parent path of local_relpath */
VERIFY((parent_relpath == NULL)
? (local_relpath[0] == '\0')
: (strcmp(svn_relpath_dirname(local_relpath, iterpool),
parent_relpath) == 0));
/* Verify op_depth <= the tree depth of local_relpath */
VERIFY(op_depth <= relpath_depth(local_relpath));
/* Verify parent_relpath refers to a row that exists */
/* TODO: Verify there is a suitable parent row - e.g. has op_depth <=
* the child's and a suitable presence */
if (parent_relpath && svn_sqlite__column_is_null(stmt, 3))
{
svn_sqlite__stmt_t *stmt2;
svn_boolean_t have_a_parent_row;
SVN_ERR(svn_sqlite__get_statement(&stmt2, wcroot->sdb,
STMT_SELECT_NODE_INFO));
SVN_ERR(svn_sqlite__bindf(stmt2, "is", wcroot->wc_id,
parent_relpath));
SVN_ERR(svn_sqlite__step(&have_a_parent_row, stmt2));
VERIFY(have_a_parent_row);
SVN_ERR(svn_sqlite__reset(stmt2));
}
}
svn_pool_destroy(iterpool);
return svn_error_trace(svn_sqlite__reset(stmt));
}
svn_error_t *
svn_wc__db_verify(svn_wc__db_t *db,
const char *wri_abspath,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
db, wri_abspath,
scratch_pool, scratch_pool));
VERIFY_USABLE_WCROOT(wcroot);
SVN_ERR(verify_wcroot(wcroot, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_bump_format(int *result_format,
svn_boolean_t *bumped_format,
svn_wc__db_t *db,
const char *wcroot_abspath,
apr_pool_t *scratch_pool)
{
svn_sqlite__db_t *sdb;
svn_error_t *err;
int format;
if (bumped_format)
*bumped_format = FALSE;
/* Do not scan upwards for a working copy root here to prevent accidental
* upgrades of any working copies the WCROOT might be nested in.
* Just try to open a DB at the specified path instead. */
err = svn_wc__db_util_open_db(&sdb, wcroot_abspath, SDB_FILE,
svn_sqlite__mode_readwrite,
TRUE, /* exclusive */
NULL, /* my statements */
scratch_pool, scratch_pool);
if (err)
{
svn_error_t *err2;
apr_hash_t *entries;
/* Could not open an sdb. Check for an entries file instead. */
err2 = svn_wc__read_entries_old(&entries, wcroot_abspath,
scratch_pool, scratch_pool);
if (err2 || apr_hash_count(entries) == 0)
return svn_error_createf(SVN_ERR_WC_INVALID_OP_ON_CWD,
svn_error_compose_create(err, err2),
_("Can't upgrade '%s' as it is not a working copy root"),
svn_dirent_local_style(wcroot_abspath, scratch_pool));
/* An entries file was found. This is a pre-wc-ng working copy
* so suggest an upgrade. */
return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, err,
_("Working copy '%s' is too old and must be upgraded to "
"at least format %d, as created by Subversion %s"),
svn_dirent_local_style(wcroot_abspath, scratch_pool),
SVN_WC__WC_NG_VERSION,
svn_wc__version_string_from_format(SVN_WC__WC_NG_VERSION));
}
SVN_ERR(svn_sqlite__read_schema_version(&format, sdb, scratch_pool));
err = svn_wc__upgrade_sdb(result_format, wcroot_abspath,
sdb, format, scratch_pool);
if (err == SVN_NO_ERROR && bumped_format)
*bumped_format = (*result_format > format);
/* Make sure we return a different error than expected for upgrades from
entries */
if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
err = svn_error_create(SVN_ERR_WC_UNSUPPORTED_FORMAT, err,
_("Working copy upgrade failed"));
err = svn_error_compose_create(err, svn_sqlite__close(sdb));
return svn_error_trace(err);
}
svn_error_t *
svn_wc__db_vacuum(svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *wcroot;
const char *local_relpath;
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
db, local_abspath,
scratch_pool, scratch_pool));
SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_VACUUM));
return SVN_NO_ERROR;
}
Index: vendor/subversion/dist/subversion/libsvn_wc/wc_db.h
===================================================================
--- vendor/subversion/dist/subversion/libsvn_wc/wc_db.h (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_wc/wc_db.h (revision 286501)
@@ -1,3459 +1,3460 @@
/**
* @copyright
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
* @endcopyright
*
* @file svn_wc_db.h
* @brief The Subversion Working Copy Library - Metadata/Base-Text Support
*
* Requires:
* - A working copy
*
* Provides:
* - Ability to manipulate working copy's administrative files.
*
* Used By:
* - The main working copy library
*/
#ifndef SVN_WC_DB_H
#define SVN_WC_DB_H
#include "svn_wc.h"
#include "svn_types.h"
#include "svn_error.h"
#include "svn_config.h"
#include "svn_io.h"
#include "private/svn_skel.h"
#include "private/svn_sqlite.h"
#include "private/svn_wc_private.h"
#include "svn_private_config.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* INTERFACE CONVENTIONS
"OUT" PARAMETERS
There are numerous functions within this API which take a (large) number
of "out" parameters. These are listed individually, rather than combined
into a struct, so that a caller can be fine-grained about the which
pieces of information are being requested. In many cases, only a subset
is required, so the implementation can perform various optimizations
to fulfill the limited request for information.
POOLS
wc_db uses the dual-pool paradigm for all of its functions. Any OUT
parameter will be allocated within the result pool, and all temporary
allocations will be performed within the scratch pool.
The pool that DB is allocated within (the "state" pool) is only used
for a few, limited allocations to track each of the working copy roots
that the DB is asked to operate upon. The memory usage on this pool
is O(# wcroots), which should normally be one or a few. Custom clients
which hold open structures over a significant period of time should
pay particular attention to the number of roots touched, and the
resulting impact on memory consumption (which should still be minimal).
PARAMETER CONVENTIONS
* Parameter Order
- any output arguments
- DB
- LOCAL_ABSPATH
- any other input arguments
- RESULT_POOL
- SCRATCH_POOL
* DB
This parameter is the primary context for all operations on the
metadata for working copies. This parameter is passed to almost every
function, and maintains information and state about every working
copy "touched" by any of the APIs in this interface.
* *_ABSPATH
All *_ABSPATH parameters in this API are absolute paths in the local
filesystem, represented in Subversion internal canonical form.
* LOCAL_ABSPATH
This parameter specifies a particular *versioned* node in the local
filesystem. From this node, a working copy root is implied, and will
be used for the given API operation.
* LOCAL_DIR_ABSPATH
This parameter is similar to LOCAL_ABSPATH, but the semantics of the
parameter and operation require the node to be a directory within
the working copy.
* WRI_ABSPATH
This is a "Working copy Root Indicator" path. This refers to a location
in the local filesystem that is anywhere inside a working copy. The given
operation will be performed within the context of the root of that
working copy. This does not necessarily need to refer to a specific
versioned node or the root of a working copy (although it can) -- any
location, existing or not, is sufficient, as long as it is inside a
working copy.
### TODO: Define behaviour for switches and externals.
### Preference has been stated that WRI_ABSPATH should imply the root
### of the parent WC of all switches and externals, but that may
### not play out well, especially with multiple repositories involved.
*/
/* Context data structure for interacting with the administrative data. */
typedef struct svn_wc__db_t svn_wc__db_t;
/* Enumerated values describing the state of a node. */
typedef enum svn_wc__db_status_t {
/* The node is present and has no known modifications applied to it. */
svn_wc__db_status_normal,
/* The node has been added (potentially obscuring a delete or move of
the BASE node; see HAVE_BASE param [### What param? This is an enum
not a function.] ). The text will be marked as
modified, and if properties exist, they will be marked as modified.
In many cases svn_wc__db_status_added means any of added, moved-here
or copied-here. See individual functions for clarification and
svn_wc__db_scan_addition() to get more details. */
svn_wc__db_status_added,
/* This node has been added with history, based on the move source.
Text and property modifications are based on whether changes have
been made against their pristine versions. */
svn_wc__db_status_moved_here,
/* This node has been added with history, based on the copy source.
Text and property modifications are based on whether changes have
been made against their pristine versions. */
svn_wc__db_status_copied,
/* This node has been deleted. No text or property modifications
will be present. */
svn_wc__db_status_deleted,
/* This node was named by the server, but no information was provided. */
svn_wc__db_status_server_excluded,
/* This node has been administratively excluded. */
svn_wc__db_status_excluded,
/* This node is not present in this revision. This typically happens
when a node is deleted and committed without updating its parent.
The parent revision indicates it should be present, but this node's
revision states otherwise. */
svn_wc__db_status_not_present,
/* This node is known, but its information is incomplete. Generally,
it should be treated similar to the other missing status values
until some (later) process updates the node with its data.
When the incomplete status applies to a directory, the list of
children and the list of its base properties as recorded in the
working copy do not match their working copy versions.
The update editor can complete a directory by using a different
update algorithm. */
svn_wc__db_status_incomplete,
/* The BASE node has been marked as deleted. Only used as an internal
status in wc_db.c and entries.c. */
svn_wc__db_status_base_deleted
} svn_wc__db_status_t;
/* Lock information. We write/read it all as one, so let's use a struct
for convenience. */
typedef struct svn_wc__db_lock_t {
/* The lock token */
const char *token;
/* The owner of the lock, possibly NULL */
const char *owner;
/* A comment about the lock, possibly NULL */
const char *comment;
/* The date the lock was created */
apr_time_t date;
} svn_wc__db_lock_t;
/* ### NOTE: I have not provided docstrings for most of this file at this
### point in time. The shape and extent of this API is still in massive
### flux. I'm iterating in public, but do not want to doc until it feels
### like it is "Right".
*/
/* ### where/how to handle: text_time, locks, working_size */
/*
@defgroup svn_wc__db_admin General administrative functions
@{
*/
/* Open a working copy administrative database context.
This context is (initially) not associated with any particular working
copy directory or working copy root (wcroot). As operations are performed,
this context will load the appropriate wcroot information.
The context is returned in DB.
CONFIG should hold the various configuration options that may apply to
the administrative operation. It should live at least as long as the
RESULT_POOL parameter.
When OPEN_WITHOUT_UPGRADE is TRUE, then the working copy databases will
be opened even when an old database format is found/detected during
the operation of a wc_db API). If open_without_upgrade is FALSE and an
upgrade is required, then SVN_ERR_WC_UPGRADE_REQUIRED will be returned
from that API.
Passing TRUE will allow a bare minimum of APIs to function (most notably,
the temp_get_format() function will always return a value) since most of
these APIs expect a current-format database to be present.
If ENFORCE_EMPTY_WQ is TRUE, then any databases with stale work items in
their work queue will raise an error when they are opened. The operation
will raise SVN_ERR_WC_CLEANUP_REQUIRED. Passing FALSE for this routine
means that the work queue is being processed (via 'svn cleanup') and all
operations should be allowed.
The DB will be closed when RESULT_POOL is cleared. It may also be closed
manually using svn_wc__db_close(). In particular, this will close any
SQLite databases that have been opened and cached.
The context is allocated in RESULT_POOL. This pool is *retained* and used
for future allocations within the DB. Be forewarned about unbounded
memory growth if this DB is used across an unbounded number of wcroots
and versioned directories.
Temporary allocations will be made in SCRATCH_POOL.
*/
svn_error_t *
svn_wc__db_open(svn_wc__db_t **db,
svn_config_t *config,
svn_boolean_t open_without_upgrade,
svn_boolean_t enforce_empty_wq,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Close DB. */
svn_error_t *
svn_wc__db_close(svn_wc__db_t *db);
/* Initialize the SDB for LOCAL_ABSPATH, which should be a working copy path.
A REPOSITORY row will be constructed for the repository identified by
REPOS_ROOT_URL and REPOS_UUID. Neither of these may be NULL.
A BASE_NODE row will be created for the directory at REPOS_RELPATH at
revision INITIAL_REV.
If INITIAL_REV is greater than zero, then the node will be marked as
"incomplete" because we don't know its children. Contrary, if the
INITIAL_REV is zero, then this directory should represent the root and
we know it has no children, so the node is complete.
### Is there any benefit to marking it 'complete' if rev==0? Seems like
### an unnecessary special case.
DEPTH is the initial depth of the working copy; it must be a definite
depth, not svn_depth_unknown.
Use SCRATCH_POOL for temporary allocations.
*/
svn_error_t *
svn_wc__db_init(svn_wc__db_t *db,
const char *local_abspath,
const char *repos_relpath,
const char *repos_root_url,
const char *repos_uuid,
svn_revnum_t initial_rev,
svn_depth_t depth,
apr_pool_t *scratch_pool);
/* Compute the LOCAL_RELPATH for the given LOCAL_ABSPATH, relative
from wri_abspath.
The LOCAL_RELPATH is a relative path to the working copy's root. That
root will be located by this function, and the path will be relative to
that location. If LOCAL_ABSPATH is the wcroot directory, then "" will
be returned.
The LOCAL_RELPATH should ONLY be used for persisting paths to disk.
Those paths should not be abspaths, otherwise the working copy cannot
be moved. The working copy library should not make these paths visible
in its API (which should all be abspaths), and it should not be using
relpaths for other processing.
LOCAL_RELPATH will be allocated in RESULT_POOL. All other (temporary)
allocations will be made in SCRATCH_POOL.
This function is available when DB is opened with the OPEN_WITHOUT_UPGRADE
option.
*/
svn_error_t *
svn_wc__db_to_relpath(const char **local_relpath,
svn_wc__db_t *db,
const char *wri_abspath,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Compute the LOCAL_ABSPATH for a LOCAL_RELPATH located within the working
copy identified by WRI_ABSPATH.
This is the reverse of svn_wc__db_to_relpath. It should be used for
returning a persisted relpath back into an abspath.
LOCAL_ABSPATH will be allocated in RESULT_POOL. All other (temporary)
allocations will be made in SCRATCH_POOL.
This function is available when DB is opened with the OPEN_WITHOUT_UPGRADE
option.
*/
svn_error_t *
svn_wc__db_from_relpath(const char **local_abspath,
svn_wc__db_t *db,
const char *wri_abspath,
const char *local_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Compute the working copy root WCROOT_ABSPATH for WRI_ABSPATH using DB.
This function is available when DB is opened with the OPEN_WITHOUT_UPGRADE
option.
*/
svn_error_t *
svn_wc__db_get_wcroot(const char **wcroot_abspath,
svn_wc__db_t *db,
const char *wri_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* @} */
/* Different kinds of trees
The design doc mentions three different kinds of trees, BASE, WORKING and
ACTUAL: http://svn.apache.org/repos/asf/subversion/trunk/notes/wc-ng-design
We have different APIs to handle each tree, enumerated below, along with
a blurb to explain what that tree represents.
*/
/* @defgroup svn_wc__db_base BASE tree management
BASE is what we get from the server. It is the *absolute* pristine copy.
You need to use checkout, update, switch, or commit to alter your view of
the repository.
In the BASE tree, each node corresponds to a particular node-rev in the
repository. It can be a mixed-revision tree. Each node holds either a
copy of the node-rev as it exists in the repository (if presence =
'normal'), or a place-holder (if presence = 'server-excluded' or 'excluded' or
'not-present').
@{
*/
/* Add or replace a directory in the BASE tree.
The directory is located at LOCAL_ABSPATH on the local filesystem, and
corresponds to <REPOS_RELPATH, REPOS_ROOT_URL, REPOS_UUID> in the
repository, at revision REVISION.
The directory properties are given by the PROPS hash (which is
const char *name => const svn_string_t *).
The last-change information is given by <CHANGED_REV, CHANGED_DATE,
CHANGED_AUTHOR>.
The directory's children are listed in CHILDREN, as an array of
const char *. The child nodes do NOT have to exist when this API
is called. For each child node which does not exists, an "incomplete"
node will be added. These child nodes will be added regardless of
the DEPTH value. The caller must sort out which must be recorded,
and which must be omitted.
This subsystem does not use DEPTH, but it can be recorded here in
the BASE tree for higher-level code to use.
If DAV_CACHE is not NULL, sets LOCAL_ABSPATH's dav cache to the specified
data.
If CONFLICT is not NULL, then it describes a conflict for this node. The
node will be record as conflicted (in ACTUAL).
If UPDATE_ACTUAL_PROPS is TRUE, set the properties store NEW_ACTUAL_PROPS
as the new set of properties in ACTUAL. If NEW_ACTUAL_PROPS is NULL or
when the value of NEW_ACTUAL_PROPS matches NEW_PROPS, store NULL in
ACTUAL, to mark the properties unmodified.
If NEW_IPROPS is not NULL, then it is a depth-first ordered array of
svn_prop_inherited_item_t * structures that is set as the base node's
inherited_properties.
Any work items that are necessary as part of this node construction may
be passed in WORK_ITEMS.
All temporary allocations will be made in SCRATCH_POOL.
*/
svn_error_t *
svn_wc__db_base_add_directory(svn_wc__db_t *db,
const char *local_abspath,
const char *wri_abspath,
const char *repos_relpath,
const char *repos_root_url,
const char *repos_uuid,
svn_revnum_t revision,
const apr_hash_t *props,
svn_revnum_t changed_rev,
apr_time_t changed_date,
const char *changed_author,
const apr_array_header_t *children,
svn_depth_t depth,
apr_hash_t *dav_cache,
const svn_skel_t *conflict,
svn_boolean_t update_actual_props,
apr_hash_t *new_actual_props,
apr_array_header_t *new_iprops,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool);
/* Add a new directory in BASE, whether WORKING nodes exist or not. Mark it
as incomplete and with revision REVISION. If REPOS_RELPATH is not NULL,
apply REPOS_RELPATH, REPOS_ROOT_URL and REPOS_UUID.
Perform all temporary allocations in SCRATCH_POOL.
*/
svn_error_t *
svn_wc__db_base_add_incomplete_directory(svn_wc__db_t *db,
const char *local_abspath,
const char *repos_relpath,
const char *repos_root_url,
const char *repos_uuid,
svn_revnum_t revision,
svn_depth_t depth,
svn_boolean_t insert_base_deleted,
svn_boolean_t delete_working,
svn_skel_t *conflict,
svn_skel_t *work_items,
apr_pool_t *scratch_pool);
/* Add or replace a file in the BASE tree.
The file is located at LOCAL_ABSPATH on the local filesystem, and
corresponds to <REPOS_RELPATH, REPOS_ROOT_URL, REPOS_UUID> in the
repository, at revision REVISION.
The file properties are given by the PROPS hash (which is
const char *name => const svn_string_t *).
The last-change information is given by <CHANGED_REV, CHANGED_DATE,
CHANGED_AUTHOR>.
The checksum of the file contents is given in CHECKSUM. An entry in
the pristine text base is NOT required when this API is called.
If DAV_CACHE is not NULL, sets LOCAL_ABSPATH's dav cache to the specified
data.
If CONFLICT is not NULL, then it describes a conflict for this node. The
node will be record as conflicted (in ACTUAL).
If UPDATE_ACTUAL_PROPS is TRUE, set the properties store NEW_ACTUAL_PROPS
as the new set of properties in ACTUAL. If NEW_ACTUAL_PROPS is NULL or
when the value of NEW_ACTUAL_PROPS matches NEW_PROPS, store NULL in
ACTUAL, to mark the properties unmodified.
Any work items that are necessary as part of this node construction may
be passed in WORK_ITEMS.
Unless KEEP_RECORDED_INFO is set to TRUE, recorded size and timestamp values
will be cleared.
All temporary allocations will be made in SCRATCH_POOL.
*/
svn_error_t *
svn_wc__db_base_add_file(svn_wc__db_t *db,
const char *local_abspath,
const char *wri_abspath,
const char *repos_relpath,
const char *repos_root_url,
const char *repos_uuid,
svn_revnum_t revision,
const apr_hash_t *props,
svn_revnum_t changed_rev,
apr_time_t changed_date,
const char *changed_author,
const svn_checksum_t *checksum,
apr_hash_t *dav_cache,
svn_boolean_t delete_working,
svn_boolean_t update_actual_props,
apr_hash_t *new_actual_props,
apr_array_header_t *new_iprops,
svn_boolean_t keep_recorded_info,
svn_boolean_t insert_base_deleted,
const svn_skel_t *conflict,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool);
/* Add or replace a symlink in the BASE tree.
The symlink is located at LOCAL_ABSPATH on the local filesystem, and
corresponds to <REPOS_RELPATH, REPOS_ROOT_URL, REPOS_UUID> in the
repository, at revision REVISION.
The symlink's properties are given by the PROPS hash (which is
const char *name => const svn_string_t *).
The last-change information is given by <CHANGED_REV, CHANGED_DATE,
CHANGED_AUTHOR>.
The target of the symlink is specified by TARGET.
If DAV_CACHE is not NULL, sets LOCAL_ABSPATH's dav cache to the specified
data.
If CONFLICT is not NULL, then it describes a conflict for this node. The
node will be record as conflicted (in ACTUAL).
If UPDATE_ACTUAL_PROPS is TRUE, set the properties store NEW_ACTUAL_PROPS
as the new set of properties in ACTUAL. If NEW_ACTUAL_PROPS is NULL or
when the value of NEW_ACTUAL_PROPS matches NEW_PROPS, store NULL in
ACTUAL, to mark the properties unmodified.
Any work items that are necessary as part of this node construction may
be passed in WORK_ITEMS.
All temporary allocations will be made in SCRATCH_POOL.
*/
/* ### KFF: This is an interesting question, because currently
### symlinks are versioned as regular files with the svn:special
### property; then the file's text contents indicate that it is a
### symlink and where that symlink points. That's for portability:
### you can check 'em out onto a platform that doesn't support
### symlinks, and even modify the link and check it back in. It's
### a great solution; but then the question for wc-ng is:
###
### Suppose you check out a symlink on platform X and platform Y.
### X supports symlinks; Y does not. Should the wc-ng storage for
### those two be the same? I mean, on platform Y, the file is just
### going to look and behave like a regular file. It would be sort
### of odd for the wc-ng storage for that file to be of a different
### type from all the other files. (On the other hand, maybe it's
### weird today that the wc-1 storage for a working symlink is to
### be like a regular file (i.e., regular text-base and whatnot).
###
### I'm still feeling my way around this problem; just pointing out
### the issues.
### gjs: symlinks are stored in the database as first-class objects,
### rather than in the filesystem as "special" regular files. thus,
### all portability concerns are moot. higher-levels can figure out
### how to represent the link in ACTUAL. higher-levels can also
### deal with translating to/from the svn:special property and
### the plain-text file contents.
### dlr: What about hard links? At minimum, mention in doc string.
*/
svn_error_t *
svn_wc__db_base_add_symlink(svn_wc__db_t *db,
const char *local_abspath,
const char *wri_abspath,
const char *repos_relpath,
const char *repos_root_url,
const char *repos_uuid,
svn_revnum_t revision,
const apr_hash_t *props,
svn_revnum_t changed_rev,
apr_time_t changed_date,
const char *changed_author,
const char *target,
apr_hash_t *dav_cache,
svn_boolean_t delete_working,
svn_boolean_t update_actual_props,
apr_hash_t *new_actual_props,
apr_array_header_t *new_iprops,
svn_boolean_t keep_recorded_info,
svn_boolean_t insert_base_deleted,
const svn_skel_t *conflict,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool);
/* Create a node in the BASE tree that is present in name only.
The new node will be located at LOCAL_ABSPATH, and correspond to the
repository node described by <REPOS_RELPATH, REPOS_ROOT_URL, REPOS_UUID>
at revision REVISION.
The node's kind is described by KIND, and the reason for its absence
is specified by STATUS. Only these values are allowed for STATUS:
svn_wc__db_status_server_excluded
svn_wc__db_status_excluded
If CONFLICT is not NULL, then it describes a conflict for this node. The
node will be record as conflicted (in ACTUAL).
Any work items that are necessary as part of this node construction may
be passed in WORK_ITEMS.
All temporary allocations will be made in SCRATCH_POOL.
*/
svn_error_t *
svn_wc__db_base_add_excluded_node(svn_wc__db_t *db,
const char *local_abspath,
const char *repos_relpath,
const char *repos_root_url,
const char *repos_uuid,
svn_revnum_t revision,
svn_node_kind_t kind,
svn_wc__db_status_t status,
const svn_skel_t *conflict,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool);
/* Create a node in the BASE tree that is present in name only.
The new node will be located at LOCAL_ABSPATH, and correspond to the
repository node described by <REPOS_RELPATH, REPOS_ROOT_URL, REPOS_UUID>
at revision REVISION.
The node's kind is described by KIND, and the reason for its absence
is 'svn_wc__db_status_not_present'.
If CONFLICT is not NULL, then it describes a conflict for this node. The
node will be record as conflicted (in ACTUAL).
Any work items that are necessary as part of this node construction may
be passed in WORK_ITEMS.
All temporary allocations will be made in SCRATCH_POOL.
*/
svn_error_t *
svn_wc__db_base_add_not_present_node(svn_wc__db_t *db,
const char *local_abspath,
const char *repos_relpath,
const char *repos_root_url,
const char *repos_uuid,
svn_revnum_t revision,
svn_node_kind_t kind,
const svn_skel_t *conflict,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool);
/* Remove a node and all its descendants from the BASE tree. This handles
the deletion of a tree from the update editor and some file external
scenarios.
The node to remove is indicated by LOCAL_ABSPATH from the local
filesystem.
This operation *installs* workqueue operations to update the local
filesystem after the database operation.
To maintain a consistent database this function will also remove
any working node that marks LOCAL_ABSPATH as base-deleted. If this
results in there being no working node for LOCAL_ABSPATH then any
actual node will be removed if the actual node does not mark a
conflict.
If KEEP_AS_WORKING is TRUE, then the base tree is copied to higher
layers as a copy of itself before deleting the BASE nodes.
If KEEP_AS_WORKING is FALSE, and QUEUE_DELETES is TRUE, also queue
workqueue items to delete all in-wc representations that aren't
shadowed by higher layers.
(With KEEP_AS_WORKING TRUE, this is a no-op, as everything is
automatically shadowed by the created copy)
If REMOVE_LOCKS is TRUE, all locks of this node and any subnodes
are also removed. This is to be done during commit of deleted nodes.
If NOT_PRESENT_REVISION specifies a valid revision a not-present
node is installed in BASE node with kind NOT_PRESENT_KIND after
deleting.
If CONFLICT and/or WORK_ITEMS are passed they are installed as part
of the operation, after the work items inserted by the operation
itself.
*/
svn_error_t *
svn_wc__db_base_remove(svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t keep_as_working,
svn_boolean_t queue_deletes,
svn_boolean_t remove_locks,
svn_revnum_t not_present_revision,
svn_skel_t *conflict,
svn_skel_t *work_items,
apr_pool_t *scratch_pool);
/* Retrieve information about a node in the BASE tree.
For the BASE node implied by LOCAL_ABSPATH from the local filesystem,
return information in the provided OUT parameters. Each OUT parameter
may be NULL, indicating that specific item is not requested.
If there is no information about this node, then SVN_ERR_WC_PATH_NOT_FOUND
will be returned.
The OUT parameters, and their "not available" values are:
STATUS n/a (always available)
KIND n/a (always available)
REVISION SVN_INVALID_REVNUM
REPOS_RELPATH NULL (caller should scan up)
REPOS_ROOT_URL NULL (caller should scan up)
REPOS_UUID NULL (caller should scan up)
CHANGED_REV SVN_INVALID_REVNUM
CHANGED_DATE 0
CHANGED_AUTHOR NULL
DEPTH svn_depth_unknown
CHECKSUM NULL
TARGET NULL
LOCK NULL
HAD_PROPS FALSE
PROPS NULL
UPDATE_ROOT FALSE
If the STATUS is normal, the REPOS_* values will be non-NULL.
If DEPTH is requested, and the node is NOT a directory, then the
value will be set to svn_depth_unknown. If LOCAL_ABSPATH is a link,
it's up to the caller to resolve depth for the link's target.
If CHECKSUM is requested, and the node is NOT a file, then it will
be set to NULL.
If TARGET is requested, and the node is NOT a symlink, then it will
be set to NULL.
*PROPS maps "const char *" names to "const svn_string_t *" values. If
the base node is capable of having properties but has none, set
*PROPS to an empty hash. If its status is such that it cannot have
properties, set *PROPS to NULL.
If UPDATE_ROOT is requested, set it to TRUE if the node should only
be updated when it is the root of an update (e.g. file externals).
All returned data will be allocated in RESULT_POOL. All temporary
allocations will be made in SCRATCH_POOL.
*/
svn_error_t *
svn_wc__db_base_get_info(svn_wc__db_status_t *status,
svn_node_kind_t *kind,
svn_revnum_t *revision,
const char **repos_relpath,
const char **repos_root_url,
const char **repos_uuid,
svn_revnum_t *changed_rev,
apr_time_t *changed_date,
const char **changed_author,
svn_depth_t *depth,
const svn_checksum_t **checksum,
const char **target,
svn_wc__db_lock_t **lock,
svn_boolean_t *had_props,
apr_hash_t **props,
svn_boolean_t *update_root,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Structure returned by svn_wc__db_base_get_children_info. Only has the
fields needed by the adm crawler. */
struct svn_wc__db_base_info_t {
svn_wc__db_status_t status;
svn_node_kind_t kind;
svn_revnum_t revnum;
const char *repos_relpath;
const char *repos_root_url;
svn_depth_t depth;
svn_boolean_t update_root;
svn_wc__db_lock_t *lock;
};
/* Return in *NODES a hash mapping name->struct svn_wc__db_base_info_t for
the children of DIR_ABSPATH at op_depth 0.
*/
svn_error_t *
svn_wc__db_base_get_children_info(apr_hash_t **nodes,
svn_wc__db_t *db,
const char *dir_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Set *PROPS to the properties of the node LOCAL_ABSPATH in the BASE tree.
*PROPS maps "const char *" names to "const svn_string_t *" values.
If the node has no properties, set *PROPS to an empty hash.
*PROPS will never be set to NULL.
If the node is not present in the BASE tree (with presence 'normal'
or 'incomplete'), return an error.
Allocate *PROPS and its keys and values in RESULT_POOL.
*/
svn_error_t *
svn_wc__db_base_get_props(apr_hash_t **props,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Return a list of the BASE tree node's children's names.
For the node indicated by LOCAL_ABSPATH, this function will return
the names of all of its children in the array CHILDREN. The array
elements are const char * values.
If the node is not a directory, then SVN_ERR_WC_NOT_WORKING_COPY will
be returned.
All returned data will be allocated in RESULT_POOL. All temporary
allocations will be made in SCRATCH_POOL.
*/
svn_error_t *
svn_wc__db_base_get_children(const apr_array_header_t **children,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Set the dav cache for LOCAL_ABSPATH to PROPS. Use SCRATCH_POOL for
temporary allocations. */
svn_error_t *
svn_wc__db_base_set_dav_cache(svn_wc__db_t *db,
const char *local_abspath,
const apr_hash_t *props,
apr_pool_t *scratch_pool);
/* Retrieve the dav cache for LOCAL_ABSPATH into *PROPS, allocated in
RESULT_POOL. Use SCRATCH_POOL for temporary allocations. Return
SVN_ERR_WC_PATH_NOT_FOUND if no dav cache can be located for
LOCAL_ABSPATH in DB. */
svn_error_t *
svn_wc__db_base_get_dav_cache(apr_hash_t **props,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Recursively clear the dav cache for LOCAL_ABSPATH. Use
SCRATCH_POOL for temporary allocations. */
svn_error_t *
svn_wc__db_base_clear_dav_cache_recursive(svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool);
/* Set LOCK_TOKENS to a hash mapping const char * full URLs to const char *
* lock tokens for every base node at or under LOCAL_ABSPATH in DB which has
* such a lock token set on it.
* Allocate the hash and all items therein from RESULT_POOL. */
svn_error_t *
svn_wc__db_base_get_lock_tokens_recursive(apr_hash_t **lock_tokens,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* ### anything else needed for maintaining the BASE tree? */
/* @} */
/* @defgroup svn_wc__db_pristine Pristine ("text base") management
@{
*/
/* Set *PRISTINE_ABSPATH to the path to the pristine text file
identified by SHA1_CHECKSUM. Error if it does not exist.
### This is temporary - callers should not be looking at the file
directly.
Allocate the path in RESULT_POOL. */
svn_error_t *
svn_wc__db_pristine_get_path(const char **pristine_abspath,
svn_wc__db_t *db,
const char *wri_abspath,
const svn_checksum_t *checksum,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Set *PRISTINE_ABSPATH to the path under WCROOT_ABSPATH that will be
used by the pristine text identified by SHA1_CHECKSUM. The file
need not exist.
*/
svn_error_t *
svn_wc__db_pristine_get_future_path(const char **pristine_abspath,
const char *wcroot_abspath,
const svn_checksum_t *sha1_checksum,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* If requested set *CONTENTS to a readable stream that will yield the pristine
text identified by SHA1_CHECKSUM (must be a SHA-1 checksum) within the WC
identified by WRI_ABSPATH in DB.
If requested set *SIZE to the size of the pristine stream in bytes,
Even if the pristine text is removed from the store while it is being
read, the stream will remain valid and readable until it is closed.
Allocate the stream in RESULT_POOL. */
svn_error_t *
svn_wc__db_pristine_read(svn_stream_t **contents,
svn_filesize_t *size,
svn_wc__db_t *db,
const char *wri_abspath,
const svn_checksum_t *sha1_checksum,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Set *TEMP_DIR_ABSPATH to a directory in which the caller should create
a uniquely named file for later installation as a pristine text file.
The directory is guaranteed to be one that svn_wc__db_pristine_install()
can use: specifically, one from which it can atomically move the file.
Allocate *TEMP_DIR_ABSPATH in RESULT_POOL. */
svn_error_t *
svn_wc__db_pristine_get_tempdir(const char **temp_dir_abspath,
svn_wc__db_t *db,
const char *wri_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Install the file TEMPFILE_ABSPATH (which is sitting in a directory given by
svn_wc__db_pristine_get_tempdir()) into the pristine data store, to be
identified by the SHA-1 checksum of its contents, SHA1_CHECKSUM, and whose
MD-5 checksum is MD5_CHECKSUM. */
svn_error_t *
svn_wc__db_pristine_install(svn_wc__db_t *db,
const char *tempfile_abspath,
const svn_checksum_t *sha1_checksum,
const svn_checksum_t *md5_checksum,
apr_pool_t *scratch_pool);
/* Set *MD5_CHECKSUM to the MD-5 checksum of a pristine text
identified by its SHA-1 checksum SHA1_CHECKSUM. Return an error
if the pristine text does not exist or its MD5 checksum is not found.
Allocate *MD5_CHECKSUM in RESULT_POOL. */
svn_error_t *
svn_wc__db_pristine_get_md5(const svn_checksum_t **md5_checksum,
svn_wc__db_t *db,
const char *wri_abspath,
const svn_checksum_t *sha1_checksum,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Set *SHA1_CHECKSUM to the SHA-1 checksum of a pristine text
identified by its MD-5 checksum MD5_CHECKSUM. Return an error
if the pristine text does not exist or its SHA-1 checksum is not found.
Note: The MD-5 checksum is not strictly guaranteed to be unique in the
database table, although duplicates are expected to be extremely rare.
### TODO: The behaviour is currently unspecified if the MD-5 checksum is
not unique. Need to see whether this function is going to stay in use,
and, if so, address this somehow.
Allocate *SHA1_CHECKSUM in RESULT_POOL. */
svn_error_t *
svn_wc__db_pristine_get_sha1(const svn_checksum_t **sha1_checksum,
svn_wc__db_t *db,
const char *wri_abspath,
const svn_checksum_t *md5_checksum,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* If necessary transfers the PRISTINE files of the tree rooted at
SRC_LOCAL_ABSPATH to the working copy identified by DST_WRI_ABSPATH. */
svn_error_t *
svn_wc__db_pristine_transfer(svn_wc__db_t *db,
const char *src_local_abspath,
const char *dst_wri_abspath,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool);
/* Remove the pristine text with SHA-1 checksum SHA1_CHECKSUM from the
* pristine store, iff it is not referenced by any of the (other) WC DB
* tables. */
svn_error_t *
svn_wc__db_pristine_remove(svn_wc__db_t *db,
const char *wri_abspath,
const svn_checksum_t *sha1_checksum,
apr_pool_t *scratch_pool);
/* Remove all unreferenced pristines in the WC of WRI_ABSPATH in DB. */
svn_error_t *
svn_wc__db_pristine_cleanup(svn_wc__db_t *db,
const char *wri_abspath,
apr_pool_t *scratch_pool);
/* Set *PRESENT to true if the pristine store for WRI_ABSPATH in DB contains
a pristine text with SHA-1 checksum SHA1_CHECKSUM, and to false otherwise.
*/
svn_error_t *
svn_wc__db_pristine_check(svn_boolean_t *present,
svn_wc__db_t *db,
const char *wri_abspath,
const svn_checksum_t *sha1_checksum,
apr_pool_t *scratch_pool);
/* @defgroup svn_wc__db_external External management
@{ */
/* Adds (or overwrites) a file external LOCAL_ABSPATH to the working copy
identified by WRI_ABSPATH.
It updates both EXTERNALS and NODES in one atomic step.
*/
svn_error_t *
svn_wc__db_external_add_file(svn_wc__db_t *db,
const char *local_abspath,
const char *wri_abspath,
const char *repos_relpath,
const char *repos_root_url,
const char *repos_uuid,
svn_revnum_t revision,
const apr_hash_t *props,
apr_array_header_t *iprops,
svn_revnum_t changed_rev,
apr_time_t changed_date,
const char *changed_author,
const svn_checksum_t *checksum,
const apr_hash_t *dav_cache,
const char *record_ancestor_abspath,
const char *recorded_repos_relpath,
svn_revnum_t recorded_peg_revision,
svn_revnum_t recorded_revision,
svn_boolean_t update_actual_props,
apr_hash_t *new_actual_props,
svn_boolean_t keep_recorded_info,
const svn_skel_t *conflict,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool);
/* Adds (or overwrites) a symlink external LOCAL_ABSPATH to the working copy
identified by WRI_ABSPATH.
*/
svn_error_t *
svn_wc__db_external_add_symlink(svn_wc__db_t *db,
const char *local_abspath,
const char *wri_abspath,
const char *repos_relpath,
const char *repos_root_url,
const char *repos_uuid,
svn_revnum_t revision,
const apr_hash_t *props,
svn_revnum_t changed_rev,
apr_time_t changed_date,
const char *changed_author,
const char *target,
const apr_hash_t *dav_cache,
const char *record_ancestor_abspath,
const char *recorded_repos_relpath,
svn_revnum_t recorded_peg_revision,
svn_revnum_t recorded_revision,
svn_boolean_t update_actual_props,
apr_hash_t *new_actual_props,
svn_boolean_t keep_recorded_info,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool);
/* Adds (or overwrites) a directory external LOCAL_ABSPATH to the working copy
identified by WRI_ABSPATH.
Directory externals are stored in their own working copy, so one should use
the normal svn_wc__db functions to access the normal working copy
information.
*/
svn_error_t *
svn_wc__db_external_add_dir(svn_wc__db_t *db,
const char *local_abspath,
const char *wri_abspath,
const char *repos_root_url,
const char *repos_uuid,
const char *record_ancestor_abspath,
const char *recorded_repos_relpath,
svn_revnum_t recorded_peg_revision,
svn_revnum_t recorded_revision,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool);
/* Remove a registered external LOCAL_ABSPATH from the working copy identified
by WRI_ABSPATH.
*/
svn_error_t *
svn_wc__db_external_remove(svn_wc__db_t *db,
const char *local_abspath,
const char *wri_abspath,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool);
/* Reads information on the external LOCAL_ABSPATH as stored in the working
copy identified with WRI_ABSPATH (If NULL the parent directory of
LOCAL_ABSPATH is taken as WRI_ABSPATH).
Return SVN_ERR_WC_PATH_NOT_FOUND if LOCAL_ABSPATH is not an external in
this working copy.
When STATUS is requested it has one of these values
svn_wc__db_status_normal The external is available
svn_wc__db_status_excluded The external is user excluded
When KIND is requested then the value will be set to the kind of external.
If DEFINING_ABSPATH is requested, then the value will be set to the
absolute path of the directory which originally defined the external.
(The path with the svn:externals property)
If REPOS_ROOT_URL is requested, then the value will be set to the
repository root of the external.
If REPOS_UUID is requested, then the value will be set to the
repository uuid of the external.
If RECORDED_REPOS_RELPATH is requested, then the value will be set to the
original repository relative path inside REPOS_ROOT_URL of the external.
If RECORDED_PEG_REVISION is requested, then the value will be set to the
original recorded operational (peg) revision of the external.
If RECORDED_REVISION is requested, then the value will be set to the
original recorded revision of the external.
Allocate the result in RESULT_POOL and perform temporary allocations in
SCRATCH_POOL.
*/
svn_error_t *
svn_wc__db_external_read(svn_wc__db_status_t *status,
svn_node_kind_t *kind,
const char **defining_abspath,
const char **repos_root_url,
const char **repos_uuid,
const char **recorded_repos_relpath,
svn_revnum_t *recorded_peg_revision,
svn_revnum_t *recorded_revision,
svn_wc__db_t *db,
const char *local_abspath,
const char *wri_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Return in *EXTERNALS a list of svn_wc__committable_external_info_t *
* containing info on externals defined to be checked out below LOCAL_ABSPATH,
* returning only those externals that are not fixed to a specific revision.
*
* If IMMEDIATES_ONLY is TRUE, only those externals defined to be checked out
* as immediate children of LOCAL_ABSPATH are returned (this is useful for
* treating user requested depth < infinity).
*
* If there are no externals to be returned, set *EXTERNALS to NULL. Otherwise
* set *EXTERNALS to an APR array newly cleated in RESULT_POOL.
*
* NOTE: This only returns the externals known by the immediate WC root for
* LOCAL_ABSPATH; i.e.:
* - If there is a further parent WC "above" the immediate WC root, and if
* that parent WC defines externals to live somewhere within this WC, these
* externals will appear to be foreign/unversioned and won't be picked up.
* - Likewise, only the topmost level of externals nestings (externals
* defined within a checked out external dir) is picked up by this function.
* (For recursion, see svn_wc__committable_externals_below().)
*
* ###TODO: Add a WRI_ABSPATH (wc root indicator) separate from LOCAL_ABSPATH,
* to allow searching any wc-root for externals under LOCAL_ABSPATH, not only
* LOCAL_ABSPATH's most immediate wc-root. */
svn_error_t *
svn_wc__db_committable_externals_below(apr_array_header_t **externals,
svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t immediates_only,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Gets a mapping from const char * local abspaths of externals to the const
char * local abspath of where they are defined for all externals defined
at or below LOCAL_ABSPATH.
### Returns NULL in *EXTERNALS until we bumped to format 29.
Allocate the result in RESULT_POOL and perform temporary allocations in
SCRATCH_POOL. */
svn_error_t *
svn_wc__db_externals_defined_below(apr_hash_t **externals,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Gather all svn:externals property values from the actual properties on
directories below LOCAL_ABSPATH as a mapping of const char *local_abspath
to const char * property values.
If DEPTHS is not NULL, set *depths to an apr_hash_t* mapping the same
local_abspaths to the const char * ambient depth of the node.
Allocate the result in RESULT_POOL and perform temporary allocations in
SCRATCH_POOL. */
svn_error_t *
svn_wc__db_externals_gather_definitions(apr_hash_t **externals,
apr_hash_t **depths,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* @} */
/* @defgroup svn_wc__db_op Operations on WORKING tree
@{
*/
/* Copy the node at SRC_ABSPATH (in NODES and ACTUAL_NODE tables) to
* DST_ABSPATH, both in DB but not necessarily in the same WC. The parent
* of DST_ABSPATH must be a versioned directory.
*
* This copy is NOT recursive. It simply establishes this one node, plus
* incomplete nodes for the children.
*
* If IS_MOVE is TRUE, mark this copy operation as the copy-half of
* a move. The delete-half of the move needs to be created separately
* with svn_wc__db_op_delete().
*
* Add WORK_ITEMS to the work queue. */
svn_error_t *
svn_wc__db_op_copy(svn_wc__db_t *db,
const char *src_abspath,
const char *dst_abspath,
const char *dst_op_root_abspath,
svn_boolean_t is_move,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool);
/* Checks if LOCAL_ABSPATH represents a move back to its original location,
* and if it is reverts the move while keeping local changes after it has been
* moved from MOVED_FROM_ABSPATH.
*
* If MOVED_BACK is not NULL, set *MOVED_BACK to TRUE when a move was reverted,
* otherwise to FALSE.
*/
svn_error_t *
svn_wc__db_op_handle_move_back(svn_boolean_t *moved_back,
svn_wc__db_t *db,
const char *local_abspath,
const char *moved_from_abspath,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool);
/* Copy the leaves of the op_depth layer directly shadowed by the operation
* of SRC_ABSPATH (so SRC_ABSPATH must be an op_root) to dst_abspaths
* parents layer.
*
* This operation is recursive. It copies all the descendants at the lower
* layer and adds base-deleted nodes on dst_abspath layer to mark these nodes
* properly deleted.
*
* Usually this operation is directly followed by a call to svn_wc__db_op_copy
* which performs the real copy from src_abspath to dst_abspath.
*/
svn_error_t *
svn_wc__db_op_copy_shadowed_layer(svn_wc__db_t *db,
const char *src_abspath,
const char *dst_abspath,
svn_boolean_t is_move,
apr_pool_t *scratch_pool);
/* Record a copy at LOCAL_ABSPATH from a repository directory.
This copy is NOT recursive. It simply establishes this one node.
CHILDREN must be provided, and incomplete nodes will be constructed
for them.
### arguments docco. */
svn_error_t *
svn_wc__db_op_copy_dir(svn_wc__db_t *db,
const char *local_abspath,
const apr_hash_t *props,
svn_revnum_t changed_rev,
apr_time_t changed_date,
const char *changed_author,
const char *original_repos_relpath,
const char *original_root_url,
const char *original_uuid,
svn_revnum_t original_revision,
const apr_array_header_t *children,
- svn_boolean_t is_move,
svn_depth_t depth,
+ svn_boolean_t is_move,
const svn_skel_t *conflict,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool);
/* Record a copy at LOCAL_ABSPATH from a repository file.
### arguments docco. */
svn_error_t *
svn_wc__db_op_copy_file(svn_wc__db_t *db,
const char *local_abspath,
const apr_hash_t *props,
svn_revnum_t changed_rev,
apr_time_t changed_date,
const char *changed_author,
const char *original_repos_relpath,
const char *original_root_url,
const char *original_uuid,
svn_revnum_t original_revision,
const svn_checksum_t *checksum,
svn_boolean_t update_actual_props,
const apr_hash_t *new_actual_props,
svn_boolean_t is_move,
const svn_skel_t *conflict,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool);
svn_error_t *
svn_wc__db_op_copy_symlink(svn_wc__db_t *db,
const char *local_abspath,
const apr_hash_t *props,
svn_revnum_t changed_rev,
apr_time_t changed_date,
const char *changed_author,
const char *original_repos_relpath,
const char *original_root_url,
const char *original_uuid,
svn_revnum_t original_revision,
const char *target,
+ svn_boolean_t is_move,
const svn_skel_t *conflict,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool);
/* ### do we need svn_wc__db_op_copy_server_excluded() ?? */
/* ### add a new versioned directory. a list of children is NOT passed
### since they are added in future, distinct calls to db_op_add_*.
PROPS gives the properties; empty or NULL means none. */
/* ### do we need a CONFLICTS param? */
svn_error_t *
svn_wc__db_op_add_directory(svn_wc__db_t *db,
const char *local_abspath,
const apr_hash_t *props,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool);
/* Add a file.
PROPS gives the properties; empty or NULL means none.
### this file has no "pristine"
### contents, so a checksum [reference] is not required. */
/* ### do we need a CONFLICTS param? */
svn_error_t *
svn_wc__db_op_add_file(svn_wc__db_t *db,
const char *local_abspath,
const apr_hash_t *props,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool);
/* Add a symlink.
PROPS gives the properties; empty or NULL means none. */
/* ### do we need a CONFLICTS param? */
svn_error_t *
svn_wc__db_op_add_symlink(svn_wc__db_t *db,
const char *local_abspath,
const char *target,
const apr_hash_t *props,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool);
/* Set the properties of the node LOCAL_ABSPATH in the ACTUAL tree to
PROPS.
PROPS maps "const char *" names to "const svn_string_t *" values.
To specify no properties, PROPS must be an empty hash, not NULL.
If the node is not present, return an error.
If PROPS is NULL, set the properties to be the same as the pristine
properties.
If CONFLICT is not NULL, it is used to register a conflict on this
node at the same time the properties are changed.
WORK_ITEMS are inserted into the work queue, as additional things that
need to be completed before the working copy is stable.
If CLEAR_RECORDED_INFO is true, the recorded information for the node
is cleared. (commonly used when updating svn:* magic properties).
NOTE: This will overwrite ALL working properties the node currently
has. There is no db_op_set_prop() function. Callers must read all the
properties, change one, and write all the properties.
### ugh. this has poor transaction semantics...
NOTE: This will create an entry in the ACTUAL table for the node if it
does not yet have one.
*/
svn_error_t *
svn_wc__db_op_set_props(svn_wc__db_t *db,
const char *local_abspath,
apr_hash_t *props,
svn_boolean_t clear_recorded_info,
const svn_skel_t *conflict,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool);
/* Mark LOCAL_ABSPATH, and all children, for deletion.
*
* This function removes the file externals (and if DELETE_DIR_EXTERNALS is
* TRUE also the directory externals) registered below LOCAL_ABSPATH.
* (DELETE_DIR_EXTERNALS should be true if also removing unversioned nodes)
*
* If MOVED_TO_ABSPATH is not NULL, mark the deletion of LOCAL_ABSPATH
* as the delete-half of a move from LOCAL_ABSPATH to MOVED_TO_ABSPATH.
*
* If NOTIFY_FUNC is not NULL, then it will be called (with NOTIFY_BATON)
* for each node deleted. While this processing occurs, if CANCEL_FUNC is
* not NULL, then it will be called (with CANCEL_BATON) to detect cancellation
* during the processing.
*
* Note: the notification (and cancellation) occur outside of a SQLite
* transaction.
*/
svn_error_t *
svn_wc__db_op_delete(svn_wc__db_t *db,
const char *local_abspath,
const char *moved_to_abspath,
svn_boolean_t delete_dir_externals,
svn_skel_t *conflict,
svn_skel_t *work_items,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool);
/* Mark all LOCAL_ABSPATH in the TARGETS array, and all of their children,
* for deletion.
*
* This function is more efficient than svn_wc__db_op_delete() because
* only one sqlite transaction is used for all targets.
* It currently lacks support for moves (though this could be changed,
* at which point svn_wc__db_op_delete() becomes redundant).
*
* This function removes the file externals (and if DELETE_DIR_EXTERNALS is
* TRUE also the directory externals) registered below the targets.
* (DELETE_DIR_EXTERNALS should be true if also removing unversioned nodes)
*
* If NOTIFY_FUNC is not NULL, then it will be called (with NOTIFY_BATON)
* for each node deleted. While this processing occurs, if CANCEL_FUNC is
* not NULL, then it will be called (with CANCEL_BATON) to detect cancellation
* during the processing.
*
* Note: the notification (and cancellation) occur outside of a SQLite
* transaction.
*/
svn_error_t *
svn_wc__db_op_delete_many(svn_wc__db_t *db,
apr_array_header_t *targets,
svn_boolean_t delete_dir_externals,
const svn_skel_t *conflict,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool);
/* ### mark PATH as (possibly) modified. "svn edit" ... right API here? */
svn_error_t *
svn_wc__db_op_modified(svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool);
/* ### use NULL to remove from a changelist.
### NOTE: only depth=svn_depth_empty is supported right now.
*/
svn_error_t *
svn_wc__db_op_set_changelist(svn_wc__db_t *db,
const char *local_abspath,
const char *new_changelist,
const apr_array_header_t *changelist_filter,
svn_depth_t depth,
/* ### flip to CANCEL, then NOTIFY. precedent. */
svn_wc_notify_func2_t notify_func,
void *notify_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool);
/* Record CONFLICT on LOCAL_ABSPATH, potentially replacing other conflicts
recorded on LOCAL_ABSPATH.
Users should in most cases pass CONFLICT to another WC_DB call instead of
calling svn_wc__db_op_mark_conflict() directly outside a transaction, to
allow recording atomically with the operation involved.
Any work items that are necessary as part of marking this node conflicted
can be passed in WORK_ITEMS.
*/
svn_error_t *
svn_wc__db_op_mark_conflict(svn_wc__db_t *db,
const char *local_abspath,
const svn_skel_t *conflict,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool);
/* ### caller maintains ACTUAL, and how the resolution occurred. we're just
### recording state.
###
### I'm not sure that these three values are the best way to do this,
### but they're handy for now. */
svn_error_t *
svn_wc__db_op_mark_resolved(svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t resolved_text,
svn_boolean_t resolved_props,
svn_boolean_t resolved_tree,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool);
/* Revert all local changes which are being maintained in the database,
* including conflict storage, properties and text modification status.
*
* Returns SVN_ERR_WC_INVALID_OPERATION_DEPTH if the revert is not
* possible, e.g. copy/delete but not a root, or a copy root with
* children.
*
* At present only depth=empty and depth=infinity are supported.
*
* This function populates the revert list that can be queried to
* determine what was reverted.
*/
svn_error_t *
svn_wc__db_op_revert(svn_wc__db_t *db,
const char *local_abspath,
svn_depth_t depth,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Query the revert list for LOCAL_ABSPATH and set *REVERTED if the
* path was reverted. Set *MARKER_FILES to a const char *list of
* marker files if any were recorded on LOCAL_ABSPATH.
*
* Set *COPIED_HERE if the reverted node was copied here and is the
* operation root of the copy.
* Set *KIND to the node kind of the reverted node.
*
* Removes the row for LOCAL_ABSPATH from the revert list.
*/
svn_error_t *
svn_wc__db_revert_list_read(svn_boolean_t *reverted,
const apr_array_header_t **marker_files,
svn_boolean_t *copied_here,
svn_node_kind_t *kind,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* The type of elements in the array returned by
* svn_wc__db_revert_list_read_copied_children(). */
typedef struct svn_wc__db_revert_list_copied_child_info_t {
const char *abspath;
svn_node_kind_t kind;
} svn_wc__db_revert_list_copied_child_info_t ;
/* Return in *CHILDREN a list of reverted copied nodes at or within
* LOCAL_ABSPATH (which is a reverted file or a reverted directory).
* Allocate *COPIED_CHILDREN and its elements in RESULT_POOL.
* The elements are of type svn_wc__db_revert_list_copied_child_info_t. */
svn_error_t *
svn_wc__db_revert_list_read_copied_children(const apr_array_header_t **children,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Make revert notifications for all paths in the revert list that are
* equal to LOCAL_ABSPATH or below LOCAL_ABSPATH.
*
* Removes all the corresponding rows from the revert list.
*
* ### Pass in cancel_func?
*/
svn_error_t *
svn_wc__db_revert_list_notify(svn_wc_notify_func2_t notify_func,
void *notify_baton,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool);
/* Clean up after svn_wc__db_op_revert by removing the revert list.
*/
svn_error_t *
svn_wc__db_revert_list_done(svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool);
/* ### status */
/* @} */
/* @defgroup svn_wc__db_read Read operations on the BASE/WORKING tree
@{
These functions query information about nodes in ACTUAL, and returns
the requested information from the appropriate ACTUAL, WORKING, or
BASE tree.
For example, asking for the checksum of the pristine version will
return the one recorded in WORKING, or if no WORKING node exists, then
the checksum comes from BASE.
*/
/* Retrieve information about a node.
For the node implied by LOCAL_ABSPATH from the local filesystem, return
information in the provided OUT parameters. Each OUT parameter may be
NULL, indicating that specific item is not requested.
The information returned comes from the BASE tree, as possibly modified
by the WORKING and ACTUAL trees.
If there is no information about the node, then SVN_ERR_WC_PATH_NOT_FOUND
will be returned.
The OUT parameters, and their "not available" values are:
STATUS n/a (always available)
KIND svn_node_unknown (For ACTUAL only nodes)
REVISION SVN_INVALID_REVNUM
REPOS_RELPATH NULL
REPOS_ROOT_URL NULL
REPOS_UUID NULL
CHANGED_REV SVN_INVALID_REVNUM
CHANGED_DATE 0
CHANGED_AUTHOR NULL
DEPTH svn_depth_unknown
CHECKSUM NULL
TARGET NULL
ORIGINAL_REPOS_RELPATH NULL
ORIGINAL_ROOT_URL NULL
ORIGINAL_UUID NULL
ORIGINAL_REVISION SVN_INVALID_REVNUM
LOCK NULL
RECORDED_SIZE SVN_INVALID_FILESIZE
RECORDED_TIME 0
CHANGELIST NULL
CONFLICTED FALSE
OP_ROOT FALSE
HAD_PROPS FALSE
PROPS_MOD FALSE
HAVE_BASE FALSE
HAVE_MORE_WORK FALSE
HAVE_WORK FALSE
When STATUS is requested, then it will be one of these values:
svn_wc__db_status_normal
A plain BASE node, with no local changes.
svn_wc__db_status_added
A node has been added/copied/moved to here. See HAVE_BASE to see
if this change overwrites a BASE node. Use scan_addition() to resolve
whether this has been added, copied, or moved, and the details of the
operation (this function only looks at LOCAL_ABSPATH, but resolving
the details requires scanning one or more ancestor nodes).
svn_wc__db_status_deleted
This node has been deleted or moved away. It may be a delete/move of
a BASE node, or a child node of a subtree that was copied/moved to
an ancestor location. Call scan_deletion() to determine the full
details of the operations upon this node.
svn_wc__db_status_server_excluded
The node is versioned/known by the server, but the server has
decided not to provide further information about the node. This
is a BASE node (since changes are not allowed to this node).
svn_wc__db_status_excluded
The node has been excluded from the working copy tree. This may
be an exclusion from the BASE tree, or an exclusion in the
WORKING tree for a child node of a copied/moved parent.
svn_wc__db_status_not_present
This is a node from the BASE tree, has been marked as "not-present"
within this mixed-revision working copy. This node is at a revision
that is not in the tree, contrary to its inclusion in the parent
node's revision.
svn_wc__db_status_incomplete
The BASE is incomplete due to an interrupted operation. An
incomplete WORKING node will be svn_wc__db_status_added.
If REVISION is requested, it will be set to the revision of the
unmodified (BASE) node, or to SVN_INVALID_REVNUM if any structural
changes have been made to that node (that is, if the node has a row in
the WORKING table).
If DEPTH is requested, and the node is NOT a directory, then
the value will be set to svn_depth_unknown.
If CHECKSUM is requested, and the node is NOT a file, then it will
be set to NULL.
If TARGET is requested, and the node is NOT a symlink, then it will
be set to NULL.
If TRANSLATED_SIZE is requested, and the node is NOT a file, then
it will be set to SVN_INVALID_FILESIZE.
If HAVE_WORK is TRUE, the returned information is from the highest WORKING
layer. In that case HAVE_MORE_WORK and HAVE_BASE provide information about
what other layers exist for this node.
If HAVE_WORK is FALSE and HAVE_BASE is TRUE then the information is from
the BASE tree.
If HAVE_WORK and HAVE_BASE are both FALSE and when retrieving CONFLICTED,
then the node doesn't exist at all.
If OP_ROOT is requested and the node has a WORKING layer, OP_ROOT will be
set to true if this node is the op_root for this layer.
If HAD_PROPS is requested and the node has pristine props, the value will
be set to TRUE.
If PROPS_MOD is requested and the node has property modification the value
will be set to TRUE.
### add information about the need to scan upwards to get a complete
### picture of the state of this node.
### add some documentation about OUT parameter values based on STATUS ??
### the TEXT_MOD may become an enumerated value at some point to
### indicate different states of knowledge about text modifications.
### for example, an "svn edit" command in the future might set a
### flag indicating administratively-defined modification. and/or we
### might have a status indicating that we saw it was modified while
### performing a filesystem traversal.
All returned data will be allocated in RESULT_POOL. All temporary
allocations will be made in SCRATCH_POOL.
*/
/* ### old docco. needs to be incorporated as appropriate. there is
### some pending, potential changes to the definition of this API,
### so not worrying about it just yet.
### if the node has not been committed (after adding):
### revision will be SVN_INVALID_REVNUM
### repos_* will be NULL
### changed_rev will be SVN_INVALID_REVNUM
### changed_date will be 0
### changed_author will be NULL
### status will be svn_wc__db_status_added
### text_mod will be TRUE
### prop_mod will be TRUE if any props have been set
### base_shadowed will be FALSE
### if the node is not a copy, or a move destination:
### original_repos_path will be NULL
### original_root_url will be NULL
### original_uuid will be NULL
### original_revision will be SVN_INVALID_REVNUM
### note that @a base_shadowed can be derived. if the status specifies
### an add/copy/move *and* there is a corresponding node in BASE, then
### the BASE has been deleted to open the way for this node.
*/
svn_error_t *
svn_wc__db_read_info(svn_wc__db_status_t *status, /* ### derived */
svn_node_kind_t *kind,
svn_revnum_t *revision,
const char **repos_relpath,
const char **repos_root_url,
const char **repos_uuid,
svn_revnum_t *changed_rev,
apr_time_t *changed_date,
const char **changed_author,
svn_depth_t *depth, /* dirs only */
const svn_checksum_t **checksum, /* files only */
const char **target, /* symlinks only */
/* ### the following fields if copied/moved (history) */
const char **original_repos_relpath,
const char **original_root_url,
const char **original_uuid,
svn_revnum_t *original_revision,
/* For BASE nodes */
svn_wc__db_lock_t **lock,
/* Recorded for files present in the working copy */
svn_filesize_t *recorded_size,
apr_time_t *recorded_time,
/* From ACTUAL */
const char **changelist,
svn_boolean_t *conflicted,
/* ### the followed are derived fields */
svn_boolean_t *op_root,
svn_boolean_t *had_props,
svn_boolean_t *props_mod,
svn_boolean_t *have_base,
svn_boolean_t *have_more_work,
svn_boolean_t *have_work,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Structure used as linked list in svn_wc__db_info_t to describe all nodes
in this location that were moved to another location */
struct svn_wc__db_moved_to_info_t
{
const char *moved_to_abspath;
const char *shadow_op_root_abspath;
struct svn_wc__db_moved_to_info_t *next;
};
/* Structure returned by svn_wc__db_read_children_info. Only has the
fields needed by status. */
struct svn_wc__db_info_t {
svn_wc__db_status_t status;
svn_node_kind_t kind;
svn_revnum_t revnum;
const char *repos_relpath;
const char *repos_root_url;
const char *repos_uuid;
svn_revnum_t changed_rev;
const char *changed_author;
apr_time_t changed_date;
svn_depth_t depth;
svn_filesize_t recorded_size;
apr_time_t recorded_time;
const char *changelist;
svn_boolean_t conflicted;
#ifdef HAVE_SYMLINK
svn_boolean_t special;
#endif
svn_boolean_t op_root;
svn_boolean_t has_checksum;
svn_boolean_t copied;
svn_boolean_t had_props;
svn_boolean_t props_mod;
svn_boolean_t have_base;
svn_boolean_t have_more_work;
svn_boolean_t locked; /* WC directory lock */
svn_wc__db_lock_t *lock; /* Repository file lock */
svn_boolean_t incomplete; /* TRUE if a working node is incomplete */
struct svn_wc__db_moved_to_info_t *moved_to; /* A linked list of locations
where nodes at this path
are moved to. Highest layers
first */
svn_boolean_t moved_here; /* Only on op-roots. */
svn_boolean_t file_external;
};
/* Return in *NODES a hash mapping name->struct svn_wc__db_info_t for
the children of DIR_ABSPATH, and in *CONFLICTS a hash of names in
conflict.
The results include any path that was a child of a deleted directory that
existed at LOCAL_ABSPATH, even if that directory is now scheduled to be
replaced by the working node at LOCAL_ABSPATH.
*/
svn_error_t *
svn_wc__db_read_children_info(apr_hash_t **nodes,
apr_hash_t **conflicts,
svn_wc__db_t *db,
const char *dir_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Like svn_wc__db_read_children_info, but only gets an info node for the root
element. */
svn_error_t *
svn_wc__db_read_single_info(const struct svn_wc__db_info_t **info,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Structure returned by svn_wc__db_read_walker_info. Only has the
fields needed by svn_wc__internal_walk_children(). */
struct svn_wc__db_walker_info_t {
svn_wc__db_status_t status;
svn_node_kind_t kind;
};
/* When a node is deleted in WORKING, some of its information is no longer
available. But in some cases it might still be relevant to obtain this
information even when the information isn't stored in the BASE tree.
This function allows access to that specific information.
When a node is not deleted, this node returns the same information
as svn_wc__db_read_info().
All output arguments are optional and behave in the same way as when
calling svn_wc__db_read_info().
(All other information (like original_*) can be obtained via other apis).
*PROPS maps "const char *" names to "const svn_string_t *" values. If
the pristine node is capable of having properties but has none, set
*PROPS to an empty hash. If its status is such that it cannot have
properties, set *PROPS to NULL.
*/
svn_error_t *
svn_wc__db_read_pristine_info(svn_wc__db_status_t *status,
svn_node_kind_t *kind,
svn_revnum_t *changed_rev,
apr_time_t *changed_date,
const char **changed_author,
svn_depth_t *depth, /* dirs only */
const svn_checksum_t **checksum, /* files only */
const char **target, /* symlinks only */
svn_boolean_t *had_props,
apr_hash_t **props,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Gets the information required to install a pristine file to the working copy
Set WCROOT_ABSPATH to the working copy root, SHA1_CHECKSUM to the
checksum of the node (a valid reference into the pristine store)
and PRISTINE_PROPS to the node's pristine properties (to use for
installing the file).
If WRI_ABSPATH is not NULL, check for information in the working copy
identified by WRI_ABSPATH.
*/
svn_error_t *
svn_wc__db_read_node_install_info(const char **wcroot_abspath,
const svn_checksum_t **sha1_checksum,
apr_hash_t **pristine_props,
apr_time_t *changed_date,
svn_wc__db_t *db,
const char *local_abspath,
const char *wri_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Return in *NODES a hash mapping name->struct svn_wc__db_walker_info_t for
the children of DIR_ABSPATH. "name" is the child's name relative to
DIR_ABSPATH, not an absolute path. */
svn_error_t *
svn_wc__db_read_children_walker_info(apr_hash_t **nodes,
svn_wc__db_t *db,
const char *dir_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/**
* Set *URL to the corresponding url for LOCAL_ABSPATH.
* If the node is added, return the url it will have in the repository.
*/
svn_error_t *
svn_wc__db_read_url(const char **url,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Set *PROPS to the properties of the node LOCAL_ABSPATH in the ACTUAL
tree (looking through to the WORKING or BASE tree as required).
### *PROPS will be set to NULL in the following situations:
### ... tbd
PROPS maps "const char *" names to "const svn_string_t *" values.
If the node has no properties, set *PROPS to an empty hash.
If the node is not present, return an error.
Allocate *PROPS and its keys and values in RESULT_POOL.
*/
svn_error_t *
svn_wc__db_read_props(apr_hash_t **props,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Call RECEIVER_FUNC, passing RECEIVER_BATON, an absolute path, and
* a hash table mapping <tt>char *</tt> names onto svn_string_t *
* values for any properties of child nodes of LOCAL_ABSPATH (up to DEPTH).
*
* If PRISTINE is FALSE, read the properties from the WORKING layer (highest
* op_depth); if PRISTINE is FALSE, local modifications will be visible.
*/
svn_error_t *
svn_wc__db_read_props_streamily(svn_wc__db_t *db,
const char *local_abspath,
svn_depth_t depth,
svn_boolean_t pristine,
const apr_array_header_t *changelists,
svn_wc__proplist_receiver_t receiver_func,
void *receiver_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool);
/* Set *PROPS to the properties of the node LOCAL_ABSPATH in the WORKING
tree (looking through to the BASE tree as required).
### *PROPS will set set to NULL in the following situations:
### ... tbd. see props.c:svn_wc__get_pristine_props()
*PROPS maps "const char *" names to "const svn_string_t *" values.
If the node has no properties, set *PROPS to an empty hash.
If the node is not present, return an error.
Allocate *PROPS and its keys and values in RESULT_POOL.
*/
svn_error_t *
svn_wc__db_read_pristine_props(apr_hash_t **props,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/**
* Set @a *iprops to a depth-first ordered array of
* #svn_prop_inherited_item_t * structures representing the properties
* inherited by @a local_abspath from the ACTUAL tree above
* @a local_abspath (looking through to the WORKING or BASE tree as
* required), up to and including the root of the working copy and
* any cached inherited properties inherited by the root.
*
* The #svn_prop_inherited_item_t->path_or_url members of the
* #svn_prop_inherited_item_t * structures in @a *iprops are
* paths relative to the repository root URL for cached inherited
* properties and absolute working copy paths otherwise.
*
* If ACTUAL_PROPS is not NULL, then set *ACTUAL_PROPS to the actual
* properties stored on LOCAL_ABSPATH.
*
* Allocate @a *iprops in @a result_pool. Use @a scratch_pool
* for temporary allocations.
*/
svn_error_t *
svn_wc__db_read_inherited_props(apr_array_header_t **iprops,
apr_hash_t **actual_props,
svn_wc__db_t *db,
const char *local_abspath,
const char *propname,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Read a BASE node's inherited property information.
Set *IPROPS to to a depth-first ordered array of
svn_prop_inherited_item_t * structures representing the cached
inherited properties for the BASE node at LOCAL_ABSPATH.
If no cached properties are found, then set *IPROPS to NULL.
If LOCAL_ABSPATH represents the root of the repository, then set
*IPROPS to an empty array.
Allocate *IPROPS in RESULT_POOL, use SCRATCH_POOL for temporary
allocations. */
svn_error_t *
svn_wc__db_read_cached_iprops(apr_array_header_t **iprops,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Find BASE nodes with cached inherited properties.
Set *IPROPS_PATHS to a hash mapping const char * absolute working copy
paths to the repos_relpath of the path for each path in the working copy
at or below LOCAL_ABSPATH, limited by DEPTH, that has cached inherited
properties for the BASE node of the path.
Allocate *IPROP_PATHS in RESULT_POOL.
Use SCRATCH_POOL for temporary allocations. */
svn_error_t *
svn_wc__db_get_children_with_cached_iprops(apr_hash_t **iprop_paths,
svn_depth_t depth,
const char *local_abspath,
svn_wc__db_t *db,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/** Obtain a mapping of const char * local_abspaths to const svn_string_t*
* property values in *VALUES, of all PROPNAME properties on LOCAL_ABSPATH
* and its descendants.
*
* Allocate the result in RESULT_POOL, and perform temporary allocations in
* SCRATCH_POOL.
*/
svn_error_t *
svn_wc__db_prop_retrieve_recursive(apr_hash_t **values,
svn_wc__db_t *db,
const char *local_abspath,
const char *propname,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Set *CHILDREN to a new array of the (const char *) basenames of the
immediate children of the working node at LOCAL_ABSPATH in DB.
Return every path that refers to a child of the working node at
LOCAL_ABSPATH. Do not include a path just because it was a child of a
deleted directory that existed at LOCAL_ABSPATH if that directory is now
scheduled to be replaced by the working node at LOCAL_ABSPATH.
Allocate *CHILDREN in RESULT_POOL and do temporary allocations in
SCRATCH_POOL.
### return some basic info for each child? e.g. kind.
### maybe the data in _read_get_info should be a structure, and this
### can return a struct for each one.
### however: _read_get_info can say "not interested", which isn't the
### case with a struct. thus, a struct requires fetching and/or
### computing all info.
*/
svn_error_t *
svn_wc__db_read_children_of_working_node(const apr_array_header_t **children,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Like svn_wc__db_read_children_of_working_node(), except also include any
path that was a child of a deleted directory that existed at
LOCAL_ABSPATH, even if that directory is now scheduled to be replaced by
the working node at LOCAL_ABSPATH.
*/
svn_error_t *
svn_wc__db_read_children(const apr_array_header_t **children,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Read into *VICTIMS the basenames of the immediate children of
LOCAL_ABSPATH in DB that are conflicted.
In case of tree conflicts a victim doesn't have to be in the
working copy.
Allocate *VICTIMS in RESULT_POOL and do temporary allocations in
SCRATCH_POOL */
/* ### This function will probably be removed. */
svn_error_t *
svn_wc__db_read_conflict_victims(const apr_array_header_t **victims,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Read into *MARKER_FILES the absolute paths of the marker files
of conflicts stored on LOCAL_ABSPATH and its immediate children in DB.
The on-disk files may have been deleted by the user.
Allocate *MARKER_FILES in RESULT_POOL and do temporary allocations
in SCRATCH_POOL */
svn_error_t *
svn_wc__db_get_conflict_marker_files(apr_hash_t **markers,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Read the conflict information recorded on LOCAL_ABSPATH in *CONFLICT,
an editable conflict skel.
If the node exists, but does not have a conflict set *CONFLICT to NULL,
otherwise return a SVN_ERR_WC_PATH_NOT_FOUND error.
Allocate *CONFLICTS in RESULT_POOL and do temporary allocations in
SCRATCH_POOL */
svn_error_t *
svn_wc__db_read_conflict(svn_skel_t **conflict,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Return the kind of the node in DB at LOCAL_ABSPATH. The WORKING tree will
be examined first, then the BASE tree. If the node is not present in either
tree and ALLOW_MISSING is TRUE, then svn_node_unknown is returned.
If the node is missing and ALLOW_MISSING is FALSE, then it will return
SVN_ERR_WC_PATH_NOT_FOUND.
The SHOW_HIDDEN and SHOW_DELETED flags report certain states as kind none.
When nodes have certain statee they are only reported when:
svn_wc__db_status_not_present when show_hidden && show_deleted
svn_wc__db_status_excluded when show_hidden
svn_wc__db_status_server_excluded when show_hidden
svn_wc__db_status_deleted when show_deleted
In other cases these nodes are reported with *KIND as svn_node_none.
(See also svn_wc_read_kind2()'s documentation)
Uses SCRATCH_POOL for temporary allocations. */
svn_error_t *
svn_wc__db_read_kind(svn_node_kind_t *kind,
svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t allow_missing,
svn_boolean_t show_deleted,
svn_boolean_t show_hidden,
apr_pool_t *scratch_pool);
/* An analog to svn_wc__entry_is_hidden(). Set *HIDDEN to TRUE if
LOCAL_ABSPATH in DB "is not present, and I haven't scheduled something
over the top of it." */
svn_error_t *
svn_wc__db_node_hidden(svn_boolean_t *hidden,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool);
/* Checks if a node replaces a node in a different layer. Also check if it
replaces a BASE (op_depth 0) node or just a node in a higher layer (a copy).
Finally check if this is the root of the replacement, or if the replacement
is initiated by the parent node.
IS_REPLACE_ROOT (if not NULL) is set to TRUE if the node is the root of a
replacement; otherwise to FALSE.
BASE_REPLACE (if not NULL) is set to TRUE if the node directly or indirectly
replaces a node in the BASE tree; otherwise to FALSE.
IS_REPLACE (if not NULL) is set to TRUE if the node directly replaces a node
in a lower layer; otherwise to FALSE.
*/
svn_error_t *
svn_wc__db_node_check_replace(svn_boolean_t *is_replace_root,
svn_boolean_t *base_replace,
svn_boolean_t *is_replace,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool);
/* ### changelists. return an array, or an iterator interface? how big
### are these things? are we okay with an in-memory array? examine other
### changelist usage -- we may already assume the list fits in memory.
*/
/* The DB-private version of svn_wc__is_wcroot(), which see.
*/
svn_error_t *
svn_wc__db_is_wcroot(svn_boolean_t *is_wcroot,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool);
/* Check whether a node is a working copy root and/or switched.
If LOCAL_ABSPATH is the root of a working copy, set *IS_WC_ROOT to TRUE,
otherwise to FALSE.
If LOCAL_ABSPATH is switched against its parent in the same working copy
set *IS_SWITCHED to TRUE, otherwise to FALSE.
If KIND is not null, set *KIND to the node type of LOCAL_ABSPATH.
Any of the output arguments can be null to specify that the result is not
interesting to the caller.
Use SCRATCH_POOL for temporary allocations.
*/
svn_error_t *
svn_wc__db_is_switched(svn_boolean_t *is_wcroot,
svn_boolean_t *is_switched,
svn_node_kind_t *kind,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool);
/* @} */
/* @defgroup svn_wc__db_global Operations that alter multiple trees
@{
*/
/* Associate LOCAL_DIR_ABSPATH, and all its children with the repository at
at REPOS_ROOT_URL. The relative path to the repos root will not change,
just the repository root. The repos uuid will also remain the same.
This also updates any locks which may exist for the node, as well as any
copyfrom repository information. Finally, the DAV cache (aka
"wcprops") will be reset for affected entries.
Use SCRATCH_POOL for any temporary allocations.
### local_dir_abspath "should be" the wcroot or a switch root. all URLs
### under this directory (depth=infinity) will be rewritten.
### This API had a depth parameter, which was removed, should it be
### resurrected? What's the purpose if we claim relocate is infinitely
### recursive?
### Assuming the future ability to copy across repositories, should we
### refrain from resetting the copyfrom information in this operation?
*/
svn_error_t *
svn_wc__db_global_relocate(svn_wc__db_t *db,
const char *local_dir_abspath,
const char *repos_root_url,
apr_pool_t *scratch_pool);
/* ### docco
### collapse the WORKING and ACTUAL tree changes down into BASE, called
for each committed node.
NEW_REVISION must be the revision number of the revision created by
the commit. It will become the BASE node's 'revnum' and 'changed_rev'
values in the BASE_NODE table.
CHANGED_REVISION is the new 'last changed' revision. If the node is
modified its value is equivalent to NEW_REVISION, but in case of a
descendant of a copy/move it can be an older revision.
CHANGED_DATE is the (server-side) date of CHANGED_REVISION. It may be 0 if
the revprop is missing on the revision.
CHANGED_AUTHOR is the (server-side) author of CHANGED_REVISION. It may be
NULL if the revprop is missing on the revision.
One or both of NEW_CHECKSUM and NEW_CHILDREN should be NULL. For new:
files: NEW_CHILDREN should be NULL
dirs: NEW_CHECKSUM should be NULL
symlinks: both should be NULL
WORK_ITEMS will be place into the work queue.
*/
svn_error_t *
svn_wc__db_global_commit(svn_wc__db_t *db,
const char *local_abspath,
svn_revnum_t new_revision,
svn_revnum_t changed_revision,
apr_time_t changed_date,
const char *changed_author,
const svn_checksum_t *new_checksum,
const apr_array_header_t *new_children,
apr_hash_t *new_dav_cache,
svn_boolean_t keep_changelist,
svn_boolean_t no_unlock,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool);
/* ### docco
Perform an "update" operation at this node. It will create/modify a BASE
node, and possibly update the ACTUAL tree's node (e.g put the node into
a conflicted state).
### there may be cases where we need to tweak an existing WORKING node
### this operations on a single node, but may affect children
### the repository cannot be changed with this function, but a "switch"
### (aka changing repos_relpath) is possible
### one of NEW_CHILDREN, NEW_CHECKSUM, or NEW_TARGET must be provided.
### the other two values must be NULL.
### should this be broken out into an update_(directory|file|symlink) ?
### how does this differ from base_add_*? just the CONFLICT param.
### the WORK_ITEMS param is new here, but the base_add_* functions
### should probably grow that. should we instead just (re)use base_add
### rather than grow a new function?
### this does not allow a change of depth
### we do not update a file's TRANSLATED_SIZE here. at some future point,
### when the file is installed, then a TRANSLATED_SIZE will be set.
*/
svn_error_t *
svn_wc__db_global_update(svn_wc__db_t *db,
const char *local_abspath,
svn_node_kind_t new_kind,
const char *new_repos_relpath,
svn_revnum_t new_revision,
const apr_hash_t *new_props,
svn_revnum_t new_changed_rev,
apr_time_t new_changed_date,
const char *new_changed_author,
const apr_array_header_t *new_children,
const svn_checksum_t *new_checksum,
const char *new_target,
const apr_hash_t *new_dav_cache,
const svn_skel_t *conflict,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool);
/* Modify the entry of working copy LOCAL_ABSPATH, presumably after an update
of depth DEPTH completes. If LOCAL_ABSPATH doesn't exist, this routine
does nothing.
Set the node's repository relpath, repository root, repository uuid and
revision to NEW_REPOS_RELPATH, NEW_REPOS_ROOT and NEW_REPOS_UUID. If
NEW_REPOS_RELPATH is null, the repository location is untouched; if
NEW_REVISION in invalid, the working revision field is untouched.
The modifications are mutually exclusive. If NEW_REPOS_ROOT is non-NULL,
set the repository root of the entry to NEW_REPOS_ROOT.
If LOCAL_ABSPATH is a directory, then, walk entries below LOCAL_ABSPATH
according to DEPTH thusly:
If DEPTH is svn_depth_infinity, perform the following actions on
every entry below PATH; if svn_depth_immediates, svn_depth_files,
or svn_depth_empty, perform them only on LOCAL_ABSPATH.
If NEW_REVISION is valid, then tweak every entry to have this new
working revision (excluding files that are scheduled for addition
or replacement). Likewise, if BASE_URL is non-null, then rewrite
all urls to be "telescoping" children of the base_url.
EXCLUDE_RELPATHS is a hash containing const char *local_relpath. Nodes
for pathnames contained in EXCLUDE_RELPATHS are not touched by this
function. These pathnames should be paths relative to the wcroot.
If WCROOT_IPROPS is not NULL it is a hash mapping const char * absolute
working copy paths to depth-first ordered arrays of
svn_prop_inherited_item_t * structures. If LOCAL_ABSPATH exists in
WCROOT_IPROPS, then set the hashed value as the node's inherited
properties.
*/
svn_error_t *
svn_wc__db_op_bump_revisions_post_update(svn_wc__db_t *db,
const char *local_abspath,
svn_depth_t depth,
const char *new_repos_relpath,
const char *new_repos_root_url,
const char *new_repos_uuid,
svn_revnum_t new_revision,
apr_hash_t *exclude_relpaths,
apr_hash_t *wcroot_iprops,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool);
/* Record the RECORDED_SIZE and RECORDED_TIME for a versioned node.
This function will record the information within the WORKING node,
if present, or within the BASE tree. If neither node is present, then
SVN_ERR_WC_PATH_NOT_FOUND will be returned.
RECORDED_SIZE may be SVN_INVALID_FILESIZE, which will be recorded
as such, implying "unknown size".
RECORDED_TIME may be 0, which will be recorded as such, implying
"unknown last mod time".
*/
svn_error_t *
svn_wc__db_global_record_fileinfo(svn_wc__db_t *db,
const char *local_abspath,
svn_filesize_t recorded_size,
apr_time_t recorded_time,
apr_pool_t *scratch_pool);
/* ### post-commit handling.
### maybe multiple phases?
### 1) mark a changelist as being-committed
### 2) collect ACTUAL content, store for future use as TEXTBASE
### 3) caller performs commit
### 4) post-commit, integrate changelist into BASE
*/
/* @} */
/* @defgroup svn_wc__db_lock Function to manage the LOCKS table.
@{
*/
/* Add or replace LOCK for LOCAL_ABSPATH to DB. */
svn_error_t *
svn_wc__db_lock_add(svn_wc__db_t *db,
const char *local_abspath,
const svn_wc__db_lock_t *lock,
apr_pool_t *scratch_pool);
/* Remove any lock for LOCAL_ABSPATH in DB. */
svn_error_t *
svn_wc__db_lock_remove(svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool);
/* @} */
/* @defgroup svn_wc__db_scan Functions to scan up a tree for further data.
@{
*/
/* Read a BASE node's repository information.
For the BASE node implied by LOCAL_ABSPATH, its location in the repository
returned in *REPOS_ROOT_URL and *REPOS_UUID will be returned in
*REPOS_RELPATH. Any of the OUT parameters may be NULL, indicating no
interest in that piece of information.
All returned data will be allocated in RESULT_POOL. All temporary
allocations will be made in SCRATCH_POOL.
### Either delete this function and use _base_get_info instead, or
### add a 'revision' output to make a complete repository node location
### and rename to not say 'scan', because it doesn't.
*/
svn_error_t *
svn_wc__db_scan_base_repos(const char **repos_relpath,
const char **repos_root_url,
const char **repos_uuid,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Scan upwards for information about a known addition to the WORKING tree.
IFF a node's status as returned by svn_wc__db_read_info() is
svn_wc__db_status_added (NOT obstructed_add!), then this function
returns a refined status in *STATUS, which is one of:
svn_wc__db_status_added -- this NODE is a simple add without history.
OP_ROOT_ABSPATH will be set to the topmost node in the added subtree
(implying its parent will be an unshadowed BASE node). The REPOS_*
values will be implied by that ancestor BASE node and this node's
position in the added subtree. ORIGINAL_* will be set to their
NULL values (and SVN_INVALID_REVNUM for ORIGINAL_REVISION).
svn_wc__db_status_copied -- this NODE is the root or child of a copy.
The root of the copy will be stored in OP_ROOT_ABSPATH. Note that
the parent of the operation root could be another WORKING node (from
an add, copy, or move). The REPOS_* values will be implied by the
ancestor unshadowed BASE node. ORIGINAL_* will indicate the source
of the copy.
svn_wc__db_status_incomplete -- this NODE is copied but incomplete.
svn_wc__db_status_moved_here -- this NODE arrived as a result of a move.
The root of the moved nodes will be stored in OP_ROOT_ABSPATH.
Similar to the copied state, its parent may be a WORKING node or a
BASE node. And again, the REPOS_* values are implied by this node's
position in the subtree under the ancestor unshadowed BASE node.
ORIGINAL_* will indicate the source of the move.
All OUT parameters may be NULL to indicate a lack of interest in
that piece of information.
STATUS, OP_ROOT_ABSPATH, and REPOS_* will always be assigned a value
if that information is requested (and assuming a successful return).
ORIGINAL_REPOS_RELPATH will refer to the *root* of the operation. It
does *not* correspond to the node given by LOCAL_ABSPATH. The caller
can use the suffix on LOCAL_ABSPATH (relative to OP_ROOT_ABSPATH) in
order to compute the source node which corresponds to LOCAL_ABSPATH.
If the node given by LOCAL_ABSPATH does not have changes recorded in
the WORKING tree, then SVN_ERR_WC_PATH_NOT_FOUND is returned. If it
doesn't have an "added" status, then SVN_ERR_WC_PATH_UNEXPECTED_STATUS
will be returned.
All returned data will be allocated in RESULT_POOL. All temporary
allocations will be made in SCRATCH_POOL.
*/
svn_error_t *
svn_wc__db_scan_addition(svn_wc__db_status_t *status,
const char **op_root_abspath,
const char **repos_relpath,
const char **repos_root_url,
const char **repos_uuid,
const char **original_repos_relpath,
const char **original_root_url,
const char **original_uuid,
svn_revnum_t *original_revision,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Scan the working copy for move information of the node LOCAL_ABSPATH.
* If LOCAL_ABSPATH return a SVN_ERR_WC_PATH_UNEXPECTED_STATUS error.
*
* If not NULL *MOVED_FROM_ABSPATH will be set to the previous location
* of LOCAL_ABSPATH, before it or an ancestror was moved.
*
* If not NULL *OP_ROOT_ABSPATH will be set to the new location of the
* path that was actually moved
*
* If not NULL *OP_ROOT_MOVED_FROM_ABSPATH will be set to the old location
* of the path that was actually moved.
*
* If not NULL *MOVED_FROM_DELETE_ABSPATH will be set to the ancestor of the
* moved from location that deletes the original location
*
* Given a working copy
* A/B/C
* svn mv A/B D
* svn rm A
*
* You can call this function on D and D/C. When called on D/C all output
* MOVED_FROM_ABSPATH will be A/B/C
* OP_ROOT_ABSPATH will be D
* OP_ROOT_MOVED_FROM_ABSPATH will be A/B
* MOVED_FROM_DELETE_ABSPATH will be A
*/
svn_error_t *
svn_wc__db_scan_moved(const char **moved_from_abspath,
const char **op_root_abspath,
const char **op_root_moved_from_abspath,
const char **moved_from_delete_abspath,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Scan upwards for additional information about a deleted node.
When a deleted node is discovered in the WORKING tree, the situation
may be quite complex. This function will provide the information to
resolve the circumstances of the deletion.
For discussion purposes, we will start with the most complex example
and then demonstrate simplified examples. Consider node B/W/D/N has been
found as deleted. B is an unmodified directory (thus, only in BASE). W is
"replacement" content that exists in WORKING, shadowing a similar B/W
directory in BASE. D is a deleted subtree in the WORKING tree, and N is
the deleted node.
In this example, BASE_DEL_ABSPATH will bet set to B/W. That is the root of
the BASE tree (implicitly) deleted by the replacement. WORK_DEL_ABSPATH
will be set to the subtree deleted within the replacement; in this case,
B/W/D. No move-away took place, so MOVED_TO_ABSPATH is set to NULL.
In another scenario, B/W was moved-away before W was put into the WORKING
tree through an add/copy/move-here. MOVED_TO_ABSPATH will indicate where
B/W was moved to. Note that further operations may have been performed
post-move, but that is not known or reported by this function.
If BASE does not have a B/W, then the WORKING B/W is not a replacement,
but a simple add/copy/move-here. BASE_DEL_ABSPATH will be set to NULL.
If B/W/D does not exist in the WORKING tree (we're only talking about a
deletion of nodes of the BASE tree), then deleting B/W/D would have marked
the subtree for deletion. BASE_DEL_ABSPATH will refer to B/W/D,
MOVED_TO_ABSPATH will be NULL, and WORK_DEL_ABSPATH will be NULL.
If the BASE node B/W/D was moved instead of deleted, then MOVED_TO_ABSPATH
would indicate the target location (and other OUT values as above).
When the user deletes B/W/D from the WORKING tree, there are a few
additional considerations. If B/W is a simple addition (not a copy or
a move-here), then the deletion will simply remove the nodes from WORKING
and possibly leave behind "base-delete" markers in the WORKING tree.
If the source is a copy/moved-here, then the nodes are replaced with
deletion markers.
If the user moves-away B/W/D from the WORKING tree, then behavior is
again dependent upon the origination of B/W. For a plain add, the nodes
simply move to the destination; this means that B/W/D ceases to be a
node and so cannot be scanned. For a copy, a deletion is made at B/W/D,
and a new copy (of a subtree of the original source) is made at the
destination. For a move-here, a deletion is made, and a copy is made at
the destination (we do not track multiple moves; the source is moved to
B/W, then B/W/D is deleted; then a copy is made at the destination;
however, note the double-move could have been performed by moving the
subtree first, then moving the source to B/W).
There are three further considerations when resolving a deleted node:
If the BASE B/W/D was deleted explicitly *and* B/W is a replacement,
then the explicit deletion is subsumed by the implicit deletion that
occurred with the B/W replacement. Thus, BASE_DEL_ABSPATH will point
to B/W as the root of the BASE deletion. IOW, we can detect the
explicit move-away, but not an explicit deletion.
If B/W/D/N refers to a node present in the BASE tree, and B/W was
replaced by a shallow subtree, then it is possible for N to be
reported as deleted (from BASE) yet no deletions occurred in the
WORKING tree above N. Thus, WORK_DEL_ABSPATH will be set to NULL.
Summary of OUT parameters:
BASE_DEL_ABSPATH will specify the nearest ancestor of the explicit or
implicit deletion (if any) that applies to the BASE tree.
WORK_DEL_ABSPATH will specify the root of a deleted subtree within
the WORKING tree (note there is no concept of layered delete operations
in WORKING, so there is only one deletion root in the ancestry).
MOVED_TO_ABSPATH will specify the path where this node was moved to
if the node has moved-away.
If the node was moved-away, MOVED_TO_OP_ROOT_ABSPATH will specify the
target path of the root of the move operation. If LOCAL_ABSPATH itself
is the source path of the root of the move operation, then
MOVED_TO_OP_ROOT_ABSPATH equals MOVED_TO_ABSPATH.
All OUT parameters may be set to NULL to indicate a lack of interest in
that piece of information.
If the node given by LOCAL_ABSPATH does not exist, then
SVN_ERR_WC_PATH_NOT_FOUND is returned. If it doesn't have a "deleted"
status, then SVN_ERR_WC_PATH_UNEXPECTED_STATUS will be returned.
All returned data will be allocated in RESULT_POOL. All temporary
allocations will be made in SCRATCH_POOL.
*/
svn_error_t *
svn_wc__db_scan_deletion(const char **base_del_abspath,
const char **moved_to_abspath,
const char **work_del_abspath,
const char **moved_to_op_root_abspath,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* @} */
/* @defgroup svn_wc__db_upgrade Functions for upgrading a working copy.
@{
*/
/* Installs or updates Sqlite schema statistics for the current (aka latest)
working copy schema.
This function should be called once on initializing the database and after
an schema update completes */
svn_error_t *
svn_wc__db_install_schema_statistics(svn_sqlite__db_t *sdb,
apr_pool_t *scratch_pool);
/* Create a new wc.db file for LOCAL_DIR_ABSPATH, which is going to be a
working copy for the repository REPOS_ROOT_URL with uuid REPOS_UUID.
Return the raw sqlite handle, repository id and working copy id
and store the database in WC_DB.
Perform temporary allocations in SCRATCH_POOL. */
svn_error_t *
svn_wc__db_upgrade_begin(svn_sqlite__db_t **sdb,
apr_int64_t *repos_id,
apr_int64_t *wc_id,
svn_wc__db_t *wc_db,
const char *local_dir_abspath,
const char *repos_root_url,
const char *repos_uuid,
apr_pool_t *scratch_pool);
svn_error_t *
svn_wc__db_upgrade_apply_dav_cache(svn_sqlite__db_t *sdb,
const char *dir_relpath,
apr_hash_t *cache_values,
apr_pool_t *scratch_pool);
/* ### need much more docco
### this function should be called within a sqlite transaction. it makes
### assumptions around this fact.
Apply the various sets of properties to the database nodes based on
their existence/presence, the current state of the node, and the original
format of the working copy which provided these property sets.
*/
svn_error_t *
svn_wc__db_upgrade_apply_props(svn_sqlite__db_t *sdb,
const char *dir_abspath,
const char *local_relpath,
apr_hash_t *base_props,
apr_hash_t *revert_props,
apr_hash_t *working_props,
int original_format,
apr_int64_t wc_id,
apr_pool_t *scratch_pool);
/* Simply insert (or replace) one row in the EXTERNALS table. */
svn_error_t *
svn_wc__db_upgrade_insert_external(svn_wc__db_t *db,
const char *local_abspath,
svn_node_kind_t kind,
const char *parent_abspath,
const char *def_local_abspath,
const char *repos_relpath,
const char *repos_root_url,
const char *repos_uuid,
svn_revnum_t def_peg_revision,
svn_revnum_t def_revision,
apr_pool_t *scratch_pool);
/* Get the repository identifier corresponding to REPOS_ROOT_URL from the
database in SDB. The value is returned in *REPOS_ID. All allocations
are allocated in SCRATCH_POOL.
NOTE: the row in REPOSITORY must exist. If not, then SVN_ERR_WC_DB_ERROR
is returned.
### unclear on whether/how this interface will stay/evolve. */
svn_error_t *
svn_wc__db_upgrade_get_repos_id(apr_int64_t *repos_id,
svn_sqlite__db_t *sdb,
const char *repos_root_url,
apr_pool_t *scratch_pool);
/* Upgrade the metadata concerning the WC at WCROOT_ABSPATH, in DB,
* to the SVN_WC__VERSION format.
*
* This function is used for upgrading wc-ng working copies to a newer
* wc-ng format. If a pre-1.7 working copy is found, this function
* returns SVN_ERR_WC_UPGRADE_REQUIRED.
*
* Upgrading subdirectories of a working copy is not supported.
* If WCROOT_ABSPATH is not a working copy root SVN_ERR_WC_INVALID_OP_ON_CWD
* is returned.
*
* If BUMPED_FORMAT is not NULL, set *BUMPED_FORMAT to TRUE if the format
* was bumped or to FALSE if the wc was already at the resulting format.
*/
svn_error_t *
svn_wc__db_bump_format(int *result_format,
svn_boolean_t *bumped_format,
svn_wc__db_t *db,
const char *wcroot_abspath,
apr_pool_t *scratch_pool);
/* @} */
/* @defgroup svn_wc__db_wq Work queue manipulation. see workqueue.h
@{
*/
/* In the WCROOT associated with DB and WRI_ABSPATH, add WORK_ITEM to the
wcroot's work queue. Use SCRATCH_POOL for all temporary allocations. */
svn_error_t *
svn_wc__db_wq_add(svn_wc__db_t *db,
const char *wri_abspath,
const svn_skel_t *work_item,
apr_pool_t *scratch_pool);
/* In the WCROOT associated with DB and WRI_ABSPATH, fetch a work item that
needs to be completed. Its identifier is returned in ID, and the data in
WORK_ITEM.
Items are returned in the same order they were queued. This allows for
(say) queueing work on a parent node to be handled before that of its
children.
If there are no work items to be completed, then ID will be set to zero,
and WORK_ITEM to NULL.
If COMPLETED_ID is not 0, the wq item COMPLETED_ID will be marked as
completed before returning the next item.
RESULT_POOL will be used to allocate WORK_ITEM, and SCRATCH_POOL
will be used for all temporary allocations. */
svn_error_t *
svn_wc__db_wq_fetch_next(apr_uint64_t *id,
svn_skel_t **work_item,
svn_wc__db_t *db,
const char *wri_abspath,
apr_uint64_t completed_id,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Special variant of svn_wc__db_wq_fetch_next(), which in the same transaction
also records timestamps and sizes for one or more nodes */
svn_error_t *
svn_wc__db_wq_record_and_fetch_next(apr_uint64_t *id,
svn_skel_t **work_item,
svn_wc__db_t *db,
const char *wri_abspath,
apr_uint64_t completed_id,
apr_hash_t *record_map,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* @} */
/* Note: LEVELS_TO_LOCK is here strictly for backward compat. The access
batons still have the notion of 'levels to lock' and we need to ensure
that they still function correctly, even in the new world. 'levels to
lock' should not be exposed through the wc-ng APIs at all: users either
get to lock the entire tree (rooted at some subdir, of course), or none.
An infinite depth lock is obtained with LEVELS_TO_LOCK set to -1, but until
we move to a single DB only depth 0 is supported.
*/
svn_error_t *
svn_wc__db_wclock_obtain(svn_wc__db_t *db,
const char *local_abspath,
int levels_to_lock,
svn_boolean_t steal_lock,
apr_pool_t *scratch_pool);
/* Set LOCK_ABSPATH to the path of the the directory that owns the
lock on LOCAL_ABSPATH, or NULL, if LOCAL_ABSPATH is not locked. */
svn_error_t*
svn_wc__db_wclock_find_root(const char **lock_abspath,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Check if somebody has a wclock on LOCAL_ABSPATH */
svn_error_t *
svn_wc__db_wclocked(svn_boolean_t *locked,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool);
/* Release the previously obtained lock on LOCAL_ABSPATH */
svn_error_t *
svn_wc__db_wclock_release(svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool);
/* Checks whether DB currently owns a lock to operate on LOCAL_ABSPATH.
If EXACT is TRUE only lock roots are checked. */
svn_error_t *
svn_wc__db_wclock_owns_lock(svn_boolean_t *own_lock,
svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t exact,
apr_pool_t *scratch_pool);
/* @defgroup svn_wc__db_temp Various temporary functions during transition
### These functions SHOULD be completely removed before 1.7
@{
*/
/* Removes all references to LOCAL_ABSPATH from DB, while optionally leaving
a not present node.
This operation always recursively removes all nodes at and below
LOCAL_ABSPATH from NODES and ACTUAL.
If NOT_PRESENT_REVISION specifies a valid revision, leave a not_present
BASE node at local_abspath of the specified status and kind.
(Requires an existing BASE node before removing)
If DESTROY_WC is TRUE, this operation *installs* workqueue operations to
update the local filesystem after the database operation. If DESTROY_CHANGES
is FALSE, modified and unversioned files are left after running this
operation (and the WQ). If DESTROY_CHANGES and DESTROY_WC are TRUE,
LOCAL_ABSPATH and everything below it will be removed by the WQ.
Note: Unlike many similar functions it is a valid scenario for this
function to be called on a wcroot! In this case it will just leave the root
record in BASE
*/
svn_error_t *
svn_wc__db_op_remove_node(svn_boolean_t *left_changes,
svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t destroy_wc,
svn_boolean_t destroy_changes,
svn_revnum_t not_present_revision,
svn_wc__db_status_t not_present_status,
svn_node_kind_t not_present_kind,
const svn_skel_t *conflict,
const svn_skel_t *work_items,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool);
/* Sets the depth of LOCAL_ABSPATH in its working copy to DEPTH using DB.
Returns SVN_ERR_WC_PATH_NOT_FOUND if LOCAL_ABSPATH is not a BASE directory
*/
svn_error_t *
svn_wc__db_op_set_base_depth(svn_wc__db_t *db,
const char *local_abspath,
svn_depth_t depth,
apr_pool_t *scratch_pool);
/* ### temp function. return the FORMAT for the directory LOCAL_ABSPATH. */
svn_error_t *
svn_wc__db_temp_get_format(int *format,
svn_wc__db_t *db,
const char *local_dir_abspath,
apr_pool_t *scratch_pool);
/* ### temp functions to manage/store access batons within the DB. */
svn_wc_adm_access_t *
svn_wc__db_temp_get_access(svn_wc__db_t *db,
const char *local_dir_abspath,
apr_pool_t *scratch_pool);
void
svn_wc__db_temp_set_access(svn_wc__db_t *db,
const char *local_dir_abspath,
svn_wc_adm_access_t *adm_access,
apr_pool_t *scratch_pool);
svn_error_t *
svn_wc__db_temp_close_access(svn_wc__db_t *db,
const char *local_dir_abspath,
svn_wc_adm_access_t *adm_access,
apr_pool_t *scratch_pool);
void
svn_wc__db_temp_clear_access(svn_wc__db_t *db,
const char *local_dir_abspath,
apr_pool_t *scratch_pool);
/* ### shallow hash: abspath -> svn_wc_adm_access_t * */
apr_hash_t *
svn_wc__db_temp_get_all_access(svn_wc__db_t *db,
apr_pool_t *result_pool);
/* ### temp function to open the sqlite database to the appropriate location,
### then borrow it for a bit.
### The *only* reason for this function is because entries.c still
### manually hacks the sqlite database.
### No matter how tempted you may be DO NOT USE THIS FUNCTION!
### (if you do, gstein will hunt you down and burn your knee caps off
### in the middle of the night)
### "Bet on it." --gstein
*/
svn_error_t *
svn_wc__db_temp_borrow_sdb(svn_sqlite__db_t **sdb,
svn_wc__db_t *db,
const char *local_dir_abspath,
apr_pool_t *scratch_pool);
/* Return a directory in *TEMP_DIR_ABSPATH that is suitable for temporary
files which may need to be moved (atomically and same-device) into the
working copy indicated by WRI_ABSPATH. */
svn_error_t *
svn_wc__db_temp_wcroot_tempdir(const char **temp_dir_abspath,
svn_wc__db_t *db,
const char *wri_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Update the BASE_NODE of directory LOCAL_ABSPATH to be NEW_REPOS_RELPATH
at revision NEW_REV with status incomplete. */
svn_error_t *
svn_wc__db_temp_op_start_directory_update(svn_wc__db_t *db,
const char *local_abspath,
const char *new_repos_relpath,
svn_revnum_t new_rev,
apr_pool_t *scratch_pool);
/* Marks a directory update started with
svn_wc__db_temp_op_start_directory_update as completed, by removing
the incomplete status */
svn_error_t *
svn_wc__db_temp_op_end_directory_update(svn_wc__db_t *db,
const char *local_dir_abspath,
apr_pool_t *scratch_pool);
/* Copy the base tree at LOCAL_ABSPATH into the working tree as copy,
leaving any subtree additions and copies as-is. This allows the
base node tree to be removed. */
svn_error_t *
svn_wc__db_op_make_copy(svn_wc__db_t *db,
const char *local_abspath,
const svn_skel_t *conflicts,
const svn_skel_t *work_items,
apr_pool_t *scratch_pool);
/* Close the wc root LOCAL_ABSPATH and remove any per-directory
handles associated with it. */
svn_error_t *
svn_wc__db_drop_root(svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool);
/* Return the OP_DEPTH for LOCAL_RELPATH. */
int
svn_wc__db_op_depth_for_upgrade(const char *local_relpath);
/* Set *HAVE_WORK TRUE if there is a working layer below the top layer and
*HAVE_BASE if there is a base layer. Set *STATUS to the status of the
highest layer below WORKING */
svn_error_t *
svn_wc__db_info_below_working(svn_boolean_t *have_base,
svn_boolean_t *have_work,
svn_wc__db_status_t *status,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool);
/* Gets an array of const char *local_relpaths of descendants of LOCAL_ABSPATH,
* which itself must be the op root of an addition, copy or move.
* The descendants returned are at the same op_depth, but are to be deleted
* by the commit processing because they are not present in the local copy.
*/
svn_error_t *
svn_wc__db_get_not_present_descendants(const apr_array_header_t **descendants,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Gather revision status information about a working copy using DB.
*
* Set *MIN_REVISION and *MAX_REVISION to the lowest and highest revision
* numbers found within LOCAL_ABSPATH.
* Only nodes with op_depth zero and presence 'normal' or 'incomplete'
* are considered, so that added, deleted or excluded nodes do not affect
* the result. If COMMITTED is TRUE, set *MIN_REVISION and *MAX_REVISION
* to the lowest and highest committed (i.e. "last changed") revision numbers,
* respectively.
*
* Indicate in *IS_SPARSE_CHECKOUT whether any of the nodes within
* LOCAL_ABSPATH is sparse.
* Indicate in *IS_MODIFIED whether the working copy has local modifications.
*
* Indicate in *IS_SWITCHED whether any node beneath LOCAL_ABSPATH
* is switched. If TRAIL_URL is non-NULL, use it to determine if LOCAL_ABSPATH
* itself is switched. It should be any trailing portion of LOCAL_ABSPATH's
* expected URL, long enough to include any parts that the caller considers
* might be changed by a switch. If it does not match the end of WC_PATH's
* actual URL, then report a "switched" status.
*
* See also the functions below which provide a subset of this functionality.
*/
svn_error_t *
svn_wc__db_revision_status(svn_revnum_t *min_revision,
svn_revnum_t *max_revision,
svn_boolean_t *is_sparse_checkout,
svn_boolean_t *is_modified,
svn_boolean_t *is_switched,
svn_wc__db_t *db,
const char *local_abspath,
const char *trail_url,
svn_boolean_t committed,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool);
/* Set *MIN_REVISION and *MAX_REVISION to the lowest and highest revision
* numbers found within LOCAL_ABSPATH in the working copy using DB.
* Only nodes with op_depth zero and presence 'normal' or 'incomplete'
* are considered, so that added, deleted or excluded nodes do not affect
* the result. If COMMITTED is TRUE, set *MIN_REVISION and *MAX_REVISION
* to the lowest and highest committed (i.e. "last changed") revision numbers,
* respectively. Use SCRATCH_POOL for temporary allocations.
*
* Either of MIN_REVISION and MAX_REVISION may be passed as NULL if
* the caller doesn't care about that return value.
*
* This function provides a subset of the functionality of
* svn_wc__db_revision_status() and is more efficient if the caller
* doesn't need all information returned by svn_wc__db_revision_status(). */
svn_error_t *
svn_wc__db_min_max_revisions(svn_revnum_t *min_revision,
svn_revnum_t *max_revision,
svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t committed,
apr_pool_t *scratch_pool);
/* Indicate in *IS_SWITCHED whether any node beneath LOCAL_ABSPATH
* is switched, using DB. Use SCRATCH_POOL for temporary allocations.
*
* If TRAIL_URL is non-NULL, use it to determine if LOCAL_ABSPATH itself
* is switched. It should be any trailing portion of LOCAL_ABSPATH's
* expected URL, long enough to include any parts that the caller considers
* might be changed by a switch. If it does not match the end of WC_PATH's
* actual URL, then report a "switched" status.
*
* This function provides a subset of the functionality of
* svn_wc__db_revision_status() and is more efficient if the caller
* doesn't need all information returned by svn_wc__db_revision_status(). */
svn_error_t *
svn_wc__db_has_switched_subtrees(svn_boolean_t *is_switched,
svn_wc__db_t *db,
const char *local_abspath,
const char *trail_url,
apr_pool_t *scratch_pool);
/* Set @a *excluded_subtrees to a hash mapping <tt>const char *</tt>
* local absolute paths to <tt>const char *</tt> local absolute paths for
* every path under @a local_abspath in @a db which are excluded by
* the server (e.g. due to authz), or user. If no such paths are found then
* @a *server_excluded_subtrees is set to @c NULL.
* Allocate the hash and all items therein from @a result_pool.
*/
svn_error_t *
svn_wc__db_get_excluded_subtrees(apr_hash_t **server_excluded_subtrees,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Indicate in *IS_MODIFIED whether the working copy has local modifications,
* using DB. Use SCRATCH_POOL for temporary allocations.
*
* This function provides a subset of the functionality of
* svn_wc__db_revision_status() and is more efficient if the caller
* doesn't need all information returned by svn_wc__db_revision_status(). */
svn_error_t *
svn_wc__db_has_local_mods(svn_boolean_t *is_modified,
svn_wc__db_t *db,
const char *local_abspath,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool);
/* Verify the consistency of metadata concerning the WC that contains
* WRI_ABSPATH, in DB. Return an error if any problem is found. */
svn_error_t *
svn_wc__db_verify(svn_wc__db_t *db,
const char *wri_abspath,
apr_pool_t *scratch_pool);
/* Possibly need two structures, one with relpaths and with abspaths?
* Only exposed for testing at present. */
struct svn_wc__db_moved_to_t {
const char *local_relpath; /* moved-to destination */
int op_depth; /* op-root of source */
};
/* Set *FINAL_ABSPATH to an array of svn_wc__db_moved_to_t for
* LOCAL_ABSPATH after following any and all nested moves.
* Only exposed for testing at present. */
svn_error_t *
svn_wc__db_follow_moved_to(apr_array_header_t **moved_tos,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Update a moved-away tree conflict victim at VICTIM_ABSPATH with changes
* brought in by the update operation which flagged the tree conflict. */
svn_error_t *
svn_wc__db_update_moved_away_conflict_victim(svn_wc__db_t *db,
const char *victim_abspath,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool);
/* LOCAL_ABSPATH is moved to MOVE_DST_ABSPATH. MOVE_SRC_ROOT_ABSPATH
* is the root of the move to MOVE_DST_OP_ROOT_ABSPATH.
* MOVE_SRC_OP_ROOT_ABSPATH is the op-root of the move; it's the same
* as MOVE_SRC_ROOT_ABSPATH except for moves inside deletes when it is
* the op-root of the delete. */
svn_error_t *
svn_wc__db_base_moved_to(const char **move_dst_abspath,
const char **move_dst_op_root_abspath,
const char **move_src_root_abspath,
const char **move_src_op_root_abspath,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Recover space from the database file for LOCAL_ABSPATH by running
* the "vacuum" command. */
svn_error_t *
svn_wc__db_vacuum(svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool);
/* This raises move-edit tree-conflicts on any moves inside the
delete-edit conflict on LOCAL_ABSPATH. This is experimental: see
comment in resolve_conflict_on_node about combining with another
function. */
svn_error_t *
svn_wc__db_resolve_delete_raise_moved_away(svn_wc__db_t *db,
const char *local_abspath,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool);
/* Like svn_wc__db_resolve_delete_raise_moved_away this should be
combined.
### LOCAL_ABSPATH specifies the move origin, but the move origin
### is not necessary unique enough. This function needs an op_root_abspath
### argument to differentiate between different origins.
### See move_tests.py: move_many_update_delete for an example case.
*/
svn_error_t *
svn_wc__db_resolve_break_moved_away(svn_wc__db_t *db,
const char *local_abspath,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool);
/* Break moves for all moved-away children of LOCAL_ABSPATH, within
* a single transaction.
*
* ### Like svn_wc__db_resolve_delete_raise_moved_away this should be
* combined. */
svn_error_t *
svn_wc__db_resolve_break_moved_away_children(svn_wc__db_t *db,
const char *local_abspath,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool);
/* Set *REQUIRED_ABSPATH to the path that should be locked to ensure
* that the lock covers all paths affected by resolving the conflicts
* in the tree LOCAL_ABSPATH. */
svn_error_t *
svn_wc__required_lock_for_resolve(const char **required_abspath,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* @} */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* SVN_WC_DB_H */
Index: vendor/subversion/dist/subversion/libsvn_wc/wc_db_private.h
===================================================================
--- vendor/subversion/dist/subversion/libsvn_wc/wc_db_private.h (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_wc/wc_db_private.h (revision 286501)
@@ -1,461 +1,473 @@
/**
* @copyright
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
* @endcopyright
*/
/* This file is not for general consumption; it should only be used by
wc_db.c. */
#ifndef SVN_WC__I_AM_WC_DB
#error "You should not be using these data structures directly"
#endif /* SVN_WC__I_AM_WC_DB */
#ifndef WC_DB_PRIVATE_H
#define WC_DB_PRIVATE_H
#include "wc_db.h"
struct svn_wc__db_t {
/* We need the config whenever we run into a new WC directory, in order
to figure out where we should look for the corresponding datastore. */
svn_config_t *config;
/* Should we fail with SVN_ERR_WC_UPGRADE_REQUIRED when it is
opened, and found to be not-current? */
svn_boolean_t verify_format;
/* Should we ensure the WORK_QUEUE is empty when a WCROOT is opened? */
svn_boolean_t enforce_empty_wq;
/* Should we open Sqlite databases EXCLUSIVE */
svn_boolean_t exclusive;
/* Map a given working copy directory to its relevant data.
const char *local_abspath -> svn_wc__db_wcroot_t *wcroot */
apr_hash_t *dir_data;
/* A few members to assist with caching of kind values for paths. See
get_path_kind() for use. */
struct
{
svn_stringbuf_t *abspath;
svn_node_kind_t kind;
} parse_cache;
/* As we grow the state of this DB, allocate that state here. */
apr_pool_t *state_pool;
};
/* Hold information about an owned lock */
typedef struct svn_wc__db_wclock_t
{
/* Relative path of the lock root */
const char *local_relpath;
/* Number of levels locked (0 for infinity) */
int levels;
} svn_wc__db_wclock_t;
/** Hold information about a WCROOT.
*
* This structure is referenced by all per-directory handles underneath it.
*/
typedef struct svn_wc__db_wcroot_t {
/* Location of this wcroot in the filesystem. */
const char *abspath;
/* The SQLite database containing the metadata for everything in
this wcroot. */
svn_sqlite__db_t *sdb;
/* The WCROOT.id for this directory (and all its children). */
apr_int64_t wc_id;
/* The format of this wcroot's metadata storage (see wc.h). If the
format has not (yet) been determined, this will be UNKNOWN_FORMAT. */
int format;
/* Array of svn_wc__db_wclock_t structures (not pointers!).
Typically just one or two locks maximum. */
apr_array_header_t *owned_locks;
/* Map a working copy directory to a cached adm_access baton.
const char *local_abspath -> svn_wc_adm_access_t *adm_access */
apr_hash_t *access_cache;
} svn_wc__db_wcroot_t;
/* */
svn_error_t *
svn_wc__db_close_many_wcroots(apr_hash_t *roots,
apr_pool_t *state_pool,
apr_pool_t *scratch_pool);
/* Construct a new svn_wc__db_wcroot_t. The WCROOT_ABSPATH and SDB parameters
must have lifetime of at least RESULT_POOL. */
svn_error_t *
svn_wc__db_pdh_create_wcroot(svn_wc__db_wcroot_t **wcroot,
const char *wcroot_abspath,
svn_sqlite__db_t *sdb,
apr_int64_t wc_id,
int format,
svn_boolean_t verify_format,
svn_boolean_t enforce_empty_wq,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* For a given LOCAL_ABSPATH, figure out what sqlite database (WCROOT) to
use and the RELPATH within that wcroot.
*LOCAL_RELPATH will be allocated within RESULT_POOL. Temporary allocations
will be made in SCRATCH_POOL.
*WCROOT will be allocated within DB->STATE_POOL.
Certain internal structures will be allocated in DB->STATE_POOL.
*/
svn_error_t *
svn_wc__db_wcroot_parse_local_abspath(svn_wc__db_wcroot_t **wcroot,
const char **local_relpath,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Assert that the given WCROOT is usable.
NOTE: the expression is multiply-evaluated!! */
#define VERIFY_USABLE_WCROOT(wcroot) SVN_ERR_ASSERT( \
(wcroot) != NULL && (wcroot)->format == SVN_WC__VERSION)
/* Check if the WCROOT is usable for light db operations such as path
calculations */
#define CHECK_MINIMAL_WCROOT(wcroot, abspath, scratch_pool) \
do \
{ \
if (wcroot == NULL) \
return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL, \
_("The node '%s' is not in a working copy."), \
svn_dirent_local_style(wri_abspath, \
scratch_pool)); \
} \
while (0)
/* Calculates the depth of the relpath below "" */
APR_INLINE static int
relpath_depth(const char *relpath)
{
int n = 1;
if (*relpath == '\0')
return 0;
do
{
if (*relpath == '/')
n++;
}
while (*(++relpath));
return n;
}
/* */
svn_error_t *
svn_wc__db_util_fetch_wc_id(apr_int64_t *wc_id,
svn_sqlite__db_t *sdb,
apr_pool_t *scratch_pool);
/* Open a connection in *SDB to the WC database found in the WC metadata
* directory inside DIR_ABSPATH, having the filename SDB_FNAME.
*
* SMODE is passed to svn_sqlite__open().
*
* Register MY_STATEMENTS, or if that is null, the default set of WC DB
* statements, as the set of statements to be prepared now and executed
* later. MY_STATEMENTS (the strings and the array itself) is not duplicated
* internally, and should have a lifetime at least as long as RESULT_POOL.
* See svn_sqlite__open() for details. */
svn_error_t *
svn_wc__db_util_open_db(svn_sqlite__db_t **sdb,
const char *dir_abspath,
const char *sdb_fname,
svn_sqlite__mode_t smode,
svn_boolean_t exclusive,
const char *const *my_statements,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Like svn_wc__db_read_info(), but taking WCROOT+LOCAL_RELPATH instead of
DB+LOCAL_ABSPATH, and outputting repos ids instead of URL+UUID. */
svn_error_t *
svn_wc__db_read_info_internal(svn_wc__db_status_t *status,
svn_node_kind_t *kind,
svn_revnum_t *revision,
const char **repos_relpath,
apr_int64_t *repos_id,
svn_revnum_t *changed_rev,
apr_time_t *changed_date,
const char **changed_author,
svn_depth_t *depth,
const svn_checksum_t **checksum,
const char **target,
const char **original_repos_relpath,
apr_int64_t *original_repos_id,
svn_revnum_t *original_revision,
svn_wc__db_lock_t **lock,
svn_filesize_t *recorded_size,
apr_time_t *recorded_mod_time,
const char **changelist,
svn_boolean_t *conflicted,
svn_boolean_t *op_root,
svn_boolean_t *had_props,
svn_boolean_t *props_mod,
svn_boolean_t *have_base,
svn_boolean_t *have_more_work,
svn_boolean_t *have_work,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Like svn_wc__db_base_get_info(), but taking WCROOT+LOCAL_RELPATH instead of
DB+LOCAL_ABSPATH and outputting REPOS_ID instead of URL+UUID. */
svn_error_t *
svn_wc__db_base_get_info_internal(svn_wc__db_status_t *status,
svn_node_kind_t *kind,
svn_revnum_t *revision,
const char **repos_relpath,
apr_int64_t *repos_id,
svn_revnum_t *changed_rev,
apr_time_t *changed_date,
const char **changed_author,
svn_depth_t *depth,
const svn_checksum_t **checksum,
const char **target,
svn_wc__db_lock_t **lock,
svn_boolean_t *had_props,
apr_hash_t **props,
svn_boolean_t *update_root,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Similar to svn_wc__db_base_get_info(), but taking WCROOT+LOCAL_RELPATH
* instead of DB+LOCAL_ABSPATH, an explicit op-depth of the node to get
* information about, and outputting REPOS_ID instead of URL+UUID, and
* without the LOCK or UPDATE_ROOT outputs.
*
* OR
*
* Similar to svn_wc__db_base_get_info_internal(), but taking an explicit
* op-depth OP_DEPTH of the node to get information about, and without the
* LOCK or UPDATE_ROOT outputs.
*
* ### [JAF] TODO: Harmonize svn_wc__db_base_get_info[_internal] with
* svn_wc__db_depth_get_info -- common API, common implementation.
*/
svn_error_t *
svn_wc__db_depth_get_info(svn_wc__db_status_t *status,
svn_node_kind_t *kind,
svn_revnum_t *revision,
const char **repos_relpath,
apr_int64_t *repos_id,
svn_revnum_t *changed_rev,
apr_time_t *changed_date,
const char **changed_author,
svn_depth_t *depth,
const svn_checksum_t **checksum,
const char **target,
svn_boolean_t *had_props,
apr_hash_t **props,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
int op_depth,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Look up REPOS_ID in SDB and set *REPOS_ROOT_URL and/or *REPOS_UUID to
its root URL and UUID respectively. If REPOS_ID is INVALID_REPOS_ID,
use NULL for both URL and UUID. Either or both output parameters may be
NULL if not wanted. */
svn_error_t *
svn_wc__db_fetch_repos_info(const char **repos_root_url,
const char **repos_uuid,
svn_sqlite__db_t *sdb,
apr_int64_t repos_id,
apr_pool_t *result_pool);
/* Like svn_wc__db_read_conflict(), but with WCROOT+LOCAL_RELPATH instead of
DB+LOCAL_ABSPATH, and outputting relpaths instead of abspaths. */
svn_error_t *
svn_wc__db_read_conflict_internal(svn_skel_t **conflict,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Like svn_wc__db_op_mark_conflict(), but with WCROOT+LOCAL_RELPATH instead of
DB+LOCAL_ABSPATH. */
svn_error_t *
svn_wc__db_mark_conflict_internal(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
const svn_skel_t *conflict_skel,
apr_pool_t *scratch_pool);
/* Transaction handling */
/* A callback which supplies WCROOTs and LOCAL_RELPATHs. */
typedef svn_error_t *(*svn_wc__db_txn_callback_t)(void *baton,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *scratch_pool);
/* Run CB_FUNC in a SQLite transaction with CB_BATON, using WCROOT and
LOCAL_RELPATH. If callbacks require additional information, they may
provide it using CB_BATON. */
svn_error_t *
svn_wc__db_with_txn(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
svn_wc__db_txn_callback_t cb_func,
void *cb_baton,
apr_pool_t *scratch_pool);
/* Evaluate the expression EXPR within a transaction.
*
* Begin a transaction in WCROOT's DB; evaluate the expression EXPR, which would
* typically be a function call that does some work in DB; finally commit
* the transaction if EXPR evaluated to SVN_NO_ERROR, otherwise roll back
* the transaction.
*/
#define SVN_WC__DB_WITH_TXN(expr, wcroot) \
SVN_SQLITE__WITH_LOCK(expr, (wcroot)->sdb)
+/* Evaluate the expressions EXPR1..EXPR4 within a transaction, returning the
+ * first error if an error occurs.
+ *
+ * Begin a transaction in WCROOT's DB; evaluate the expressions, which would
+ * typically be function calls that do some work in DB; finally commit
+ * the transaction if EXPR evaluated to SVN_NO_ERROR, otherwise roll back
+ * the transaction.
+ */
+#define SVN_WC__DB_WITH_TXN4(expr1, expr2, expr3, expr4, wcroot) \
+ SVN_SQLITE__WITH_LOCK4(expr1, expr2, expr3, expr4, (wcroot)->sdb)
+
+
/* Return CHILDREN mapping const char * names to svn_node_kind_t * for the
children of LOCAL_RELPATH at OP_DEPTH. */
svn_error_t *
svn_wc__db_get_children_op_depth(apr_hash_t **children,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
int op_depth,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Extend any delete of the parent of LOCAL_RELPATH to LOCAL_RELPATH.
### What about KIND and OP_DEPTH? KIND ought to be redundant; I'm
discussing on dev@ whether we can let that be null for presence
== base-deleted. OP_DEPTH is the op-depth of what, and why?
It is used to select the lowest working node higher than OP_DEPTH,
so, in terms of the API, OP_DEPTH means ...?
Given a wc:
0 1 2 3 4
normal
A normal
A/B normal normal
A/B/C not-pres normal
A/B/C/D normal
That is checkout, delete A/B, copy a replacement A/B, delete copied
child A/B/C, add replacement A/B/C, add A/B/C/D.
Now an update that adds base nodes for A/B/C, A/B/C/D and A/B/C/D/E
must extend the A/B deletion:
0 1 2 3 4
normal
A normal
A/B normal normal
A/B/C normal not-pres normal
A/B/C/D normal base-del normal
A/B/C/D/E normal base-del
When adding a node if the parent has a higher working node then the
parent node is deleted (or replaced) and the delete must be extended
to cover new node.
In the example above A/B/C/D and A/B/C/D/E are the nodes that get
the extended delete, A/B/C is already deleted.
*/
svn_error_t *
svn_wc__db_extend_parent_delete(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
svn_node_kind_t kind,
int op_depth,
apr_pool_t *scratch_pool);
svn_error_t *
svn_wc__db_retract_parent_delete(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
int op_depth,
apr_pool_t *scratch_pool);
svn_error_t *
svn_wc__db_op_depth_moved_to(const char **move_dst_relpath,
const char **move_dst_op_root_relpath,
const char **move_src_root_relpath,
const char **move_src_op_root_relpath,
int op_depth,
svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/* Do a post-drive revision bump for the moved-away destination for
any move sources under LOCAL_RELPATH. This is called from within
the revision bump transaction after the tree at LOCAL_RELPATH has
been bumped. */
svn_error_t *
svn_wc__db_bump_moved_away(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
svn_depth_t depth,
svn_wc__db_t *db,
apr_pool_t *scratch_pool);
/* Unbreak the move from LOCAL_RELPATH on op-depth in WCROOT, by making
the destination a normal copy */
svn_error_t *
svn_wc__db_resolve_break_moved_away_internal(svn_wc__db_wcroot_t *wcroot,
const char *local_relpath,
int op_depth,
apr_pool_t *scratch_pool);
svn_error_t *
svn_wc__db_update_move_list_notify(svn_wc__db_wcroot_t *wcroot,
svn_revnum_t old_revision,
svn_revnum_t new_revision,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool);
#endif /* WC_DB_PRIVATE_H */
Index: vendor/subversion/dist/subversion/libsvn_wc/wc_db_wcroot.c
===================================================================
--- vendor/subversion/dist/subversion/libsvn_wc/wc_db_wcroot.c (revision 286500)
+++ vendor/subversion/dist/subversion/libsvn_wc/wc_db_wcroot.c (revision 286501)
@@ -1,922 +1,973 @@
/*
* wc_db_wcroot.c : supporting datastructures for the administrative database
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#define SVN_WC__I_AM_WC_DB
#include <assert.h>
#include "svn_dirent_uri.h"
#include "svn_hash.h"
#include "svn_path.h"
#include "svn_version.h"
#include "wc.h"
#include "adm_files.h"
#include "wc_db_private.h"
#include "wc-queries.h"
#include "svn_private_config.h"
/* ### Same values as wc_db.c */
#define SDB_FILE "wc.db"
#define UNKNOWN_WC_ID ((apr_int64_t) -1)
#define FORMAT_FROM_SDB (-1)
/* Get the format version from a wc-1 directory. If it is not a working copy
directory, then it sets VERSION to zero and returns no error. */
static svn_error_t *
get_old_version(int *version,
const char *abspath,
apr_pool_t *scratch_pool)
{
svn_error_t *err;
const char *format_file_path;
svn_node_kind_t kind;
/* Try reading the format number from the entries file. */
format_file_path = svn_wc__adm_child(abspath, SVN_WC__ADM_ENTRIES,
scratch_pool);
/* Since trying to open a non-existent file is quite expensive, try a
quick stat call first. In wc-ng w/cs, this will be an early exit. */
SVN_ERR(svn_io_check_path(format_file_path, &kind, scratch_pool));
if (kind == svn_node_none)
{
*version = 0;
return SVN_NO_ERROR;
}
err = svn_io_read_version_file(version, format_file_path, scratch_pool);
if (err == NULL)
return SVN_NO_ERROR;
if (err->apr_err != SVN_ERR_BAD_VERSION_FILE_FORMAT
&& !APR_STATUS_IS_ENOENT(err->apr_err)
&& !APR_STATUS_IS_ENOTDIR(err->apr_err))
return svn_error_createf(SVN_ERR_WC_MISSING, err, _("'%s' does not exist"),
svn_dirent_local_style(abspath, scratch_pool));
svn_error_clear(err);
/* This must be a really old working copy! Fall back to reading the
format file.
Note that the format file might not exist in newer working copies
(format 7 and higher), but in that case, the entries file should
have contained the format number. */
format_file_path = svn_wc__adm_child(abspath, SVN_WC__ADM_FORMAT,
scratch_pool);
err = svn_io_read_version_file(version, format_file_path, scratch_pool);
if (err == NULL)
return SVN_NO_ERROR;
/* Whatever error may have occurred... we can just ignore. This is not
a working copy directory. Signal the caller. */
svn_error_clear(err);
*version = 0;
return SVN_NO_ERROR;
}
/* A helper function to parse_local_abspath() which returns the on-disk KIND
of LOCAL_ABSPATH, using DB and SCRATCH_POOL as needed.
This function may do strange things, but at long as it comes up with the
Right Answer, we should be happy. */
static svn_error_t *
get_path_kind(svn_node_kind_t *kind,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
svn_boolean_t special;
svn_node_kind_t node_kind;
/* This implements a *really* simple LRU cache, where "simple" is defined
as "only one element". In other words, we remember the most recently
queried path, and nothing else. This gives >80% cache hits. */
if (db->parse_cache.abspath
&& strcmp(db->parse_cache.abspath->data, local_abspath) == 0)
{
/* Cache hit! */
*kind = db->parse_cache.kind;
return SVN_NO_ERROR;
}
if (!db->parse_cache.abspath)
{
db->parse_cache.abspath = svn_stringbuf_create(local_abspath,
db->state_pool);
}
else
{
svn_stringbuf_set(db->parse_cache.abspath, local_abspath);
}
SVN_ERR(svn_io_check_special_path(local_abspath, &node_kind,
&special, scratch_pool));
db->parse_cache.kind = (special ? svn_node_symlink : node_kind);
*kind = db->parse_cache.kind;
return SVN_NO_ERROR;
}
/* Return an error if the work queue in SDB is non-empty. */
static svn_error_t *
verify_no_work(svn_sqlite__db_t *sdb)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_LOOK_FOR_WORK));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
SVN_ERR(svn_sqlite__reset(stmt));
if (have_row)
return svn_error_create(SVN_ERR_WC_CLEANUP_REQUIRED, NULL,
NULL /* nothing to add. */);
return SVN_NO_ERROR;
}
/* */
static apr_status_t
close_wcroot(void *data)
{
svn_wc__db_wcroot_t *wcroot = data;
svn_error_t *err;
SVN_ERR_ASSERT_NO_RETURN(wcroot->sdb != NULL);
err = svn_sqlite__close(wcroot->sdb);
wcroot->sdb = NULL;
if (err)
{
apr_status_t result = err->apr_err;
svn_error_clear(err);
return result;
}
return APR_SUCCESS;
}
svn_error_t *
svn_wc__db_open(svn_wc__db_t **db,
svn_config_t *config,
svn_boolean_t open_without_upgrade,
svn_boolean_t enforce_empty_wq,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
*db = apr_pcalloc(result_pool, sizeof(**db));
(*db)->config = config;
(*db)->verify_format = !open_without_upgrade;
(*db)->enforce_empty_wq = enforce_empty_wq;
(*db)->dir_data = apr_hash_make(result_pool);
(*db)->state_pool = result_pool;
/* Don't need to initialize (*db)->parse_cache, due to the calloc above */
if (config)
{
svn_error_t *err;
svn_boolean_t sqlite_exclusive = FALSE;
err = svn_config_get_bool(config, &sqlite_exclusive,
SVN_CONFIG_SECTION_WORKING_COPY,
SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE,
FALSE);
if (err)
{
svn_error_clear(err);
}
else
(*db)->exclusive = sqlite_exclusive;
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_close(svn_wc__db_t *db)
{
apr_pool_t *scratch_pool = db->state_pool;
apr_hash_t *roots = apr_hash_make(scratch_pool);
apr_hash_index_t *hi;
/* Collect all the unique WCROOT structures, and empty out DIR_DATA. */
for (hi = apr_hash_first(scratch_pool, db->dir_data);
hi;
hi = apr_hash_next(hi))
{
svn_wc__db_wcroot_t *wcroot = svn__apr_hash_index_val(hi);
const char *local_abspath = svn__apr_hash_index_key(hi);
if (wcroot->sdb)
svn_hash_sets(roots, wcroot->abspath, wcroot);
svn_hash_sets(db->dir_data, local_abspath, NULL);
}
/* Run the cleanup for each WCROOT. */
return svn_error_trace(svn_wc__db_close_many_wcroots(roots, db->state_pool,
scratch_pool));
}
svn_error_t *
svn_wc__db_pdh_create_wcroot(svn_wc__db_wcroot_t **wcroot,
const char *wcroot_abspath,
svn_sqlite__db_t *sdb,
apr_int64_t wc_id,
int format,
svn_boolean_t verify_format,
svn_boolean_t enforce_empty_wq,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- if (sdb != NULL)
+ if (sdb && format == FORMAT_FROM_SDB)
SVN_ERR(svn_sqlite__read_schema_version(&format, sdb, scratch_pool));
/* If we construct a wcroot, then we better have a format. */
SVN_ERR_ASSERT(format >= 1);
/* If this working copy is PRE-1.0, then simply bail out. */
if (format < 4)
{
return svn_error_createf(
SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL,
_("Working copy format of '%s' is too old (%d); "
"please check out your working copy again"),
svn_dirent_local_style(wcroot_abspath, scratch_pool), format);
}
/* If this working copy is from a future version, then bail out. */
if (format > SVN_WC__VERSION)
{
return svn_error_createf(
SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL,
_("This client is too old to work with the working copy at\n"
"'%s' (format %d).\n"
"You need to get a newer Subversion client. For more details, see\n"
" http://subversion.apache.org/faq.html#working-copy-format-change\n"
),
svn_dirent_local_style(wcroot_abspath, scratch_pool),
format);
}
/* Verify that no work items exists. If they do, then our integrity is
suspect and, thus, we cannot use this database. */
if (format >= SVN_WC__HAS_WORK_QUEUE
&& (enforce_empty_wq || (format < SVN_WC__VERSION && verify_format)))
{
svn_error_t *err = verify_no_work(sdb);
if (err)
{
/* Special message for attempts to upgrade a 1.7-dev wc with
outstanding workqueue items. */
if (err->apr_err == SVN_ERR_WC_CLEANUP_REQUIRED
&& format < SVN_WC__VERSION && verify_format)
err = svn_error_quick_wrap(err, _("Cleanup with an older 1.7 "
"client before upgrading with "
"this client"));
return svn_error_trace(err);
}
}
/* Auto-upgrade the SDB if possible. */
if (format < SVN_WC__VERSION && verify_format)
{
return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, NULL,
_("The working copy at '%s'\nis too old "
"(format %d) to work with client version "
"'%s' (expects format %d). You need to "
"upgrade the working copy first.\n"),
svn_dirent_local_style(wcroot_abspath,
scratch_pool),
format, SVN_VERSION, SVN_WC__VERSION);
}
*wcroot = apr_palloc(result_pool, sizeof(**wcroot));
(*wcroot)->abspath = wcroot_abspath;
(*wcroot)->sdb = sdb;
(*wcroot)->wc_id = wc_id;
(*wcroot)->format = format;
/* 8 concurrent locks is probably more than a typical wc_ng based svn client
uses. */
(*wcroot)->owned_locks = apr_array_make(result_pool, 8,
sizeof(svn_wc__db_wclock_t));
(*wcroot)->access_cache = apr_hash_make(result_pool);
/* SDB will be NULL for pre-NG working copies. We only need to run a
cleanup when the SDB is present. */
if (sdb != NULL)
apr_pool_cleanup_register(result_pool, *wcroot, close_wcroot,
apr_pool_cleanup_null);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_close_many_wcroots(apr_hash_t *roots,
apr_pool_t *state_pool,
apr_pool_t *scratch_pool)
{
apr_hash_index_t *hi;
for (hi = apr_hash_first(scratch_pool, roots); hi; hi = apr_hash_next(hi))
{
svn_wc__db_wcroot_t *wcroot = svn__apr_hash_index_val(hi);
apr_status_t result;
result = apr_pool_cleanup_run(state_pool, wcroot, close_wcroot);
if (result != APR_SUCCESS)
return svn_error_wrap_apr(result, NULL);
}
return SVN_NO_ERROR;
}
/* POOL may be NULL if the lifetime of LOCAL_ABSPATH is sufficient. */
static const char *
compute_relpath(const svn_wc__db_wcroot_t *wcroot,
const char *local_abspath,
apr_pool_t *result_pool)
{
const char *relpath = svn_dirent_is_child(wcroot->abspath, local_abspath,
result_pool);
if (relpath == NULL)
return "";
return relpath;
}
/* Return in *LINK_TARGET_ABSPATH the absolute path the symlink at
* LOCAL_ABSPATH is pointing to. Perform all allocations in POOL. */
static svn_error_t *
read_link_target(const char **link_target_abspath,
const char *local_abspath,
apr_pool_t *pool)
{
svn_string_t *link_target;
const char *canon_link_target;
SVN_ERR(svn_io_read_link(&link_target, local_abspath, pool));
if (link_target->len == 0)
return svn_error_createf(SVN_ERR_WC_NOT_SYMLINK, NULL,
_("The symlink at '%s' points nowhere"),
svn_dirent_local_style(local_abspath, pool));
canon_link_target = svn_dirent_canonicalize(link_target->data, pool);
/* Treat relative symlinks as relative to LOCAL_ABSPATH's parent. */
if (!svn_dirent_is_absolute(canon_link_target))
canon_link_target = svn_dirent_join(svn_dirent_dirname(local_abspath,
pool),
canon_link_target, pool);
/* Collapse any .. in the symlink part of the path. */
if (svn_path_is_backpath_present(canon_link_target))
SVN_ERR(svn_dirent_get_absolute(link_target_abspath, canon_link_target,
pool));
else
*link_target_abspath = canon_link_target;
return SVN_NO_ERROR;
}
+/* Verify if the sqlite_stat1 table exists and if not tries to add
+ this table (but ignores errors on adding the schema) */
+static svn_error_t *
+verify_stats_table(svn_sqlite__db_t *sdb,
+ int format,
+ apr_pool_t *scratch_pool)
+{
+ svn_sqlite__stmt_t *stmt;
+ svn_boolean_t have_row;
+
+ if (format != SVN_WC__ENSURE_STAT1_TABLE)
+ return SVN_NO_ERROR;
+
+ SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
+ STMT_HAVE_STAT1_TABLE));
+ SVN_ERR(svn_sqlite__step(&have_row, stmt));
+ SVN_ERR(svn_sqlite__reset(stmt));
+
+ if (!have_row)
+ {
+ svn_error_clear(
+ svn_wc__db_install_schema_statistics(sdb, scratch_pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Sqlite transaction helper for opening the db in
+ svn_wc__db_wcroot_parse_local_abspath() to avoid multiple
+ db operations that each obtain and release a lock */
+static svn_error_t *
+fetch_sdb_info(apr_int64_t *wc_id,
+ int *format,
+ svn_sqlite__db_t *sdb,
+ apr_pool_t *scratch_pool)
+{
+ *wc_id = -1;
+ *format = -1;
+
+ SVN_SQLITE__WITH_LOCK4(
+ svn_wc__db_util_fetch_wc_id(wc_id, sdb, scratch_pool),
+ svn_sqlite__read_schema_version(format, sdb, scratch_pool),
+ verify_stats_table(sdb, *format, scratch_pool),
+ SVN_NO_ERROR,
+ sdb);
+
+ return SVN_NO_ERROR;
+}
+
+
svn_error_t *
svn_wc__db_wcroot_parse_local_abspath(svn_wc__db_wcroot_t **wcroot,
const char **local_relpath,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *local_dir_abspath;
const char *original_abspath = local_abspath;
svn_node_kind_t kind;
const char *build_relpath;
svn_wc__db_wcroot_t *probe_wcroot;
svn_wc__db_wcroot_t *found_wcroot = NULL;
const char *scan_abspath;
svn_sqlite__db_t *sdb = NULL;
svn_boolean_t moved_upwards = FALSE;
svn_boolean_t always_check = FALSE;
int wc_format = 0;
const char *adm_relpath;
/* Non-NULL if WCROOT is found through a symlink: */
const char *symlink_wcroot_abspath = NULL;
/* ### we need more logic for finding the database (if it is located
### outside of the wcroot) and then managing all of that within DB.
### for now: play quick & dirty. */
probe_wcroot = svn_hash_gets(db->dir_data, local_abspath);
if (probe_wcroot != NULL)
{
*wcroot = probe_wcroot;
/* We got lucky. Just return the thing BEFORE performing any I/O. */
/* ### validate SMODE against how we opened wcroot->sdb? and against
### DB->mode? (will we record per-dir mode?) */
/* ### for most callers, we could pass NULL for result_pool. */
*local_relpath = compute_relpath(probe_wcroot, local_abspath,
result_pool);
return SVN_NO_ERROR;
}
/* ### at some point in the future, we may need to find a way to get
### rid of this stat() call. it is going to happen for EVERY call
### into wc_db which references a file. calls for directories could
### get an early-exit in the hash lookup just above. */
SVN_ERR(get_path_kind(&kind, db, local_abspath, scratch_pool));
if (kind != svn_node_dir)
{
/* If the node specified by the path is NOT present, then it cannot
possibly be a directory containing ".svn/wc.db".
If it is a file, then it cannot contain ".svn/wc.db".
For both of these cases, strip the basename off of the path and
move up one level. Keep record of what we strip, though, since
we'll need it later to construct local_relpath. */
svn_dirent_split(&local_dir_abspath, &build_relpath, local_abspath,
scratch_pool);
/* Is this directory in our hash? */
probe_wcroot = svn_hash_gets(db->dir_data, local_dir_abspath);
if (probe_wcroot != NULL)
{
const char *dir_relpath;
*wcroot = probe_wcroot;
/* Stashed directory's local_relpath + basename. */
dir_relpath = compute_relpath(probe_wcroot, local_dir_abspath,
NULL);
*local_relpath = svn_relpath_join(dir_relpath,
build_relpath,
result_pool);
return SVN_NO_ERROR;
}
/* If the requested path is not on the disk, then we don't know how
many ancestors need to be scanned until we start hitting content
on the disk. Set ALWAYS_CHECK to keep looking for .svn/entries
rather than bailing out after the first check. */
if (kind == svn_node_none)
always_check = TRUE;
/* Start the scanning at LOCAL_DIR_ABSPATH. */
local_abspath = local_dir_abspath;
}
else
{
/* Start the local_relpath empty. If *this* directory contains the
wc.db, then relpath will be the empty string. */
build_relpath = "";
/* Remember the dir containing LOCAL_ABSPATH (they're the same). */
local_dir_abspath = local_abspath;
}
/* LOCAL_ABSPATH refers to a directory at this point. At this point,
we've determined that an associated WCROOT is NOT in the DB's hash
table for this directory. Let's find an existing one in the ancestors,
or create one when we find the actual wcroot. */
/* Assume that LOCAL_ABSPATH is a directory, and look for the SQLite
database in the right place. If we find it... great! If not, then
peel off some components, and try again. */
adm_relpath = svn_wc_get_adm_dir(scratch_pool);
while (TRUE)
{
svn_error_t *err;
svn_node_kind_t adm_subdir_kind;
const char *adm_subdir = svn_dirent_join(local_abspath, adm_relpath,
scratch_pool);
SVN_ERR(svn_io_check_path(adm_subdir, &adm_subdir_kind, scratch_pool));
if (adm_subdir_kind == svn_node_dir)
{
/* We always open the database in read/write mode. If the database
isn't writable in the filesystem, SQLite will internally open
it as read-only, and we'll get an error if we try to do a write
operation.
We could decide what to do on a per-operation basis, but since
we're caching database handles, it make sense to be as permissive
as the filesystem allows. */
err = svn_wc__db_util_open_db(&sdb, local_abspath, SDB_FILE,
svn_sqlite__mode_readwrite,
db->exclusive, NULL,
db->state_pool, scratch_pool);
if (err == NULL)
{
#ifdef SVN_DEBUG
/* Install self-verification trigger statements. */
err = svn_sqlite__exec_statements(sdb,
STMT_VERIFICATION_TRIGGERS);
if (err && err->apr_err == SVN_ERR_SQLITE_ERROR)
{
/* Verification triggers can fail to install on old 1.7-dev
* formats which didn't have a NODES table yet. Ignore sqlite
* errors so such working copies can be upgraded. */
svn_error_clear(err);
}
else
SVN_ERR(err);
#endif
break;
}
if (err->apr_err != SVN_ERR_SQLITE_ERROR
&& !APR_STATUS_IS_ENOENT(err->apr_err))
return svn_error_trace(err);
svn_error_clear(err);
/* If we have not moved upwards, then check for a wc-1 working copy.
Since wc-1 has a .svn in every directory, and we didn't find one
in the original directory, then we aren't looking at a wc-1.
If the original path is not present, then we have to check on every
iteration. The content may be the immediate parent, or possibly
five ancetors higher. We don't test for directory presence (just
for the presence of subdirs/files), so we don't know when we can
stop checking ... so just check always. */
if (!moved_upwards || always_check)
{
SVN_ERR(get_old_version(&wc_format, local_abspath,
scratch_pool));
if (wc_format != 0)
break;
}
}
/* We couldn't open the SDB within the specified directory, so
move up one more directory. */
if (svn_dirent_is_root(local_abspath, strlen(local_abspath)))
{
/* Hit the root without finding a wcroot. */
/* The wcroot could be a symlink to a directory.
* (Issue #2557, #3987). If so, try again, this time scanning
* for a db within the directory the symlink points to,
* rather than within the symlink's parent directory. */
if (kind == svn_node_symlink)
{
svn_node_kind_t resolved_kind;
local_abspath = original_abspath;
SVN_ERR(svn_io_check_resolved_path(local_abspath,
&resolved_kind,
scratch_pool));
if (resolved_kind == svn_node_dir)
{
/* Is this directory recorded in our hash? */
found_wcroot = svn_hash_gets(db->dir_data, local_abspath);
if (found_wcroot)
break;
symlink_wcroot_abspath = local_abspath;
SVN_ERR(read_link_target(&local_abspath, local_abspath,
scratch_pool));
try_symlink_as_dir:
kind = svn_node_dir;
moved_upwards = FALSE;
local_dir_abspath = local_abspath;
build_relpath = "";
continue;
}
}
return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
_("'%s' is not a working copy"),
svn_dirent_local_style(original_abspath,
scratch_pool));
}
local_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
moved_upwards = TRUE;
symlink_wcroot_abspath = NULL;
/* Is the parent directory recorded in our hash? */
found_wcroot = svn_hash_gets(db->dir_data, local_abspath);
if (found_wcroot != NULL)
break;
}
if (found_wcroot != NULL)
{
/* We found a hash table entry for an ancestor, so we stopped scanning
since all subdirectories use the same WCROOT. */
*wcroot = found_wcroot;
}
else if (wc_format == 0)
{
/* We finally found the database. Construct a wcroot_t for it. */
apr_int64_t wc_id;
+ int format;
svn_error_t *err;
- err = svn_wc__db_util_fetch_wc_id(&wc_id, sdb, scratch_pool);
+ err = fetch_sdb_info(&wc_id, &format, sdb, scratch_pool);
if (err)
{
if (err->apr_err == SVN_ERR_WC_CORRUPT)
return svn_error_quick_wrap(
err, apr_psprintf(scratch_pool,
_("Missing a row in WCROOT for '%s'."),
svn_dirent_local_style(original_abspath,
scratch_pool)));
return svn_error_trace(err);
}
/* WCROOT.local_abspath may be NULL when the database is stored
inside the wcroot, but we know the abspath is this directory
(ie. where we found it). */
err = svn_wc__db_pdh_create_wcroot(wcroot,
apr_pstrdup(db->state_pool,
symlink_wcroot_abspath
? symlink_wcroot_abspath
: local_abspath),
- sdb, wc_id, FORMAT_FROM_SDB,
+ sdb, wc_id, format,
db->verify_format, db->enforce_empty_wq,
db->state_pool, scratch_pool);
if (err && (err->apr_err == SVN_ERR_WC_UNSUPPORTED_FORMAT ||
err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED) &&
kind == svn_node_symlink)
{
/* We found an unsupported WC after traversing upwards from a
* symlink. Fall through to code below to check if the symlink
* points at a supported WC. */
svn_error_clear(err);
*wcroot = NULL;
}
else if (err)
{
/* Close handle if we are not going to use it to support
upgrading with exclusive wc locking. */
return svn_error_compose_create(err, svn_sqlite__close(sdb));
}
}
else
{
/* We found something that looks like a wc-1 working copy directory.
However, if the format version is 12 and the .svn/entries file
is only 3 bytes long, then it's a breadcrumb in a wc-ng working
copy that's missing an .svn/wc.db, or its .svn/wc.db is corrupt. */
if (wc_format == SVN_WC__WC_NG_VERSION /* 12 */)
{
apr_finfo_t info;
/* Check attributes of .svn/entries */
const char *admin_abspath = svn_wc__adm_child(
local_abspath, SVN_WC__ADM_ENTRIES, scratch_pool);
svn_error_t *err = svn_io_stat(&info, admin_abspath, APR_FINFO_SIZE,
scratch_pool);
/* If the former does not succeed, something is seriously wrong. */
if (err)
return svn_error_createf(
SVN_ERR_WC_CORRUPT, err,
_("The working copy at '%s' is corrupt."),
svn_dirent_local_style(local_abspath, scratch_pool));
svn_error_clear(err);
if (3 == info.size)
{
/* Check existence of .svn/wc.db */
admin_abspath = svn_wc__adm_child(local_abspath, SDB_FILE,
scratch_pool);
err = svn_io_stat(&info, admin_abspath, APR_FINFO_SIZE,
scratch_pool);
if (err && APR_STATUS_IS_ENOENT(err->apr_err))
{
svn_error_clear(err);
return svn_error_createf(
SVN_ERR_WC_CORRUPT, NULL,
_("The working copy database at '%s' is missing."),
svn_dirent_local_style(local_abspath, scratch_pool));
}
else
/* We should never have reached this point in the code
if .svn/wc.db exists; therefore it's best to assume
it's corrupt. */
return svn_error_createf(
SVN_ERR_WC_CORRUPT, err,
_("The working copy database at '%s' is corrupt."),
svn_dirent_local_style(local_abspath, scratch_pool));
}
}
SVN_ERR(svn_wc__db_pdh_create_wcroot(wcroot,
apr_pstrdup(db->state_pool,
symlink_wcroot_abspath
? symlink_wcroot_abspath
: local_abspath),
NULL, UNKNOWN_WC_ID, wc_format,
db->verify_format, db->enforce_empty_wq,
db->state_pool, scratch_pool));
}
if (*wcroot)
{
const char *dir_relpath;
if (symlink_wcroot_abspath)
{
/* The WCROOT was found through a symlink pointing at the root of
* the WC. Cache the WCROOT under the symlink's path. */
local_dir_abspath = symlink_wcroot_abspath;
}
/* The subdirectory's relpath is easily computed relative to the
wcroot that we just found. */
dir_relpath = compute_relpath(*wcroot, local_dir_abspath, NULL);
/* And the result local_relpath may include a filename. */
*local_relpath = svn_relpath_join(dir_relpath, build_relpath, result_pool);
}
if (kind == svn_node_symlink)
{
svn_boolean_t retry_if_dir = FALSE;
svn_wc__db_status_t status;
svn_boolean_t conflicted;
svn_error_t *err;
/* Check if the symlink is versioned or obstructs a versioned node
* in this DB -- in that case, use this wcroot. Else, if the symlink
* points to a directory, try to find a wcroot in that directory
* instead. */
if (*wcroot)
{
err = svn_wc__db_read_info_internal(&status, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, &conflicted,
NULL, NULL, NULL, NULL, NULL,
NULL, *wcroot, *local_relpath,
scratch_pool, scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND
&& !SVN_WC__ERR_IS_NOT_CURRENT_WC(err))
return svn_error_trace(err);
svn_error_clear(err);
retry_if_dir = TRUE; /* The symlink is unversioned. */
}
else
{
/* The symlink is versioned, or obstructs a versioned node.
* Ignore non-conflicted not-present/excluded nodes.
* This allows the symlink to redirect the wcroot query to a
* directory, regardless of 'invisible' nodes in this WC. */
retry_if_dir = ((status == svn_wc__db_status_not_present ||
status == svn_wc__db_status_excluded ||
status == svn_wc__db_status_server_excluded)
&& !conflicted);
}
}
else
retry_if_dir = TRUE;
if (retry_if_dir)
{
svn_node_kind_t resolved_kind;
SVN_ERR(svn_io_check_resolved_path(original_abspath,
&resolved_kind,
scratch_pool));
if (resolved_kind == svn_node_dir)
{
symlink_wcroot_abspath = original_abspath;
SVN_ERR(read_link_target(&local_abspath, original_abspath,
scratch_pool));
/* This handle was opened in this function but is not going
to be used further so close it. */
if (sdb)
SVN_ERR(svn_sqlite__close(sdb));
goto try_symlink_as_dir;
}
}
}
/* We've found the appropriate WCROOT for the requested path. Stash
it into that path's directory. */
svn_hash_sets(db->dir_data,
apr_pstrdup(db->state_pool, local_dir_abspath),
*wcroot);
/* Did we traverse up to parent directories? */
if (!moved_upwards)
{
/* We did NOT move to a parent of the original requested directory.
We've constructed and filled in a WCROOT for the request, so we
are done. */
return SVN_NO_ERROR;
}
/* The WCROOT that we just found/built was for the LOCAL_ABSPATH originally
passed into this function. We stepped *at least* one directory above that.
We should now associate the WROOT for each parent directory that does
not (yet) have one. */
scan_abspath = local_dir_abspath;
do
{
const char *parent_dir = svn_dirent_dirname(scan_abspath, scratch_pool);
svn_wc__db_wcroot_t *parent_wcroot;
parent_wcroot = svn_hash_gets(db->dir_data, parent_dir);
if (parent_wcroot == NULL)
{
svn_hash_sets(db->dir_data, apr_pstrdup(db->state_pool, parent_dir),
*wcroot);
}
/* Move up a directory, stopping when we reach the directory where
we found/built the WCROOT. */
scan_abspath = parent_dir;
}
while (strcmp(scan_abspath, local_abspath) != 0);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_drop_root(svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *root_wcroot = svn_hash_gets(db->dir_data, local_abspath);
apr_hash_index_t *hi;
apr_status_t result;
if (!root_wcroot)
return SVN_NO_ERROR;
if (strcmp(root_wcroot->abspath, local_abspath) != 0)
return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
_("'%s' is not a working copy root"),
svn_dirent_local_style(local_abspath,
scratch_pool));
for (hi = apr_hash_first(scratch_pool, db->dir_data);
hi;
hi = apr_hash_next(hi))
{
svn_wc__db_wcroot_t *wcroot = svn__apr_hash_index_val(hi);
if (wcroot == root_wcroot)
svn_hash_sets(db->dir_data, svn__apr_hash_index_key(hi), NULL);
}
result = apr_pool_cleanup_run(db->state_pool, root_wcroot, close_wcroot);
if (result != APR_SUCCESS)
return svn_error_wrap_apr(result, NULL);
return SVN_NO_ERROR;
}
Index: vendor/subversion/dist/subversion/svn/conflict-callbacks.c
===================================================================
--- vendor/subversion/dist/subversion/svn/conflict-callbacks.c (revision 286500)
+++ vendor/subversion/dist/subversion/svn/conflict-callbacks.c (revision 286501)
@@ -1,1390 +1,1299 @@
/*
* conflict-callbacks.c: conflict resolution callbacks specific to the
* commandline client.
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include <apr_xlate.h> /* for APR_LOCALE_CHARSET */
#define APR_WANT_STRFUNC
#include <apr_want.h>
#include "svn_hash.h"
#include "svn_cmdline.h"
#include "svn_client.h"
#include "svn_dirent_uri.h"
#include "svn_types.h"
#include "svn_pools.h"
#include "svn_sorts.h"
#include "svn_utf.h"
#include "cl.h"
#include "cl-conflicts.h"
#include "private/svn_cmdline_private.h"
#include "svn_private_config.h"
#define ARRAY_LEN(ary) ((sizeof (ary)) / (sizeof ((ary)[0])))
struct svn_cl__interactive_conflict_baton_t {
svn_cl__accept_t accept_which;
apr_hash_t *config;
const char *editor_cmd;
svn_boolean_t external_failed;
svn_cmdline_prompt_baton_t *pb;
const char *path_prefix;
svn_boolean_t quit;
svn_cl__conflict_stats_t *conflict_stats;
};
svn_error_t *
svn_cl__get_conflict_func_interactive_baton(
svn_cl__interactive_conflict_baton_t **b,
svn_cl__accept_t accept_which,
apr_hash_t *config,
const char *editor_cmd,
svn_cl__conflict_stats_t *conflict_stats,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *result_pool)
{
svn_cmdline_prompt_baton_t *pb = apr_palloc(result_pool, sizeof(*pb));
pb->cancel_func = cancel_func;
pb->cancel_baton = cancel_baton;
*b = apr_palloc(result_pool, sizeof(**b));
(*b)->accept_which = accept_which;
(*b)->config = config;
(*b)->editor_cmd = editor_cmd;
(*b)->external_failed = FALSE;
(*b)->pb = pb;
SVN_ERR(svn_dirent_get_absolute(&(*b)->path_prefix, "", result_pool));
(*b)->quit = FALSE;
(*b)->conflict_stats = conflict_stats;
return SVN_NO_ERROR;
}
svn_cl__accept_t
svn_cl__accept_from_word(const char *word)
{
/* Shorthand options are consistent with svn_cl__conflict_handler(). */
if (strcmp(word, SVN_CL__ACCEPT_POSTPONE) == 0
|| strcmp(word, "p") == 0 || strcmp(word, ":-P") == 0)
return svn_cl__accept_postpone;
if (strcmp(word, SVN_CL__ACCEPT_BASE) == 0)
/* ### shorthand? */
return svn_cl__accept_base;
if (strcmp(word, SVN_CL__ACCEPT_WORKING) == 0)
/* ### shorthand? */
return svn_cl__accept_working;
if (strcmp(word, SVN_CL__ACCEPT_MINE_CONFLICT) == 0
|| strcmp(word, "mc") == 0 || strcmp(word, "X-)") == 0)
return svn_cl__accept_mine_conflict;
if (strcmp(word, SVN_CL__ACCEPT_THEIRS_CONFLICT) == 0
|| strcmp(word, "tc") == 0 || strcmp(word, "X-(") == 0)
return svn_cl__accept_theirs_conflict;
if (strcmp(word, SVN_CL__ACCEPT_MINE_FULL) == 0
|| strcmp(word, "mf") == 0 || strcmp(word, ":-)") == 0)
return svn_cl__accept_mine_full;
if (strcmp(word, SVN_CL__ACCEPT_THEIRS_FULL) == 0
|| strcmp(word, "tf") == 0 || strcmp(word, ":-(") == 0)
return svn_cl__accept_theirs_full;
if (strcmp(word, SVN_CL__ACCEPT_EDIT) == 0
|| strcmp(word, "e") == 0 || strcmp(word, ":-E") == 0)
return svn_cl__accept_edit;
if (strcmp(word, SVN_CL__ACCEPT_LAUNCH) == 0
|| strcmp(word, "l") == 0 || strcmp(word, ":-l") == 0)
return svn_cl__accept_launch;
/* word is an invalid action. */
return svn_cl__accept_invalid;
}
/* Print on stdout a diff that shows incoming conflicting changes
* corresponding to the conflict described in DESC. */
static svn_error_t *
show_diff(const svn_wc_conflict_description2_t *desc,
const char *path_prefix,
apr_pool_t *pool)
{
const char *path1, *path2;
const char *label1, *label2;
svn_diff_t *diff;
svn_stream_t *output;
svn_diff_file_options_t *options;
if (desc->merged_file)
{
/* For conflicts recorded by the 'merge' operation, show a diff between
* 'mine' (the working version of the file as it appeared before the
* 'merge' operation was run) and 'merged' (the version of the file
* as it appears after the merge operation).
*
* For conflicts recorded by the 'update' and 'switch' operations,
* show a diff beween 'theirs' (the new pristine version of the
* file) and 'merged' (the version of the file as it appears with
* local changes merged with the new pristine version).
*
* This way, the diff is always minimal and clearly identifies changes
* brought into the working copy by the update/switch/merge operation. */
if (desc->operation == svn_wc_operation_merge)
{
path1 = desc->my_abspath;
label1 = _("MINE");
}
else
{
path1 = desc->their_abspath;
label1 = _("THEIRS");
}
path2 = desc->merged_file;
label2 = _("MERGED");
}
else
{
/* There's no merged file, but we can show the
difference between mine and theirs. */
path1 = desc->their_abspath;
label1 = _("THEIRS");
path2 = desc->my_abspath;
label2 = _("MINE");
}
label1 = apr_psprintf(pool, "%s\t- %s",
svn_cl__local_style_skip_ancestor(
path_prefix, path1, pool), label1);
label2 = apr_psprintf(pool, "%s\t- %s",
svn_cl__local_style_skip_ancestor(
path_prefix, path2, pool), label2);
options = svn_diff_file_options_create(pool);
options->ignore_eol_style = TRUE;
SVN_ERR(svn_stream_for_stdout(&output, pool));
SVN_ERR(svn_diff_file_diff_2(&diff, path1, path2,
options, pool));
return svn_diff_file_output_unified3(output, diff,
path1, path2,
label1, label2,
APR_LOCALE_CHARSET,
NULL, FALSE,
pool);
}
/* Print on stdout just the conflict hunks of a diff among the 'base', 'their'
* and 'my' files of DESC. */
static svn_error_t *
show_conflicts(const svn_wc_conflict_description2_t *desc,
apr_pool_t *pool)
{
svn_diff_t *diff;
svn_stream_t *output;
svn_diff_file_options_t *options;
options = svn_diff_file_options_create(pool);
options->ignore_eol_style = TRUE;
SVN_ERR(svn_stream_for_stdout(&output, pool));
SVN_ERR(svn_diff_file_diff3_2(&diff,
desc->base_abspath,
desc->my_abspath,
desc->their_abspath,
options, pool));
/* ### Consider putting the markers/labels from
### svn_wc__merge_internal in the conflict description. */
return svn_diff_file_output_merge2(output, diff,
desc->base_abspath,
desc->my_abspath,
desc->their_abspath,
_("||||||| ORIGINAL"),
_("<<<<<<< MINE (select with 'mc')"),
_(">>>>>>> THEIRS (select with 'tc')"),
"=======",
svn_diff_conflict_display_only_conflicts,
pool);
}
/* Perform a 3-way merge of the conflicting values of a property,
* and write the result to the OUTPUT stream.
*
* If MERGED_ABSPATH is non-NULL, use it as 'my' version instead of
* DESC->MY_ABSPATH.
*
* Assume the values are printable UTF-8 text.
*/
static svn_error_t *
merge_prop_conflict(svn_stream_t *output,
const svn_wc_conflict_description2_t *desc,
const char *merged_abspath,
apr_pool_t *pool)
{
const char *base_abspath = desc->base_abspath;
const char *my_abspath = desc->my_abspath;
const char *their_abspath = desc->their_abspath;
svn_diff_file_options_t *options = svn_diff_file_options_create(pool);
svn_diff_t *diff;
/* If any of the property values is missing, use an empty file instead
* for the purpose of showing a diff. */
if (! base_abspath || ! my_abspath || ! their_abspath)
{
const char *empty_file;
SVN_ERR(svn_io_open_unique_file3(NULL, &empty_file,
NULL, svn_io_file_del_on_pool_cleanup,
pool, pool));
if (! base_abspath)
base_abspath = empty_file;
if (! my_abspath)
my_abspath = empty_file;
if (! their_abspath)
their_abspath = empty_file;
}
options->ignore_eol_style = TRUE;
SVN_ERR(svn_diff_file_diff3_2(&diff,
base_abspath,
merged_abspath ? merged_abspath : my_abspath,
their_abspath,
options, pool));
SVN_ERR(svn_diff_file_output_merge2(output, diff,
base_abspath,
merged_abspath ? merged_abspath
: my_abspath,
their_abspath,
_("||||||| ORIGINAL"),
_("<<<<<<< MINE"),
_(">>>>>>> THEIRS"),
"=======",
svn_diff_conflict_display_modified_original_latest,
pool));
return SVN_NO_ERROR;
}
/* Display the conflicting values of a property as a 3-way diff.
*
* If MERGED_ABSPATH is non-NULL, show it as 'my' version instead of
* DESC->MY_ABSPATH.
*
* Assume the values are printable UTF-8 text.
*/
static svn_error_t *
show_prop_conflict(const svn_wc_conflict_description2_t *desc,
const char *merged_abspath,
apr_pool_t *pool)
{
svn_stream_t *output;
SVN_ERR(svn_stream_for_stdout(&output, pool));
SVN_ERR(merge_prop_conflict(output, desc, merged_abspath, pool));
return SVN_NO_ERROR;
}
/* Run an external editor, passing it the MERGED_FILE, or, if the
* 'merged' file is null, return an error. The tool to use is determined by
* B->editor_cmd, B->config and environment variables; see
* svn_cl__edit_file_externally() for details.
*
* If the tool runs, set *PERFORMED_EDIT to true; if a tool is not
* configured or cannot run, do not touch *PERFORMED_EDIT, report the error
* on stderr, and return SVN_NO_ERROR; if any other error is encountered,
* return that error. */
static svn_error_t *
open_editor(svn_boolean_t *performed_edit,
const char *merged_file,
svn_cl__interactive_conflict_baton_t *b,
apr_pool_t *pool)
{
svn_error_t *err;
if (merged_file)
{
err = svn_cmdline__edit_file_externally(merged_file, b->editor_cmd,
b->config, pool);
if (err && (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_EDITOR))
{
svn_error_t *root_err = svn_error_root_cause(err);
SVN_ERR(svn_cmdline_fprintf(stderr, pool, "%s\n",
root_err->message ? root_err->message :
_("No editor found.")));
svn_error_clear(err);
}
else if (err && (err->apr_err == SVN_ERR_EXTERNAL_PROGRAM))
{
svn_error_t *root_err = svn_error_root_cause(err);
SVN_ERR(svn_cmdline_fprintf(stderr, pool, "%s\n",
root_err->message ? root_err->message :
_("Error running editor.")));
svn_error_clear(err);
}
else if (err)
return svn_error_trace(err);
else
*performed_edit = TRUE;
}
else
SVN_ERR(svn_cmdline_fprintf(stderr, pool,
_("Invalid option; there's no "
"merged version to edit.\n\n")));
return SVN_NO_ERROR;
}
/* Run an external editor, passing it the 'merged' property in DESC.
* The tool to use is determined by B->editor_cmd, B->config and
* environment variables; see svn_cl__edit_file_externally() for details. */
static svn_error_t *
edit_prop_conflict(const char **merged_file_path,
const svn_wc_conflict_description2_t *desc,
svn_cl__interactive_conflict_baton_t *b,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_file_t *file;
const char *file_path;
svn_boolean_t performed_edit = FALSE;
svn_stream_t *merged_prop;
SVN_ERR(svn_io_open_unique_file3(&file, &file_path, NULL,
svn_io_file_del_on_pool_cleanup,
result_pool, scratch_pool));
merged_prop = svn_stream_from_aprfile2(file, TRUE /* disown */,
scratch_pool);
SVN_ERR(merge_prop_conflict(merged_prop, desc, NULL, scratch_pool));
SVN_ERR(svn_stream_close(merged_prop));
SVN_ERR(svn_io_file_flush_to_disk(file, scratch_pool));
SVN_ERR(open_editor(&performed_edit, file_path, b, scratch_pool));
*merged_file_path = (performed_edit ? file_path : NULL);
return SVN_NO_ERROR;
}
/* Run an external merge tool, passing it the 'base', 'their', 'my' and
* 'merged' files in DESC. The tool to use is determined by B->config and
* environment variables; see svn_cl__merge_file_externally() for details.
*
* If the tool runs, set *PERFORMED_EDIT to true; if a tool is not
* configured or cannot run, do not touch *PERFORMED_EDIT, report the error
* on stderr, and return SVN_NO_ERROR; if any other error is encountered,
* return that error. */
static svn_error_t *
launch_resolver(svn_boolean_t *performed_edit,
const svn_wc_conflict_description2_t *desc,
svn_cl__interactive_conflict_baton_t *b,
apr_pool_t *pool)
{
svn_error_t *err;
err = svn_cl__merge_file_externally(desc->base_abspath, desc->their_abspath,
desc->my_abspath, desc->merged_file,
desc->local_abspath, b->config, NULL,
pool);
if (err && err->apr_err == SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL)
{
SVN_ERR(svn_cmdline_fprintf(stderr, pool, "%s\n",
err->message ? err->message :
_("No merge tool found, "
"try '(m) merge' instead.\n")));
svn_error_clear(err);
}
else if (err && err->apr_err == SVN_ERR_EXTERNAL_PROGRAM)
{
SVN_ERR(svn_cmdline_fprintf(stderr, pool, "%s\n",
err->message ? err->message :
_("Error running merge tool, "
"try '(m) merge' instead.")));
svn_error_clear(err);
}
else if (err)
return svn_error_trace(err);
else if (performed_edit)
*performed_edit = TRUE;
return SVN_NO_ERROR;
}
/* Maximum line length for the prompt string. */
#define MAX_PROMPT_WIDTH 70
/* Description of a resolver option */
typedef struct resolver_option_t
{
const char *code; /* one or two characters */
const char *short_desc; /* label in prompt (localized) */
const char *long_desc; /* longer description (localized) */
svn_wc_conflict_choice_t choice; /* or -1 if not a simple choice */
} resolver_option_t;
/* Resolver options for a text conflict */
/* (opt->code == "" causes a blank line break in help_string()) */
static const resolver_option_t text_conflict_options[] =
{
/* Translators: keep long_desc below 70 characters (wrap with a left
margin of 9 spaces if needed); don't translate the words within square
brackets. */
{ "e", N_("edit file"), N_("change merged file in an editor"
" [edit]"),
-1 },
{ "df", N_("show diff"), N_("show all changes made to merged file"),
-1 },
{ "r", N_("mark resolved"), N_("accept merged version of file"),
svn_wc_conflict_choose_merged },
{ "", "", "", svn_wc_conflict_choose_unspecified },
{ "dc", N_("display conflict"), N_("show all conflicts "
"(ignoring merged version)"), -1 },
{ "mc", N_("my side of conflict"), N_("accept my version for all conflicts "
"(same) [mine-conflict]"),
svn_wc_conflict_choose_mine_conflict },
{ "tc", N_("their side of conflict"), N_("accept their version for all "
"conflicts (same)"
" [theirs-conflict]"),
svn_wc_conflict_choose_theirs_conflict },
{ "", "", "", svn_wc_conflict_choose_unspecified },
{ "mf", N_("my version"), N_("accept my version of entire file (even "
"non-conflicts) [mine-full]"),
svn_wc_conflict_choose_mine_full },
{ "tf", N_("their version"), N_("accept their version of entire file "
"(same) [theirs-full]"),
svn_wc_conflict_choose_theirs_full },
{ "", "", "", svn_wc_conflict_choose_unspecified },
{ "m", N_("merge"), N_("use internal merge tool to resolve "
"conflict"), -1 },
{ "l", N_("launch tool"), N_("launch external tool to resolve "
"conflict [launch]"), -1 },
{ "p", N_("postpone"), N_("mark the conflict to be resolved later"
" [postpone]"),
svn_wc_conflict_choose_postpone },
{ "q", N_("quit resolution"), N_("postpone all remaining conflicts"),
svn_wc_conflict_choose_postpone },
{ "s", N_("show all options"), N_("show this list (also 'h', '?')"), -1 },
{ NULL }
};
/* Resolver options for a property conflict */
static const resolver_option_t prop_conflict_options[] =
{
{ "mf", N_("my version"), N_("accept my version of entire property (even "
"non-conflicts) [mine-full]"),
svn_wc_conflict_choose_mine_full },
{ "tf", N_("their version"), N_("accept their version of entire property "
"(same) [theirs-full]"),
svn_wc_conflict_choose_theirs_full },
{ "dc", N_("display conflict"), N_("show conflicts in this property"), -1 },
{ "e", N_("edit property"), N_("change merged property value in an editor"
" [edit]"), -1 },
{ "r", N_("mark resolved"), N_("accept edited version of property"),
svn_wc_conflict_choose_merged },
{ "p", N_("postpone"), N_("mark the conflict to be resolved later"
" [postpone]"),
svn_wc_conflict_choose_postpone },
{ "q", N_("quit resolution"), N_("postpone all remaining conflicts"),
svn_wc_conflict_choose_postpone },
{ "h", N_("help"), N_("show this help (also '?')"), -1 },
{ NULL }
};
-/* Resolver options for an obstructued addition */
-static const resolver_option_t obstructed_add_options[] =
-{
- { "mf", N_("my version"), N_("accept pre-existing item (ignore "
- "upstream addition) [mine-full]"),
- svn_wc_conflict_choose_mine_full },
- { "tf", N_("their version"), N_("accept incoming item (overwrite "
- "pre-existing item) [theirs-full]"),
- svn_wc_conflict_choose_theirs_full },
- { "p", N_("postpone"), N_("mark the conflict to be resolved later"
- " [postpone]"),
- svn_wc_conflict_choose_postpone },
- { "q", N_("quit resolution"), N_("postpone all remaining conflicts"),
- svn_wc_conflict_choose_postpone },
- { "h", N_("help"), N_("show this help (also '?')"), -1 },
- { NULL }
-};
-
/* Resolver options for a tree conflict */
static const resolver_option_t tree_conflict_options[] =
{
{ "r", N_("mark resolved"), N_("accept current working copy state"),
svn_wc_conflict_choose_merged },
{ "p", N_("postpone"), N_("resolve the conflict later [postpone]"),
svn_wc_conflict_choose_postpone },
{ "q", N_("quit resolution"), N_("postpone all remaining conflicts"),
svn_wc_conflict_choose_postpone },
{ "h", N_("help"), N_("show this help (also '?')"), -1 },
{ NULL }
};
static const resolver_option_t tree_conflict_options_update_moved_away[] =
{
{ "mc", N_("apply update (recommended)"),
N_("apply update to the move destination"
" [mine-conflict]"),
svn_wc_conflict_choose_mine_conflict },
{ "r", N_("discard update (breaks move)"), N_("discard update, mark "
"resolved, the move will "
"will become a copy"),
svn_wc_conflict_choose_merged },
{ "p", N_("postpone"), N_("resolve the conflict later [postpone]"),
svn_wc_conflict_choose_postpone },
{ "q", N_("quit resolution"), N_("postpone all remaining conflicts"),
svn_wc_conflict_choose_postpone },
{ "h", N_("help"), N_("show this help (also '?')"), -1 },
{ NULL }
};
static const resolver_option_t tree_conflict_options_update_edit_moved_away[] =
{
{ "mc", N_("apply update to move destination"),
N_("apply incoming update to move destination"
" [mine-conflict]"),
svn_wc_conflict_choose_mine_conflict },
{ "p", N_("postpone"), N_("resolve the conflict later [postpone]"),
svn_wc_conflict_choose_postpone },
{ "q", N_("quit resolution"), N_("postpone all remaining conflicts"),
svn_wc_conflict_choose_postpone },
{ "h", N_("help"), N_("show this help (also '?')"), -1 },
{ NULL }
};
static const resolver_option_t tree_conflict_options_update_deleted[] =
{
{ "mc", N_("keep affected local moves"), N_("keep any local moves affected "
"by this deletion [mine-conflict]"),
svn_wc_conflict_choose_mine_conflict },
{ "r", N_("mark resolved (breaks moves)"), N_("mark resolved, any affected "
"moves will become copies"),
svn_wc_conflict_choose_merged },
{ "p", N_("postpone"), N_("resolve the conflict later [postpone]"),
svn_wc_conflict_choose_postpone },
{ "q", N_("quit resolution"), N_("postpone all remaining conflicts"),
svn_wc_conflict_choose_postpone },
{ "h", N_("help"), N_("show this help (also '?')"), -1 },
{ NULL }
};
static const resolver_option_t tree_conflict_options_update_replaced[] =
{
{ "mc", N_("keep affected local moves"), N_("keep any moves affected by this "
"replacement [mine-conflict]"),
svn_wc_conflict_choose_mine_conflict },
{ "r", N_("mark resolved (breaks moves)"), N_("mark resolved (any affected "
"moves will become copies)"),
svn_wc_conflict_choose_merged },
{ "p", N_("postpone"), N_("resolve the conflict later [postpone]"),
svn_wc_conflict_choose_postpone },
{ "q", N_("quit resolution"), N_("postpone all remaining conflicts"),
svn_wc_conflict_choose_postpone },
{ "h", N_("help"), N_("show this help (also '?')"), -1 },
{ NULL }
};
/* Return a pointer to the option description in OPTIONS matching the
* one- or two-character OPTION_CODE. Return NULL if not found. */
static const resolver_option_t *
find_option(const resolver_option_t *options,
const char *option_code)
{
const resolver_option_t *opt;
for (opt = options; opt->code; opt++)
{
/* Ignore code "" (blank lines) which is not a valid answer. */
if (opt->code[0] && strcmp(opt->code, option_code) == 0)
return opt;
}
return NULL;
}
/* Return a prompt string listing the options OPTIONS. If OPTION_CODES is
* non-null, select only the options whose codes are mentioned in it. */
static const char *
prompt_string(const resolver_option_t *options,
const char *const *option_codes,
apr_pool_t *pool)
{
const char *result = _("Select:");
int left_margin = svn_utf_cstring_utf8_width(result);
const char *line_sep = apr_psprintf(pool, "\n%*s", left_margin, "");
int this_line_len = left_margin;
svn_boolean_t first = TRUE;
while (1)
{
const resolver_option_t *opt;
const char *s;
int slen;
if (option_codes)
{
if (! *option_codes)
break;
opt = find_option(options, *option_codes++);
}
else
{
opt = options++;
if (! opt->code)
break;
}
if (! first)
result = apr_pstrcat(pool, result, ",", (char *)NULL);
s = apr_psprintf(pool, _(" (%s) %s"),
opt->code, _(opt->short_desc));
slen = svn_utf_cstring_utf8_width(s);
/* Break the line if adding the next option would make it too long */
if (this_line_len + slen > MAX_PROMPT_WIDTH)
{
result = apr_pstrcat(pool, result, line_sep, (char *)NULL);
this_line_len = left_margin;
}
result = apr_pstrcat(pool, result, s, (char *)NULL);
this_line_len += slen;
first = FALSE;
}
return apr_pstrcat(pool, result, ": ", (char *)NULL);
}
/* Return a help string listing the OPTIONS. */
static const char *
help_string(const resolver_option_t *options,
apr_pool_t *pool)
{
const char *result = "";
const resolver_option_t *opt;
for (opt = options; opt->code; opt++)
{
/* Append a line describing OPT, or a blank line if its code is "". */
if (opt->code[0])
{
const char *s = apr_psprintf(pool, " (%s)", opt->code);
result = apr_psprintf(pool, "%s%-6s - %s\n",
result, s, _(opt->long_desc));
}
else
{
result = apr_pstrcat(pool, result, "\n", (char *)NULL);
}
}
result = apr_pstrcat(pool, result,
_("Words in square brackets are the corresponding "
"--accept option arguments.\n"),
(char *)NULL);
return result;
}
/* Prompt the user with CONFLICT_OPTIONS, restricted to the options listed
* in OPTIONS_TO_SHOW if that is non-null. Set *OPT to point to the chosen
* one of CONFLICT_OPTIONS (not necessarily one of OPTIONS_TO_SHOW), or to
* NULL if the answer was not one of them.
*
* If the answer is the (globally recognized) 'help' option, then display
* the help (on stderr) and return with *OPT == NULL.
*/
static svn_error_t *
prompt_user(const resolver_option_t **opt,
const resolver_option_t *conflict_options,
const char *const *options_to_show,
void *prompt_baton,
apr_pool_t *scratch_pool)
{
const char *prompt
= prompt_string(conflict_options, options_to_show, scratch_pool);
const char *answer;
SVN_ERR(svn_cmdline_prompt_user2(&answer, prompt, prompt_baton, scratch_pool));
if (strcmp(answer, "h") == 0 || strcmp(answer, "?") == 0)
{
SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "\n%s\n",
help_string(conflict_options,
scratch_pool)));
*opt = NULL;
}
else
{
*opt = find_option(conflict_options, answer);
if (! *opt)
{
SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool,
_("Unrecognized option.\n\n")));
}
}
return SVN_NO_ERROR;
}
/* Ask the user what to do about the text conflict described by DESC.
* Return the answer in RESULT. B is the conflict baton for this
* conflict resolution session.
* SCRATCH_POOL is used for temporary allocations. */
static svn_error_t *
handle_text_conflict(svn_wc_conflict_result_t *result,
const svn_wc_conflict_description2_t *desc,
svn_cl__interactive_conflict_baton_t *b,
apr_pool_t *scratch_pool)
{
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
svn_boolean_t diff_allowed = FALSE;
/* Have they done something that might have affected the merged
file (so that we need to save a .edited copy)? */
svn_boolean_t performed_edit = FALSE;
/* Have they done *something* (edit, look at diff, etc) to
give them a rational basis for choosing (r)esolved? */
svn_boolean_t knows_something = FALSE;
SVN_ERR_ASSERT(desc->kind == svn_wc_conflict_kind_text);
SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool,
_("Conflict discovered in file '%s'.\n"),
svn_cl__local_style_skip_ancestor(
b->path_prefix, desc->local_abspath,
scratch_pool)));
/* Diffing can happen between base and merged, to show conflict
markers to the user (this is the typical 3-way merge
scenario), or if no base is available, we can show a diff
between mine and theirs. */
if ((desc->merged_file && desc->base_abspath)
|| (!desc->base_abspath && desc->my_abspath && desc->their_abspath))
diff_allowed = TRUE;
while (TRUE)
{
const char *options[ARRAY_LEN(text_conflict_options)];
const char **next_option = options;
const resolver_option_t *opt;
svn_pool_clear(iterpool);
*next_option++ = "p";
if (diff_allowed)
{
*next_option++ = "df";
*next_option++ = "e";
*next_option++ = "m";
if (knows_something)
*next_option++ = "r";
if (! desc->is_binary)
{
*next_option++ = "mc";
*next_option++ = "tc";
}
}
else
{
if (knows_something)
*next_option++ = "r";
*next_option++ = "mf";
*next_option++ = "tf";
}
*next_option++ = "s";
*next_option++ = NULL;
SVN_ERR(prompt_user(&opt, text_conflict_options, options, b->pb,
iterpool));
if (! opt)
continue;
if (strcmp(opt->code, "q") == 0)
{
result->choice = opt->choice;
b->accept_which = svn_cl__accept_postpone;
b->quit = TRUE;
break;
}
else if (strcmp(opt->code, "s") == 0)
{
SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "\n%s\n",
help_string(text_conflict_options,
iterpool)));
}
else if (strcmp(opt->code, "dc") == 0)
{
if (desc->is_binary)
{
SVN_ERR(svn_cmdline_fprintf(stderr, iterpool,
_("Invalid option; cannot "
"display conflicts for a "
"binary file.\n\n")));
continue;
}
else if (! (desc->my_abspath && desc->base_abspath &&
desc->their_abspath))
{
SVN_ERR(svn_cmdline_fprintf(stderr, iterpool,
_("Invalid option; original "
"files not available.\n\n")));
continue;
}
SVN_ERR(show_conflicts(desc, iterpool));
knows_something = TRUE;
}
else if (strcmp(opt->code, "df") == 0)
{
if (! diff_allowed)
{
SVN_ERR(svn_cmdline_fprintf(stderr, iterpool,
_("Invalid option; there's no "
"merged version to diff.\n\n")));
continue;
}
SVN_ERR(show_diff(desc, b->path_prefix, iterpool));
knows_something = TRUE;
}
else if (strcmp(opt->code, "e") == 0 || strcmp(opt->code, ":-E") == 0)
{
SVN_ERR(open_editor(&performed_edit, desc->merged_file, b, iterpool));
if (performed_edit)
knows_something = TRUE;
}
else if (strcmp(opt->code, "m") == 0 || strcmp(opt->code, ":-g") == 0 ||
strcmp(opt->code, "=>-") == 0 || strcmp(opt->code, ":>.") == 0)
{
if (desc->kind != svn_wc_conflict_kind_text)
{
SVN_ERR(svn_cmdline_fprintf(stderr, iterpool,
_("Invalid option; can only "
"resolve text conflicts with "
"the internal merge tool."
"\n\n")));
continue;
}
if (desc->base_abspath && desc->their_abspath &&
desc->my_abspath && desc->merged_file)
{
svn_boolean_t remains_in_conflict;
SVN_ERR(svn_cl__merge_file(desc->base_abspath,
desc->their_abspath,
desc->my_abspath,
desc->merged_file,
desc->local_abspath,
b->path_prefix,
b->editor_cmd,
b->config,
&remains_in_conflict,
iterpool));
knows_something = !remains_in_conflict;
}
else
SVN_ERR(svn_cmdline_fprintf(stderr, iterpool,
_("Invalid option.\n\n")));
}
else if (strcmp(opt->code, "l") == 0 || strcmp(opt->code, ":-l") == 0)
{
/* ### This check should be earlier as it's nasty to offer an option
* and then when the user chooses it say 'Invalid option'. */
/* ### 'merged_file' shouldn't be necessary *before* we launch the
* resolver: it should be the *result* of doing so. */
if (desc->base_abspath && desc->their_abspath &&
desc->my_abspath && desc->merged_file)
{
SVN_ERR(launch_resolver(&performed_edit, desc, b, iterpool));
if (performed_edit)
knows_something = TRUE;
}
else
SVN_ERR(svn_cmdline_fprintf(stderr, iterpool,
_("Invalid option.\n\n")));
}
else if (opt->choice != -1)
{
if ((opt->choice == svn_wc_conflict_choose_mine_conflict
|| opt->choice == svn_wc_conflict_choose_theirs_conflict)
&& desc->is_binary)
{
SVN_ERR(svn_cmdline_fprintf(stderr, iterpool,
_("Invalid option; cannot choose "
"based on conflicts in a "
"binary file.\n\n")));
continue;
}
/* We only allow the user accept the merged version of
the file if they've edited it, or at least looked at
the diff. */
if (opt->choice == svn_wc_conflict_choose_merged
&& ! knows_something)
{
SVN_ERR(svn_cmdline_fprintf(
stderr, iterpool,
_("Invalid option; use diff/edit/merge/launch "
"before choosing 'mark resolved'.\n\n")));
continue;
}
result->choice = opt->choice;
if (performed_edit)
result->save_merged = TRUE;
break;
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Ask the user what to do about the property conflict described by DESC.
* Return the answer in RESULT. B is the conflict baton for this
* conflict resolution session.
* SCRATCH_POOL is used for temporary allocations. */
static svn_error_t *
handle_prop_conflict(svn_wc_conflict_result_t *result,
const svn_wc_conflict_description2_t *desc,
svn_cl__interactive_conflict_baton_t *b,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_pool_t *iterpool;
const char *message;
const char *merged_file_path = NULL;
svn_boolean_t resolved_allowed = FALSE;
/* ### Work around a historical bug in the provider: the path to the
* conflict description file was put in the 'theirs' field, and
* 'theirs' was put in the 'merged' field. */
((svn_wc_conflict_description2_t *)desc)->their_abspath = desc->merged_file;
((svn_wc_conflict_description2_t *)desc)->merged_file = NULL;
SVN_ERR_ASSERT(desc->kind == svn_wc_conflict_kind_property);
SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool,
_("Conflict for property '%s' discovered"
" on '%s'.\n"),
desc->property_name,
svn_cl__local_style_skip_ancestor(
b->path_prefix, desc->local_abspath,
scratch_pool)));
SVN_ERR(svn_cl__get_human_readable_prop_conflict_description(&message, desc,
scratch_pool));
SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "%s\n", message));
iterpool = svn_pool_create(scratch_pool);
while (TRUE)
{
const resolver_option_t *opt;
const char *options[ARRAY_LEN(prop_conflict_options)];
const char **next_option = options;
*next_option++ = "p";
*next_option++ = "mf";
*next_option++ = "tf";
*next_option++ = "dc";
*next_option++ = "e";
if (resolved_allowed)
*next_option++ = "r";
*next_option++ = "q";
*next_option++ = "h";
*next_option++ = NULL;
svn_pool_clear(iterpool);
SVN_ERR(prompt_user(&opt, prop_conflict_options, options, b->pb,
iterpool));
if (! opt)
continue;
if (strcmp(opt->code, "q") == 0)
{
result->choice = opt->choice;
b->accept_which = svn_cl__accept_postpone;
b->quit = TRUE;
break;
}
else if (strcmp(opt->code, "dc") == 0)
{
SVN_ERR(show_prop_conflict(desc, merged_file_path, scratch_pool));
}
else if (strcmp(opt->code, "e") == 0)
{
SVN_ERR(edit_prop_conflict(&merged_file_path, desc, b,
result_pool, scratch_pool));
resolved_allowed = (merged_file_path != NULL);
}
else if (strcmp(opt->code, "r") == 0)
{
if (! resolved_allowed)
{
SVN_ERR(svn_cmdline_fprintf(stderr, iterpool,
_("Invalid option; please edit the property "
"first.\n\n")));
continue;
}
result->merged_file = merged_file_path;
result->choice = svn_wc_conflict_choose_merged;
break;
}
else if (opt->choice != -1)
{
result->choice = opt->choice;
break;
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Ask the user what to do about the tree conflict described by DESC.
* Return the answer in RESULT. B is the conflict baton for this
* conflict resolution session.
* SCRATCH_POOL is used for temporary allocations. */
static svn_error_t *
handle_tree_conflict(svn_wc_conflict_result_t *result,
const svn_wc_conflict_description2_t *desc,
svn_cl__interactive_conflict_baton_t *b,
apr_pool_t *scratch_pool)
{
const char *readable_desc;
apr_pool_t *iterpool;
SVN_ERR(svn_cl__get_human_readable_tree_conflict_description(
&readable_desc, desc, scratch_pool));
SVN_ERR(svn_cmdline_fprintf(
stderr, scratch_pool,
_("Tree conflict on '%s'\n > %s\n"),
svn_cl__local_style_skip_ancestor(b->path_prefix,
desc->local_abspath,
scratch_pool),
readable_desc));
iterpool = svn_pool_create(scratch_pool);
while (1)
{
const resolver_option_t *opt;
const resolver_option_t *tc_opts;
svn_pool_clear(iterpool);
if (desc->operation == svn_wc_operation_update ||
desc->operation == svn_wc_operation_switch)
{
if (desc->reason == svn_wc_conflict_reason_moved_away)
{
if (desc->action == svn_wc_conflict_action_edit)
tc_opts = tree_conflict_options_update_edit_moved_away;
else
tc_opts = tree_conflict_options_update_moved_away;
}
else if (desc->reason == svn_wc_conflict_reason_deleted)
tc_opts = tree_conflict_options_update_deleted;
else if (desc->reason == svn_wc_conflict_reason_replaced)
tc_opts = tree_conflict_options_update_replaced;
else
tc_opts = tree_conflict_options;
}
else
tc_opts = tree_conflict_options;
SVN_ERR(prompt_user(&opt, tc_opts, NULL, b->pb, iterpool));
if (! opt)
continue;
if (strcmp(opt->code, "q") == 0)
{
result->choice = opt->choice;
b->accept_which = svn_cl__accept_postpone;
b->quit = TRUE;
break;
}
else if (opt->choice != -1)
{
result->choice = opt->choice;
break;
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
-/* Ask the user what to do about the obstructed add described by DESC.
- * Return the answer in RESULT. B is the conflict baton for this
- * conflict resolution session.
- * SCRATCH_POOL is used for temporary allocations. */
-static svn_error_t *
-handle_obstructed_add(svn_wc_conflict_result_t *result,
- const svn_wc_conflict_description2_t *desc,
- svn_cl__interactive_conflict_baton_t *b,
- apr_pool_t *scratch_pool)
-{
- apr_pool_t *iterpool;
-
- SVN_ERR(svn_cmdline_fprintf(
- stderr, scratch_pool,
- _("Conflict discovered when trying to add '%s'.\n"
- "An object of the same name already exists.\n"),
- svn_cl__local_style_skip_ancestor(b->path_prefix,
- desc->local_abspath,
- scratch_pool)));
-
- iterpool = svn_pool_create(scratch_pool);
- while (1)
- {
- const resolver_option_t *opt;
-
- svn_pool_clear(iterpool);
-
- SVN_ERR(prompt_user(&opt, obstructed_add_options, NULL, b->pb,
- iterpool));
- if (! opt)
- continue;
-
- if (strcmp(opt->code, "q") == 0)
- {
- result->choice = opt->choice;
- b->accept_which = svn_cl__accept_postpone;
- b->quit = TRUE;
- break;
- }
- else if (opt->choice != -1)
- {
- result->choice = opt->choice;
- break;
- }
- }
- svn_pool_destroy(iterpool);
-
- return SVN_NO_ERROR;
-}
-
/* The body of svn_cl__conflict_func_interactive(). */
static svn_error_t *
conflict_func_interactive(svn_wc_conflict_result_t **result,
const svn_wc_conflict_description2_t *desc,
void *baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_cl__interactive_conflict_baton_t *b = baton;
svn_error_t *err;
/* Start out assuming we're going to postpone the conflict. */
*result = svn_wc_create_conflict_result(svn_wc_conflict_choose_postpone,
NULL, result_pool);
switch (b->accept_which)
{
case svn_cl__accept_invalid:
case svn_cl__accept_unspecified:
/* No (or no valid) --accept option, fall through to prompting. */
break;
case svn_cl__accept_postpone:
(*result)->choice = svn_wc_conflict_choose_postpone;
return SVN_NO_ERROR;
case svn_cl__accept_base:
(*result)->choice = svn_wc_conflict_choose_base;
return SVN_NO_ERROR;
case svn_cl__accept_working:
/* If the caller didn't merge the property values, then I guess
* 'choose working' means 'choose mine'... */
if (! desc->merged_file)
(*result)->merged_file = desc->my_abspath;
(*result)->choice = svn_wc_conflict_choose_merged;
return SVN_NO_ERROR;
case svn_cl__accept_mine_conflict:
(*result)->choice = svn_wc_conflict_choose_mine_conflict;
return SVN_NO_ERROR;
case svn_cl__accept_theirs_conflict:
(*result)->choice = svn_wc_conflict_choose_theirs_conflict;
return SVN_NO_ERROR;
case svn_cl__accept_mine_full:
(*result)->choice = svn_wc_conflict_choose_mine_full;
return SVN_NO_ERROR;
case svn_cl__accept_theirs_full:
(*result)->choice = svn_wc_conflict_choose_theirs_full;
return SVN_NO_ERROR;
case svn_cl__accept_edit:
if (desc->merged_file)
{
if (b->external_failed)
{
(*result)->choice = svn_wc_conflict_choose_postpone;
return SVN_NO_ERROR;
}
err = svn_cmdline__edit_file_externally(desc->merged_file,
b->editor_cmd, b->config,
scratch_pool);
if (err && (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_EDITOR))
{
SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "%s\n",
err->message ? err->message :
_("No editor found;"
" leaving all conflicts.")));
svn_error_clear(err);
b->external_failed = TRUE;
}
else if (err && (err->apr_err == SVN_ERR_EXTERNAL_PROGRAM))
{
SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "%s\n",
err->message ? err->message :
_("Error running editor;"
" leaving all conflicts.")));
svn_error_clear(err);
b->external_failed = TRUE;
}
else if (err)
return svn_error_trace(err);
(*result)->choice = svn_wc_conflict_choose_merged;
return SVN_NO_ERROR;
}
/* else, fall through to prompting. */
break;
case svn_cl__accept_launch:
if (desc->base_abspath && desc->their_abspath
&& desc->my_abspath && desc->merged_file)
{
svn_boolean_t remains_in_conflict;
if (b->external_failed)
{
(*result)->choice = svn_wc_conflict_choose_postpone;
return SVN_NO_ERROR;
}
err = svn_cl__merge_file_externally(desc->base_abspath,
desc->their_abspath,
desc->my_abspath,
desc->merged_file,
desc->local_abspath,
b->config,
&remains_in_conflict,
scratch_pool);
if (err && err->apr_err == SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL)
{
SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "%s\n",
err->message ? err->message :
_("No merge tool found;"
" leaving all conflicts.")));
b->external_failed = TRUE;
return svn_error_trace(err);
}
else if (err && err->apr_err == SVN_ERR_EXTERNAL_PROGRAM)
{
SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "%s\n",
err->message ? err->message :
_("Error running merge tool;"
" leaving all conflicts.")));
b->external_failed = TRUE;
return svn_error_trace(err);
}
else if (err)
return svn_error_trace(err);
if (remains_in_conflict)
(*result)->choice = svn_wc_conflict_choose_postpone;
else
(*result)->choice = svn_wc_conflict_choose_merged;
return SVN_NO_ERROR;
}
/* else, fall through to prompting. */
break;
}
/* We're in interactive mode and either the user gave no --accept
option or the option did not apply; let's prompt. */
/* Handle the most common cases, which is either:
Conflicting edits on a file's text, or
Conflicting edits on a property.
*/
if (((desc->kind == svn_wc_conflict_kind_text)
&& (desc->action == svn_wc_conflict_action_edit)
&& (desc->reason == svn_wc_conflict_reason_edited)))
SVN_ERR(handle_text_conflict(*result, desc, b, scratch_pool));
else if (desc->kind == svn_wc_conflict_kind_property)
SVN_ERR(handle_prop_conflict(*result, desc, b, result_pool, scratch_pool));
-
- /*
- Dealing with obstruction of additions can be tricky. The
- obstructing item could be unversioned, versioned, or even
- schedule-add. Here's a matrix of how the caller should behave,
- based on results we return.
-
- Unversioned Versioned Schedule-Add
-
- choose_mine skip addition, skip addition skip addition
- add existing item
-
- choose_theirs destroy file, schedule-delete, revert add,
- add new item. add new item. rm file,
- add new item
-
- postpone [ bail out ]
-
- */
- else if ((desc->action == svn_wc_conflict_action_add)
- && (desc->reason == svn_wc_conflict_reason_obstructed))
- SVN_ERR(handle_obstructed_add(*result, desc, b, scratch_pool));
-
else if (desc->kind == svn_wc_conflict_kind_tree)
SVN_ERR(handle_tree_conflict(*result, desc, b, scratch_pool));
else /* other types of conflicts -- do nothing about them. */
{
(*result)->choice = svn_wc_conflict_choose_postpone;
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_cl__conflict_func_interactive(svn_wc_conflict_result_t **result,
const svn_wc_conflict_description2_t *desc,
void *baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_cl__interactive_conflict_baton_t *b = baton;
SVN_ERR(conflict_func_interactive(result, desc, baton,
result_pool, scratch_pool));
/* If we are resolving a conflict, adjust the summary of conflicts. */
if ((*result)->choice != svn_wc_conflict_choose_postpone)
{
const char *local_path
= svn_cl__local_style_skip_ancestor(
b->path_prefix, desc->local_abspath, scratch_pool);
svn_cl__conflict_stats_resolved(b->conflict_stats, local_path,
desc->kind);
}
return SVN_NO_ERROR;
}
Index: vendor/subversion/dist/subversion/svn/list-cmd.c
===================================================================
--- vendor/subversion/dist/subversion/svn/list-cmd.c (revision 286500)
+++ vendor/subversion/dist/subversion/svn/list-cmd.c (revision 286501)
@@ -1,424 +1,435 @@
/*
* list-cmd.c -- list a URL
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include "svn_cmdline.h"
#include "svn_client.h"
#include "svn_error.h"
#include "svn_pools.h"
#include "svn_time.h"
#include "svn_xml.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_utf.h"
#include "svn_opt.h"
#include "cl.h"
#include "svn_private_config.h"
/* Baton used when printing directory entries. */
struct print_baton {
svn_boolean_t verbose;
svn_client_ctx_t *ctx;
/* To keep track of last seen external information. */
const char *last_external_parent_url;
const char *last_external_target;
svn_boolean_t in_external;
};
+/* Field flags required for this function */
+static const apr_uint32_t print_dirent_fields = SVN_DIRENT_KIND;
+static const apr_uint32_t print_dirent_fields_verbose = (
+ SVN_DIRENT_KIND | SVN_DIRENT_SIZE | SVN_DIRENT_TIME |
+ SVN_DIRENT_CREATED_REV | SVN_DIRENT_LAST_AUTHOR);
+
/* This implements the svn_client_list_func2_t API, printing a single
directory entry in text format. */
static svn_error_t *
print_dirent(void *baton,
const char *path,
const svn_dirent_t *dirent,
const svn_lock_t *lock,
const char *abs_path,
const char *external_parent_url,
const char *external_target,
apr_pool_t *scratch_pool)
{
struct print_baton *pb = baton;
const char *entryname;
static const char *time_format_long = NULL;
static const char *time_format_short = NULL;
SVN_ERR_ASSERT((external_parent_url == NULL && external_target == NULL) ||
(external_parent_url && external_target));
if (time_format_long == NULL)
time_format_long = _("%b %d %H:%M");
if (time_format_short == NULL)
time_format_short = _("%b %d %Y");
if (pb->ctx->cancel_func)
SVN_ERR(pb->ctx->cancel_func(pb->ctx->cancel_baton));
if (strcmp(path, "") == 0)
{
if (dirent->kind == svn_node_file)
entryname = svn_dirent_basename(abs_path, scratch_pool);
else if (pb->verbose)
entryname = ".";
else
/* Don't bother to list if no useful information will be shown. */
return SVN_NO_ERROR;
}
else
entryname = path;
if (external_parent_url && external_target)
{
if ((pb->last_external_parent_url == NULL
&& pb->last_external_target == NULL)
|| (strcmp(pb->last_external_parent_url, external_parent_url) != 0
|| strcmp(pb->last_external_target, external_target) != 0))
{
SVN_ERR(svn_cmdline_printf(scratch_pool,
_("Listing external '%s'"
" defined on '%s':\n"),
external_target,
external_parent_url));
pb->last_external_parent_url = external_parent_url;
pb->last_external_target = external_target;
}
}
if (pb->verbose)
{
apr_time_t now = apr_time_now();
apr_time_exp_t exp_time;
apr_status_t apr_err;
apr_size_t size;
char timestr[20];
const char *sizestr, *utf8_timestr;
/* svn_time_to_human_cstring gives us something *way* too long
to use for this, so we have to roll our own. We include
the year if the entry's time is not within half a year. */
apr_time_exp_lt(&exp_time, dirent->time);
if (apr_time_sec(now - dirent->time) < (365 * 86400 / 2)
&& apr_time_sec(dirent->time - now) < (365 * 86400 / 2))
{
apr_err = apr_strftime(timestr, &size, sizeof(timestr),
time_format_long, &exp_time);
}
else
{
apr_err = apr_strftime(timestr, &size, sizeof(timestr),
time_format_short, &exp_time);
}
/* if that failed, just zero out the string and print nothing */
if (apr_err)
timestr[0] = '\0';
/* we need it in UTF-8. */
SVN_ERR(svn_utf_cstring_to_utf8(&utf8_timestr, timestr, scratch_pool));
sizestr = apr_psprintf(scratch_pool, "%" SVN_FILESIZE_T_FMT,
dirent->size);
return svn_cmdline_printf
(scratch_pool, "%7ld %-8.8s %c %10s %12s %s%s\n",
dirent->created_rev,
dirent->last_author ? dirent->last_author : " ? ",
lock ? 'O' : ' ',
(dirent->kind == svn_node_file) ? sizestr : "",
utf8_timestr,
entryname,
(dirent->kind == svn_node_dir) ? "/" : "");
}
else
{
return svn_cmdline_printf(scratch_pool, "%s%s\n", entryname,
(dirent->kind == svn_node_dir)
? "/" : "");
}
}
-
+/* Field flags required for this function */
+static const apr_uint32_t print_dirent_xml_fields = (
+ SVN_DIRENT_KIND | SVN_DIRENT_SIZE | SVN_DIRENT_TIME |
+ SVN_DIRENT_CREATED_REV | SVN_DIRENT_LAST_AUTHOR);
/* This implements the svn_client_list_func2_t API, printing a single dirent
in XML format. */
static svn_error_t *
print_dirent_xml(void *baton,
const char *path,
const svn_dirent_t *dirent,
const svn_lock_t *lock,
const char *abs_path,
const char *external_parent_url,
const char *external_target,
apr_pool_t *scratch_pool)
{
struct print_baton *pb = baton;
const char *entryname;
svn_stringbuf_t *sb = svn_stringbuf_create_empty(scratch_pool);
SVN_ERR_ASSERT((external_parent_url == NULL && external_target == NULL) ||
(external_parent_url && external_target));
if (strcmp(path, "") == 0)
{
if (dirent->kind == svn_node_file)
entryname = svn_dirent_basename(abs_path, scratch_pool);
else
/* Don't bother to list if no useful information will be shown. */
return SVN_NO_ERROR;
}
else
entryname = path;
if (pb->ctx->cancel_func)
SVN_ERR(pb->ctx->cancel_func(pb->ctx->cancel_baton));
if (external_parent_url && external_target)
{
if ((pb->last_external_parent_url == NULL
&& pb->last_external_target == NULL)
|| (strcmp(pb->last_external_parent_url, external_parent_url) != 0
|| strcmp(pb->last_external_target, external_target) != 0))
{
if (pb->in_external)
{
/* The external item being listed is different from the previous
one, so close the tag. */
svn_xml_make_close_tag(&sb, scratch_pool, "external");
pb->in_external = FALSE;
}
svn_xml_make_open_tag(&sb, scratch_pool, svn_xml_normal, "external",
"parent_url", external_parent_url,
"target", external_target,
NULL);
pb->last_external_parent_url = external_parent_url;
pb->last_external_target = external_target;
pb->in_external = TRUE;
}
}
svn_xml_make_open_tag(&sb, scratch_pool, svn_xml_normal, "entry",
"kind", svn_cl__node_kind_str_xml(dirent->kind),
NULL);
svn_cl__xml_tagged_cdata(&sb, scratch_pool, "name", entryname);
if (dirent->kind == svn_node_file)
{
svn_cl__xml_tagged_cdata
(&sb, scratch_pool, "size",
apr_psprintf(scratch_pool, "%" SVN_FILESIZE_T_FMT, dirent->size));
}
svn_xml_make_open_tag(&sb, scratch_pool, svn_xml_normal, "commit",
"revision",
apr_psprintf(scratch_pool, "%ld", dirent->created_rev),
NULL);
svn_cl__xml_tagged_cdata(&sb, scratch_pool, "author", dirent->last_author);
if (dirent->time)
svn_cl__xml_tagged_cdata(&sb, scratch_pool, "date",
svn_time_to_cstring(dirent->time, scratch_pool));
svn_xml_make_close_tag(&sb, scratch_pool, "commit");
if (lock)
{
svn_xml_make_open_tag(&sb, scratch_pool, svn_xml_normal, "lock", NULL);
svn_cl__xml_tagged_cdata(&sb, scratch_pool, "token", lock->token);
svn_cl__xml_tagged_cdata(&sb, scratch_pool, "owner", lock->owner);
svn_cl__xml_tagged_cdata(&sb, scratch_pool, "comment", lock->comment);
svn_cl__xml_tagged_cdata(&sb, scratch_pool, "created",
svn_time_to_cstring(lock->creation_date,
scratch_pool));
if (lock->expiration_date != 0)
svn_cl__xml_tagged_cdata(&sb, scratch_pool, "expires",
svn_time_to_cstring
(lock->expiration_date, scratch_pool));
svn_xml_make_close_tag(&sb, scratch_pool, "lock");
}
svn_xml_make_close_tag(&sb, scratch_pool, "entry");
return svn_cl__error_checked_fputs(sb->data, stdout);
}
/* This implements the `svn_opt_subcommand_t' interface. */
svn_error_t *
svn_cl__list(apr_getopt_t *os,
void *baton,
apr_pool_t *pool)
{
svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
apr_array_header_t *targets;
int i;
apr_pool_t *subpool = svn_pool_create(pool);
apr_uint32_t dirent_fields;
struct print_baton pb;
svn_boolean_t seen_nonexistent_target = FALSE;
svn_error_t *err;
svn_error_t *externals_err = SVN_NO_ERROR;
struct svn_cl__check_externals_failed_notify_baton nwb;
SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
opt_state->targets,
ctx, FALSE, pool));
/* Add "." if user passed 0 arguments */
svn_opt_push_implicit_dot_target(targets, pool);
if (opt_state->xml)
{
/* The XML output contains all the information, so "--verbose"
does not apply. */
if (opt_state->verbose)
return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("'verbose' option invalid in XML mode"));
/* If output is not incremental, output the XML header and wrap
everything in a top-level element. This makes the output in
its entirety a well-formed XML document. */
if (! opt_state->incremental)
SVN_ERR(svn_cl__xml_print_header("lists", pool));
}
else
{
if (opt_state->incremental)
return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("'incremental' option only valid in XML "
"mode"));
}
- if (opt_state->verbose || opt_state->xml)
- dirent_fields = SVN_DIRENT_ALL;
+ if (opt_state->xml)
+ dirent_fields = print_dirent_xml_fields;
+ else if (opt_state->verbose)
+ dirent_fields = print_dirent_fields_verbose;
else
- dirent_fields = SVN_DIRENT_KIND; /* the only thing we actually need... */
+ dirent_fields = print_dirent_fields;
pb.ctx = ctx;
pb.verbose = opt_state->verbose;
if (opt_state->depth == svn_depth_unknown)
opt_state->depth = svn_depth_immediates;
if (opt_state->include_externals)
{
nwb.wrapped_func = ctx->notify_func2;
nwb.wrapped_baton = ctx->notify_baton2;
nwb.had_externals_error = FALSE;
ctx->notify_func2 = svn_cl__check_externals_failed_notify_wrapper;
ctx->notify_baton2 = &nwb;
}
/* For each target, try to list it. */
for (i = 0; i < targets->nelts; i++)
{
const char *target = APR_ARRAY_IDX(targets, i, const char *);
const char *truepath;
svn_opt_revision_t peg_revision;
/* Initialize the following variables for
every list target. */
pb.last_external_parent_url = NULL;
pb.last_external_target = NULL;
pb.in_external = FALSE;
svn_pool_clear(subpool);
SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
/* Get peg revisions. */
SVN_ERR(svn_opt_parse_path(&peg_revision, &truepath, target,
subpool));
if (opt_state->xml)
{
svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool);
svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "list",
"path", truepath[0] == '\0' ? "." : truepath,
NULL);
SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
}
err = svn_client_list3(truepath, &peg_revision,
&(opt_state->start_revision),
opt_state->depth,
dirent_fields,
(opt_state->xml || opt_state->verbose),
opt_state->include_externals,
opt_state->xml ? print_dirent_xml : print_dirent,
&pb, ctx, subpool);
if (err)
{
/* If one of the targets is a non-existent URL or wc-entry,
don't bail out. Just warn and move on to the next target. */
if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND ||
err->apr_err == SVN_ERR_FS_NOT_FOUND)
svn_handle_warning2(stderr, err, "svn: ");
else
return svn_error_trace(err);
svn_error_clear(err);
err = NULL;
seen_nonexistent_target = TRUE;
}
if (opt_state->xml)
{
svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool);
if (pb.in_external)
{
/* close the final external item's tag */
svn_xml_make_close_tag(&sb, pool, "external");
pb.in_external = FALSE;
}
svn_xml_make_close_tag(&sb, pool, "list");
SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
}
}
svn_pool_destroy(subpool);
if (opt_state->include_externals && nwb.had_externals_error)
{
externals_err = svn_error_create(SVN_ERR_CL_ERROR_PROCESSING_EXTERNALS,
NULL,
_("Failure occurred processing one or "
"more externals definitions"));
}
if (opt_state->xml && ! opt_state->incremental)
SVN_ERR(svn_cl__xml_print_footer("lists", pool));
if (seen_nonexistent_target)
err = svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL,
_("Could not list all targets because some targets don't exist"));
return svn_error_compose_create(externals_err, err);
}
Index: vendor/subversion/dist/subversion/svn/svn.c
===================================================================
--- vendor/subversion/dist/subversion/svn/svn.c (revision 286500)
+++ vendor/subversion/dist/subversion/svn/svn.c (revision 286501)
@@ -1,2977 +1,2990 @@
/*
* svn.c: Subversion command line client main file.
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
/* ==================================================================== */
/*** Includes. ***/
#include <string.h>
#include <assert.h>
#include <apr_strings.h>
#include <apr_tables.h>
#include <apr_general.h>
#include <apr_signal.h>
#include "svn_cmdline.h"
#include "svn_pools.h"
#include "svn_wc.h"
#include "svn_client.h"
#include "svn_config.h"
#include "svn_string.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_delta.h"
#include "svn_diff.h"
#include "svn_error.h"
#include "svn_io.h"
#include "svn_opt.h"
#include "svn_utf.h"
#include "svn_auth.h"
#include "svn_hash.h"
#include "svn_version.h"
#include "cl.h"
#include "private/svn_opt_private.h"
#include "private/svn_cmdline_private.h"
#include "private/svn_subr_private.h"
#include "svn_private_config.h"
/*** Option Processing ***/
/* Add an identifier here for long options that don't have a short
option. Options that have both long and short options should just
use the short option letter as identifier. */
typedef enum svn_cl__longopt_t {
opt_auth_password = SVN_OPT_FIRST_LONGOPT_ID,
opt_auth_username,
opt_autoprops,
opt_changelist,
opt_config_dir,
opt_config_options,
/* diff options */
opt_diff_cmd,
opt_internal_diff,
opt_no_diff_added,
opt_no_diff_deleted,
opt_show_copies_as_adds,
opt_notice_ancestry,
opt_summarize,
opt_use_git_diff_format,
opt_ignore_properties,
opt_properties_only,
opt_patch_compatible,
/* end of diff options */
opt_dry_run,
opt_editor_cmd,
opt_encoding,
opt_force_log,
opt_force,
opt_keep_changelists,
opt_ignore_ancestry,
opt_ignore_externals,
opt_incremental,
opt_merge_cmd,
opt_native_eol,
opt_new_cmd,
opt_no_auth_cache,
opt_no_autoprops,
opt_no_ignore,
opt_no_unlock,
opt_non_interactive,
opt_force_interactive,
opt_old_cmd,
opt_record_only,
opt_relocate,
opt_remove,
opt_revprop,
opt_stop_on_copy,
opt_strict,
opt_targets,
opt_depth,
opt_set_depth,
opt_version,
opt_xml,
opt_keep_local,
opt_with_revprop,
opt_with_all_revprops,
opt_with_no_revprops,
opt_parents,
opt_accept,
opt_show_revs,
opt_reintegrate,
opt_trust_server_cert,
opt_strip,
opt_ignore_keywords,
opt_reverse_diff,
opt_ignore_whitespace,
opt_diff,
opt_allow_mixed_revisions,
opt_include_externals,
opt_show_inherited_props,
opt_search,
opt_search_and
} svn_cl__longopt_t;
/* Option codes and descriptions for the command line client.
*
* The entire list must be terminated with an entry of nulls.
*/
const apr_getopt_option_t svn_cl__options[] =
{
{"force", opt_force, 0, N_("force operation to run")},
{"force-log", opt_force_log, 0,
N_("force validity of log message source")},
{"help", 'h', 0, N_("show help on a subcommand")},
{NULL, '?', 0, N_("show help on a subcommand")},
{"message", 'm', 1, N_("specify log message ARG")},
{"quiet", 'q', 0, N_("print nothing, or only summary information")},
{"recursive", 'R', 0, N_("descend recursively, same as --depth=infinity")},
{"non-recursive", 'N', 0, N_("obsolete; try --depth=files or --depth=immediates")},
{"change", 'c', 1,
N_("the change made by revision ARG (like -r ARG-1:ARG)\n"
" "
"If ARG is negative this is like -r ARG:ARG-1\n"
" "
"If ARG is of the form ARG1-ARG2 then this is like\n"
" "
"ARG1:ARG2, where ARG1 is inclusive")},
{"revision", 'r', 1,
N_("ARG (some commands also take ARG1:ARG2 range)\n"
" "
"A revision argument can be one of:\n"
" "
" NUMBER revision number\n"
" "
" '{' DATE '}' revision at start of the date\n"
" "
" 'HEAD' latest in repository\n"
" "
" 'BASE' base rev of item's working copy\n"
" "
" 'COMMITTED' last commit at or before BASE\n"
" "
" 'PREV' revision just before COMMITTED")},
{"file", 'F', 1, N_("read log message from file ARG")},
{"incremental", opt_incremental, 0,
N_("give output suitable for concatenation")},
{"encoding", opt_encoding, 1,
N_("treat value as being in charset encoding ARG")},
{"version", opt_version, 0, N_("show program version information")},
{"verbose", 'v', 0, N_("print extra information")},
{"show-updates", 'u', 0, N_("display update information")},
{"username", opt_auth_username, 1, N_("specify a username ARG")},
{"password", opt_auth_password, 1, N_("specify a password ARG")},
{"extensions", 'x', 1,
N_("Specify differencing options for external diff or\n"
" "
"internal diff or blame. Default: '-u'. Options are\n"
" "
"separated by spaces. Internal diff and blame take:\n"
" "
" -u, --unified: Show 3 lines of unified context\n"
" "
" -b, --ignore-space-change: Ignore changes in\n"
" "
" amount of white space\n"
" "
" -w, --ignore-all-space: Ignore all white space\n"
" "
" --ignore-eol-style: Ignore changes in EOL style\n"
" "
" -p, --show-c-function: Show C function name")},
{"targets", opt_targets, 1,
N_("pass contents of file ARG as additional args")},
{"depth", opt_depth, 1,
N_("limit operation by depth ARG ('empty', 'files',\n"
" "
"'immediates', or 'infinity')")},
{"set-depth", opt_set_depth, 1,
N_("set new working copy depth to ARG ('exclude',\n"
" "
"'empty', 'files', 'immediates', or 'infinity')")},
{"xml", opt_xml, 0, N_("output in XML")},
{"strict", opt_strict, 0, N_("use strict semantics")},
{"stop-on-copy", opt_stop_on_copy, 0,
N_("do not cross copies while traversing history")},
{"no-ignore", opt_no_ignore, 0,
N_("disregard default and svn:ignore and\n"
" "
"svn:global-ignores property ignores")},
{"no-auth-cache", opt_no_auth_cache, 0,
N_("do not cache authentication tokens")},
{"trust-server-cert", opt_trust_server_cert, 0,
N_("accept SSL server certificates from unknown\n"
" "
"certificate authorities without prompting (but only\n"
" "
"with '--non-interactive')") },
{"non-interactive", opt_non_interactive, 0,
N_("do no interactive prompting (default is to prompt\n"
" "
"only if standard input is a terminal device)")},
{"force-interactive", opt_force_interactive, 0,
N_("do interactive prompting even if standard input\n"
" "
"is not a terminal device")},
{"dry-run", opt_dry_run, 0,
N_("try operation but make no changes")},
{"ignore-ancestry", opt_ignore_ancestry, 0,
N_("disable merge tracking; diff nodes as if related")},
{"ignore-externals", opt_ignore_externals, 0,
N_("ignore externals definitions")},
{"diff3-cmd", opt_merge_cmd, 1, N_("use ARG as merge command")},
{"editor-cmd", opt_editor_cmd, 1, N_("use ARG as external editor")},
{"record-only", opt_record_only, 0,
N_("merge only mergeinfo differences")},
{"old", opt_old_cmd, 1, N_("use ARG as the older target")},
{"new", opt_new_cmd, 1, N_("use ARG as the newer target")},
{"revprop", opt_revprop, 0,
N_("operate on a revision property (use with -r)")},
{"relocate", opt_relocate, 0, N_("relocate via URL-rewriting")},
{"config-dir", opt_config_dir, 1,
N_("read user configuration files from directory ARG")},
{"config-option", opt_config_options, 1,
N_("set user configuration option in the format:\n"
" "
" FILE:SECTION:OPTION=[VALUE]\n"
" "
"For example:\n"
" "
" servers:global:http-library=serf")},
{"auto-props", opt_autoprops, 0, N_("enable automatic properties")},
{"no-auto-props", opt_no_autoprops, 0, N_("disable automatic properties")},
{"native-eol", opt_native_eol, 1,
N_("use a different EOL marker than the standard\n"
" "
"system marker for files with the svn:eol-style\n"
" "
"property set to 'native'.\n"
" "
"ARG may be one of 'LF', 'CR', 'CRLF'")},
{"limit", 'l', 1, N_("maximum number of log entries")},
{"no-unlock", opt_no_unlock, 0, N_("don't unlock the targets")},
{"remove", opt_remove, 0, N_("remove changelist association")},
{"changelist", opt_changelist, 1,
N_("operate only on members of changelist ARG")},
{"keep-changelists", opt_keep_changelists, 0,
N_("don't delete changelists after commit")},
{"keep-local", opt_keep_local, 0, N_("keep path in working copy")},
{"with-all-revprops", opt_with_all_revprops, 0,
N_("retrieve all revision properties")},
{"with-no-revprops", opt_with_no_revprops, 0,
N_("retrieve no revision properties")},
{"with-revprop", opt_with_revprop, 1,
N_("set revision property ARG in new revision\n"
" "
"using the name[=value] format")},
{"parents", opt_parents, 0, N_("make intermediate directories")},
{"use-merge-history", 'g', 0,
N_("use/display additional information from merge\n"
" "
"history")},
{"accept", opt_accept, 1,
N_("specify automatic conflict resolution action\n"
" "
"('postpone', 'working', 'base', 'mine-conflict',\n"
" "
"'theirs-conflict', 'mine-full', 'theirs-full',\n"
" "
"'edit', 'launch')\n"
" "
"(shorthand: 'p', 'mc', 'tc', 'mf', 'tf', 'e', 'l')"
)},
{"show-revs", opt_show_revs, 1,
N_("specify which collection of revisions to display\n"
" "
"('merged', 'eligible')")},
{"reintegrate", opt_reintegrate, 0,
N_("deprecated")},
{"strip", opt_strip, 1,
N_("number of leading path components to strip from\n"
" "
"paths parsed from the patch file. --strip 0\n"
" "
"is the default and leaves paths unmodified.\n"
" "
"--strip 1 would change the path\n"
" "
"'doc/fudge/crunchy.html' to 'fudge/crunchy.html'.\n"
" "
"--strip 2 would leave just 'crunchy.html'\n"
" "
"The expected component separator is '/' on all\n"
" "
"platforms. A leading '/' counts as one component.")},
{"ignore-keywords", opt_ignore_keywords, 0,
N_("don't expand keywords")},
{"reverse-diff", opt_reverse_diff, 0,
N_("apply the unidiff in reverse")},
{"ignore-whitespace", opt_ignore_whitespace, 0,
N_("ignore whitespace during pattern matching")},
{"diff", opt_diff, 0, N_("produce diff output")}, /* maps to show_diff */
/* diff options */
{"diff-cmd", opt_diff_cmd, 1, N_("use ARG as diff command")},
{"internal-diff", opt_internal_diff, 0,
N_("override diff-cmd specified in config file")},
{"no-diff-added", opt_no_diff_added, 0,
N_("do not print differences for added files")},
{"no-diff-deleted", opt_no_diff_deleted, 0,
N_("do not print differences for deleted files")},
{"show-copies-as-adds", opt_show_copies_as_adds, 0,
N_("don't diff copied or moved files with their source")},
{"notice-ancestry", opt_notice_ancestry, 0,
N_("diff unrelated nodes as delete and add")},
{"summarize", opt_summarize, 0, N_("show a summary of the results")},
{"git", opt_use_git_diff_format, 0,
N_("use git's extended diff format")},
{"ignore-properties", opt_ignore_properties, 0,
N_("ignore properties during the operation")},
{"properties-only", opt_properties_only, 0,
N_("show only properties during the operation")},
{"patch-compatible", opt_patch_compatible, 0,
N_("generate diff suitable for generic third-party\n"
" "
"patch tools; currently the same as\n"
" "
"--show-copies-as-adds --ignore-properties"
)},
/* end of diff options */
{"allow-mixed-revisions", opt_allow_mixed_revisions, 0,
N_("Allow operation on mixed-revision working copy.\n"
" "
"Use of this option is not recommended!\n"
" "
"Please run 'svn update' instead.")},
{"include-externals", opt_include_externals, 0,
N_("Also commit file and dir externals reached by\n"
" "
"recursion. This does not include externals with a\n"
" "
"fixed revision. (See the svn:externals property)")},
{"show-inherited-props", opt_show_inherited_props, 0,
N_("retrieve target's inherited properties")},
{"search", opt_search, 1,
N_("use ARG as search pattern (glob syntax)")},
{"search-and", opt_search_and, 1,
N_("combine ARG with the previous search pattern")},
/* Long-opt Aliases
*
* These have NULL desriptions, but an option code that matches some
* other option (whose description should probably mention its aliases).
*/
{"cl", opt_changelist, 1, NULL},
{0, 0, 0, 0},
};
/*** Command dispatch. ***/
/* Our array of available subcommands.
*
* The entire list must be terminated with an entry of nulls.
*
* In most of the help text "PATH" is used where a working copy path is
* required, "URL" where a repository URL is required and "TARGET" when
* either a path or a url can be used. Hmm, should this be part of the
* help text?
*/
/* Options that apply to all commands. (While not every command may
currently require authentication or be interactive, allowing every
command to take these arguments allows scripts to just pass them
willy-nilly to every invocation of 'svn') . */
const int svn_cl__global_options[] =
{ opt_auth_username, opt_auth_password, opt_no_auth_cache, opt_non_interactive,
opt_force_interactive, opt_trust_server_cert, opt_config_dir,
opt_config_options, 0
};
/* Options for giving a log message. (Some of these also have other uses.)
*/
#define SVN_CL__LOG_MSG_OPTIONS 'm', 'F', \
opt_force_log, \
opt_editor_cmd, \
opt_encoding, \
opt_with_revprop
const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
{
{ "add", svn_cl__add, {0}, N_
("Put files and directories under version control, scheduling\n"
"them for addition to repository. They will be added in next commit.\n"
"usage: add PATH...\n"),
{opt_targets, 'N', opt_depth, 'q', opt_force, opt_no_ignore, opt_autoprops,
opt_no_autoprops, opt_parents },
{{opt_parents, N_("add intermediate parents")}} },
{ "blame", svn_cl__blame, {"praise", "annotate", "ann"}, N_
("Output the content of specified files or\n"
"URLs with revision and author information in-line.\n"
"usage: blame TARGET[@REV]...\n"
"\n"
" If specified, REV determines in which revision the target is first\n"
" looked up.\n"),
{'r', 'v', 'g', opt_incremental, opt_xml, 'x', opt_force} },
{ "cat", svn_cl__cat, {0}, N_
("Output the content of specified files or URLs.\n"
"usage: cat TARGET[@REV]...\n"
"\n"
" If specified, REV determines in which revision the target is first\n"
" looked up.\n"),
{'r'} },
{ "changelist", svn_cl__changelist, {"cl"}, N_
("Associate (or dissociate) changelist CLNAME with the named files.\n"
"usage: 1. changelist CLNAME PATH...\n"
" 2. changelist --remove PATH...\n"),
{ 'q', 'R', opt_depth, opt_remove, opt_targets, opt_changelist} },
{ "checkout", svn_cl__checkout, {"co"}, N_
("Check out a working copy from a repository.\n"
"usage: checkout URL[@REV]... [PATH]\n"
"\n"
" If specified, REV determines in which revision the URL is first\n"
" looked up.\n"
"\n"
" If PATH is omitted, the basename of the URL will be used as\n"
" the destination. If multiple URLs are given each will be checked\n"
" out into a sub-directory of PATH, with the name of the sub-directory\n"
" being the basename of the URL.\n"
"\n"
" If --force is used, unversioned obstructing paths in the working\n"
" copy destination do not automatically cause the check out to fail.\n"
" If the obstructing path is the same type (file or directory) as the\n"
" corresponding path in the repository it becomes versioned but its\n"
" contents are left 'as-is' in the working copy. This means that an\n"
" obstructing directory's unversioned children may also obstruct and\n"
" become versioned. For files, any content differences between the\n"
" obstruction and the repository are treated like a local modification\n"
" to the working copy. All properties from the repository are applied\n"
" to the obstructing path.\n"
"\n"
" See also 'svn help update' for a list of possible characters\n"
" reporting the action taken.\n"),
{'r', 'q', 'N', opt_depth, opt_force, opt_ignore_externals} },
{ "cleanup", svn_cl__cleanup, {0}, N_
("Recursively clean up the working copy, removing write locks, resuming\n"
"unfinished operations, etc.\n"
"usage: cleanup [WCPATH...]\n"
"\n"
" Finish any unfinished business in the working copy at WCPATH, and remove\n"
" write locks (shown as 'L' by the 'svn status' command) from the working\n"
" copy. Usually, this is only necessary if a Subversion client has crashed\n"
" while using the working copy, leaving it in an unusable state.\n"
"\n"
" WARNING: There is no mechanism that will protect write locks still\n"
" being used by other Subversion clients. Running this command\n"
" while another client is using the working copy can corrupt\n"
" the working copy beyond repair!\n"),
{opt_merge_cmd} },
{ "commit", svn_cl__commit, {"ci"},
N_("Send changes from your working copy to the repository.\n"
"usage: commit [PATH...]\n"
"\n"
" A log message must be provided, but it can be empty. If it is not\n"
" given by a --message or --file option, an editor will be started.\n"
" If any targets are (or contain) locked items, those will be\n"
" unlocked after a successful commit.\n"),
{'q', 'N', opt_depth, opt_targets, opt_no_unlock, SVN_CL__LOG_MSG_OPTIONS,
opt_changelist, opt_keep_changelists, opt_include_externals} },
{ "copy", svn_cl__copy, {"cp"}, N_
("Copy files and directories in a working copy or repository.\n"
"usage: copy SRC[@REV]... DST\n"
"\n"
" SRC and DST can each be either a working copy (WC) path or URL:\n"
" WC -> WC: copy and schedule for addition (with history)\n"
" WC -> URL: immediately commit a copy of WC to URL\n"
" URL -> WC: check out URL into WC, schedule for addition\n"
" URL -> URL: complete server-side copy; used to branch and tag\n"
" All the SRCs must be of the same type. When copying multiple sources,\n"
" they will be added as children of DST, which must be a directory.\n"
"\n"
" WARNING: For compatibility with previous versions of Subversion,\n"
" copies performed using two working copy paths (WC -> WC) will not\n"
" contact the repository. As such, they may not, by default, be able\n"
" to propagate merge tracking information from the source of the copy\n"
" to the destination.\n"),
{'r', 'q', opt_ignore_externals, opt_parents, SVN_CL__LOG_MSG_OPTIONS} },
{ "delete", svn_cl__delete, {"del", "remove", "rm"}, N_
("Remove files and directories from version control.\n"
"usage: 1. delete PATH...\n"
" 2. delete URL...\n"
"\n"
" 1. Each item specified by a PATH is scheduled for deletion upon\n"
" the next commit. Files, and directories that have not been\n"
" committed, are immediately removed from the working copy\n"
" unless the --keep-local option is given.\n"
" PATHs that are, or contain, unversioned or modified items will\n"
" not be removed unless the --force or --keep-local option is given.\n"
"\n"
" 2. Each item specified by a URL is deleted from the repository\n"
" via an immediate commit.\n"),
{opt_force, 'q', opt_targets, SVN_CL__LOG_MSG_OPTIONS, opt_keep_local} },
{ "diff", svn_cl__diff, {"di"}, N_
("Display local changes or differences between two revisions or paths.\n"
"usage: 1. diff\n"
" 2. diff [-c M | -r N[:M]] [TARGET[@REV]...]\n"
" 3. diff [-r N[:M]] --old=OLD-TGT[@OLDREV] [--new=NEW-TGT[@NEWREV]] \\\n"
" [PATH...]\n"
" 4. diff OLD-URL[@OLDREV] NEW-URL[@NEWREV]\n"
" 5. diff OLD-URL[@OLDREV] NEW-PATH[@NEWREV]\n"
" 6. diff OLD-PATH[@OLDREV] NEW-URL[@NEWREV]\n"
"\n"
" 1. Use just 'svn diff' to display local modifications in a working copy.\n"
"\n"
" 2. Display the changes made to TARGETs as they are seen in REV between\n"
" two revisions. TARGETs may be all working copy paths or all URLs.\n"
" If TARGETs are working copy paths, N defaults to BASE and M to the\n"
" working copy; if URLs, N must be specified and M defaults to HEAD.\n"
" The '-c M' option is equivalent to '-r N:M' where N = M-1.\n"
" Using '-c -M' does the reverse: '-r M:N' where N = M-1.\n"
"\n"
" 3. Display the differences between OLD-TGT as it was seen in OLDREV and\n"
" NEW-TGT as it was seen in NEWREV. PATHs, if given, are relative to\n"
" OLD-TGT and NEW-TGT and restrict the output to differences for those\n"
" paths. OLD-TGT and NEW-TGT may be working copy paths or URL[@REV].\n"
" NEW-TGT defaults to OLD-TGT if not specified. -r N makes OLDREV default\n"
" to N, -r N:M makes OLDREV default to N and NEWREV default to M.\n"
" If OLDREV or NEWREV are not specified, they default to WORKING for\n"
" working copy targets and to HEAD for URL targets.\n"
"\n"
" Either or both OLD-TGT and NEW-TGT may also be paths to unversioned\n"
" targets. Revisions cannot be specified for unversioned targets.\n"
" Both targets must be of the same node kind (file or directory).\n"
" Diffing unversioned targets against URL targets is not supported.\n"
"\n"
" 4. Shorthand for 'svn diff --old=OLD-URL[@OLDREV] --new=NEW-URL[@NEWREV]'\n"
" 5. Shorthand for 'svn diff --old=OLD-URL[@OLDREV] --new=NEW-PATH[@NEWREV]'\n"
" 6. Shorthand for 'svn diff --old=OLD-PATH[@OLDREV] --new=NEW-URL[@NEWREV]'\n"),
{'r', 'c', opt_old_cmd, opt_new_cmd, 'N', opt_depth, opt_diff_cmd,
opt_internal_diff, 'x', opt_no_diff_added, opt_no_diff_deleted,
opt_ignore_properties, opt_properties_only,
opt_show_copies_as_adds, opt_notice_ancestry, opt_summarize, opt_changelist,
opt_force, opt_xml, opt_use_git_diff_format, opt_patch_compatible} },
{ "export", svn_cl__export, {0}, N_
("Create an unversioned copy of a tree.\n"
"usage: 1. export [-r REV] URL[@PEGREV] [PATH]\n"
" 2. export [-r REV] PATH1[@PEGREV] [PATH2]\n"
"\n"
" 1. Exports a clean directory tree from the repository specified by\n"
" URL, at revision REV if it is given, otherwise at HEAD, into\n"
" PATH. If PATH is omitted, the last component of the URL is used\n"
" for the local directory name.\n"
"\n"
" 2. Exports a clean directory tree from the working copy specified by\n"
" PATH1, at revision REV if it is given, otherwise at WORKING, into\n"
" PATH2. If PATH2 is omitted, the last component of the PATH1 is used\n"
" for the local directory name. If REV is not specified, all local\n"
" changes will be preserved. Files not under version control will\n"
" not be copied.\n"
"\n"
" If specified, PEGREV determines in which revision the target is first\n"
" looked up.\n"),
{'r', 'q', 'N', opt_depth, opt_force, opt_native_eol, opt_ignore_externals,
opt_ignore_keywords} },
{ "help", svn_cl__help, {"?", "h"}, N_
("Describe the usage of this program or its subcommands.\n"
"usage: help [SUBCOMMAND...]\n"),
{0} },
/* This command is also invoked if we see option "--help", "-h" or "-?". */
{ "import", svn_cl__import, {0}, N_
("Commit an unversioned file or tree into the repository.\n"
"usage: import [PATH] URL\n"
"\n"
" Recursively commit a copy of PATH to URL.\n"
" If PATH is omitted '.' is assumed.\n"
" Parent directories are created as necessary in the repository.\n"
" If PATH is a directory, the contents of the directory are added\n"
" directly under URL.\n"
" Unversionable items such as device files and pipes are ignored\n"
" if --force is specified.\n"),
{'q', 'N', opt_depth, opt_autoprops, opt_force, opt_no_autoprops,
SVN_CL__LOG_MSG_OPTIONS, opt_no_ignore} },
{ "info", svn_cl__info, {0}, N_
("Display information about a local or remote item.\n"
"usage: info [TARGET[@REV]...]\n"
"\n"
" Print information about each TARGET (default: '.').\n"
" TARGET may be either a working-copy path or URL. If specified, REV\n"
" determines in which revision the target is first looked up.\n"),
{'r', 'R', opt_depth, opt_targets, opt_incremental, opt_xml, opt_changelist}
},
{ "list", svn_cl__list, {"ls"}, N_
("List directory entries in the repository.\n"
"usage: list [TARGET[@REV]...]\n"
"\n"
" List each TARGET file and the contents of each TARGET directory as\n"
" they exist in the repository. If TARGET is a working copy path, the\n"
" corresponding repository URL will be used. If specified, REV determines\n"
" in which revision the target is first looked up.\n"
"\n"
" The default TARGET is '.', meaning the repository URL of the current\n"
" working directory.\n"
"\n"
" With --verbose, the following fields will be shown for each item:\n"
"\n"
" Revision number of the last commit\n"
" Author of the last commit\n"
" If locked, the letter 'O'. (Use 'svn info URL' to see details)\n"
" Size (in bytes)\n"
" Date and time of the last commit\n"),
{'r', 'v', 'R', opt_depth, opt_incremental, opt_xml,
opt_include_externals },
{{opt_include_externals, N_("include externals definitions")}} },
{ "lock", svn_cl__lock, {0}, N_
("Lock working copy paths or URLs in the repository, so that\n"
"no other user can commit changes to them.\n"
"usage: lock TARGET...\n"
"\n"
" Use --force to steal the lock from another user or working copy.\n"),
{ opt_targets, 'm', 'F', opt_force_log, opt_encoding, opt_force },
{{'F', N_("read lock comment from file ARG")},
{'m', N_("specify lock comment ARG")},
{opt_force_log, N_("force validity of lock comment source")}} },
{ "log", svn_cl__log, {0}, N_
("Show the log messages for a set of revision(s) and/or path(s).\n"
"usage: 1. log [PATH][@REV]\n"
" 2. log URL[@REV] [PATH...]\n"
"\n"
" 1. Print the log messages for the URL corresponding to PATH\n"
" (default: '.'). If specified, REV is the revision in which the\n"
" URL is first looked up, and the default revision range is REV:1.\n"
" If REV is not specified, the default revision range is BASE:1,\n"
" since the URL might not exist in the HEAD revision.\n"
"\n"
" 2. Print the log messages for the PATHs (default: '.') under URL.\n"
" If specified, REV is the revision in which the URL is first\n"
" looked up, and the default revision range is REV:1; otherwise,\n"
" the URL is looked up in HEAD, and the default revision range is\n"
" HEAD:1.\n"
"\n"
" Multiple '-c' or '-r' options may be specified (but not a\n"
" combination of '-c' and '-r' options), and mixing of forward and\n"
" reverse ranges is allowed.\n"
"\n"
" With -v, also print all affected paths with each log message.\n"
" With -q, don't print the log message body itself (note that this is\n"
" compatible with -v).\n"
"\n"
" Each log message is printed just once, even if more than one of the\n"
" affected paths for that revision were explicitly requested. Logs\n"
" follow copy history by default. Use --stop-on-copy to disable this\n"
" behavior, which can be useful for determining branchpoints.\n"
"\n"
" The --depth option is only valid in combination with the --diff option\n"
" and limits the scope of the displayed diff to the specified depth.\n"
"\n"
" If the --search option is used, log messages are displayed only if the\n"
" provided search pattern matches any of the author, date, log message\n"
" text (unless --quiet is used), or, if the --verbose option is also\n"
" provided, a changed path.\n"
" The search pattern may include \"glob syntax\" wildcards:\n"
" ? matches any single character\n"
" * matches a sequence of arbitrary characters\n"
" [abc] matches any of the characters listed inside the brackets\n"
" If multiple --search options are provided, a log message is shown if\n"
" it matches any of the provided search patterns. If the --search-and\n"
" option is used, that option's argument is combined with the pattern\n"
" from the previous --search or --search-and option, and a log message\n"
" is shown only if it matches the combined search pattern.\n"
" If --limit is used in combination with --search, --limit restricts the\n"
" number of log messages searched, rather than restricting the output\n"
" to a particular number of matching log messages.\n"
"\n"
" Examples:\n"
"\n"
" Show the latest 5 log messages for the current working copy\n"
" directory and display paths changed in each commit:\n"
" svn log -l 5 -v\n"
"\n"
" Show the log for bar.c as of revision 42:\n"
" svn log bar.c@42\n"
"\n"
" Show log messages and diffs for each commit to foo.c:\n"
" svn log --diff http://www.example.com/repo/project/foo.c\n"
" (Because the above command uses a full URL it does not require\n"
" a working copy.)\n"
"\n"
" Show log messages for the children foo.c and bar.c of the directory\n"
" '/trunk' as it appeared in revision 50, using the ^/ URL shortcut:\n"
" svn log ^/trunk@50 foo.c bar.c\n"
"\n"
" Show the log messages for any incoming changes to foo.c during the\n"
" next 'svn update':\n"
" svn log -r BASE:HEAD foo.c\n"
"\n"
" Show the log message for the revision in which /branches/foo\n"
" was created:\n"
" svn log --stop-on-copy --limit 1 -r0:HEAD ^/branches/foo\n"),
{'r', 'q', 'v', 'g', 'c', opt_targets, opt_stop_on_copy, opt_incremental,
opt_xml, 'l', opt_with_all_revprops, opt_with_no_revprops, opt_with_revprop,
opt_depth, opt_diff, opt_diff_cmd, opt_internal_diff, 'x', opt_search,
opt_search_and, },
{{opt_with_revprop, N_("retrieve revision property ARG")},
{'c', N_("the change made in revision ARG")}} },
{ "merge", svn_cl__merge, {0}, N_
( /* For this large section, let's keep it unindented for easier
* viewing/editing. It has been vim-treated with a textwidth=75 and 'gw'
* (with quotes and newlines removed). */
"Merge changes into a working copy.\n"
"usage: 1. merge SOURCE[@REV] [TARGET_WCPATH]\n"
" (the 'complete' merge)\n"
" 2. merge [-c M[,N...] | -r N:M ...] SOURCE[@REV] [TARGET_WCPATH]\n"
" (the 'cherry-pick' merge)\n"
" 3. merge SOURCE1[@REV1] SOURCE2[@REV2] [TARGET_WCPATH]\n"
" (the '2-URL' merge)\n"
"\n"
" 1. This form, with one source path and no revision range, is called\n"
" a 'complete' merge:\n"
"\n"
" svn merge SOURCE[@REV] [TARGET_WCPATH]\n"
"\n"
" The complete merge is used for the 'sync' and 'reintegrate' merges\n"
" in the 'feature branch' pattern described below. It finds all the\n"
" changes on the source branch that have not already been merged to the\n"
" target branch, and merges them into the working copy. Merge tracking\n"
" is used to know which changes have already been merged.\n"
"\n"
" SOURCE specifies the branch from where the changes will be pulled, and\n"
" TARGET_WCPATH specifies a working copy of the target branch to which\n"
" the changes will be applied. Normally SOURCE and TARGET_WCPATH should\n"
" each correspond to the root of a branch. (If you want to merge only a\n"
" subtree, then the subtree path must be included in both SOURCE and\n"
" TARGET_WCPATH; this is discouraged, to avoid subtree mergeinfo.)\n"
"\n"
" SOURCE is usually a URL. The optional '@REV' specifies both the peg\n"
" revision of the URL and the latest revision that will be considered\n"
" for merging; if REV is not specified, the HEAD revision is assumed. If\n"
" SOURCE is a working copy path, the corresponding URL of the path is\n"
" used, and the default value of 'REV' is the base revision (usually the\n"
" revision last updated to).\n"
"\n"
" TARGET_WCPATH is a working copy path; if omitted, '.' is generally\n"
" assumed. There are some special cases:\n"
"\n"
" - If SOURCE is a URL:\n"
"\n"
" - If the basename of the URL and the basename of '.' are the\n"
" same, then the differences are applied to '.'. Otherwise,\n"
" if a file with the same basename as that of the URL is found\n"
" within '.', then the differences are applied to that file.\n"
" In all other cases, the target defaults to '.'.\n"
"\n"
" - If SOURCE is a working copy path:\n"
"\n"
" - If the source is a file, then differences are applied to that\n"
" file (useful for reverse-merging earlier changes). Otherwise,\n"
" if the source is a directory, then the target defaults to '.'.\n"
"\n"
" In normal usage the working copy should be up to date, at a single\n"
" revision, with no local modifications and no switched subtrees.\n"
"\n"
" - The 'Feature Branch' Merging Pattern -\n"
"\n"
" In this commonly used work flow, known also as the 'development\n"
" branch' pattern, a developer creates a branch and commits a series of\n"
" changes that implement a new feature. The developer periodically\n"
" merges all the latest changes from the parent branch so as to keep the\n"
" development branch up to date with those changes. When the feature is\n"
" complete, the developer performs a merge from the feature branch to\n"
" the parent branch to re-integrate the changes.\n"
"\n"
" parent --+----------o------o-o-------------o--\n"
" \\ \\ \\ /\n"
" \\ merge merge merge\n"
" \\ \\ \\ /\n"
" feature +--o-o-------o----o-o----o-------\n"
"\n"
" A merge from the parent branch to the feature branch is called a\n"
" 'sync' or 'catch-up' merge, and a merge from the feature branch to the\n"
" parent branch is called a 'reintegrate' merge.\n"
"\n"
" - Sync Merge Example -\n"
" ............\n"
" . .\n"
" trunk --+------------L--------------R------\n"
" \\ \\\n"
" \\ |\n"
" \\ v\n"
" feature +------------------------o-----\n"
" r100 r200\n"
"\n"
" Subversion will locate all the changes on 'trunk' that have not yet\n"
" been merged into the 'feature' branch. In this case that is a single\n"
" range, r100:200. In the diagram above, L marks the left side (trunk@100)\n"
" and R marks the right side (trunk@200) of the merge source. The\n"
" difference between L and R will be applied to the target working copy\n"
" path. In this case, the working copy is a clean checkout of the entire\n"
" 'feature' branch.\n"
"\n"
" To perform this sync merge, have a clean working copy of the feature\n"
" branch and run the following command in its top-level directory:\n"
"\n"
" svn merge ^/trunk\n"
"\n"
" Note that the merge is now only in your local working copy and still\n"
" needs to be committed to the repository so that it can be seen by\n"
" others. You can review the changes and you may have to resolve\n"
" conflicts before you commit the merge.\n"
"\n"
" - Reintegrate Merge Example -\n"
"\n"
" The feature branch was last synced with trunk up to revision X. So the\n"
" difference between trunk@X and feature@HEAD contains the complete set\n"
" of changes that implement the feature, and no other changes. These\n"
" changes are applied to trunk.\n"
"\n"
" rW rX\n"
" trunk ------+--------------------L------------------o\n"
" \\ . ^\n"
" \\ ............. /\n"
" \\ . /\n"
" feature +--------------------------------R\n"
"\n"
" In the diagram above, L marks the left side (trunk@X) and R marks the\n"
" right side (feature@HEAD) of the merge. The difference between the\n"
" left and right side is merged into trunk, the target.\n"
"\n"
" To perform the merge, have a clean working copy of trunk and run the\n"
" following command in its top-level directory:\n"
"\n"
" svn merge ^/feature\n"
"\n"
" To prevent unnecessary merge conflicts, a reintegrate merge requires\n"
" that TARGET_WCPATH is not a mixed-revision working copy, has no local\n"
" modifications, and has no switched subtrees.\n"
"\n"
" A reintegrate merge also requires that the source branch is coherently\n"
" synced with the target -- in the above example, this means that all\n"
" revisions between the branch point W and the last merged revision X\n"
" are merged to the feature branch, so that there are no unmerged\n"
" revisions in-between.\n"
"\n"
"\n"
" 2. This form is called a 'cherry-pick' merge:\n"
"\n"
" svn merge [-c M[,N...] | -r N:M ...] SOURCE[@REV] [TARGET_WCPATH]\n"
"\n"
" A cherry-pick merge is used to merge specific revisions (or revision\n"
" ranges) from one branch to another. By default, this uses merge\n"
" tracking to automatically skip any revisions that have already been\n"
" merged to the target; you can use the --ignore-ancestry option to\n"
" disable such skipping.\n"
"\n"
" SOURCE is usually a URL. The optional '@REV' specifies only the peg\n"
" revision of the URL and does not affect the merge range; if REV is not\n"
" specified, the HEAD revision is assumed. If SOURCE is a working copy\n"
" path, the corresponding URL of the path is used, and the default value\n"
" of 'REV' is the base revision (usually the revision last updated to).\n"
"\n"
" TARGET_WCPATH is a working copy path; if omitted, '.' is generally\n"
" assumed. The special cases noted above in the 'complete' merge form\n"
" also apply here.\n"
"\n"
" The revision ranges to be merged are specified by the '-r' and/or '-c'\n"
" options. '-r N:M' refers to the difference in the history of the\n"
" source branch between revisions N and M. You can use '-c M' to merge\n"
" single revisions: '-c M' is equivalent to '-r <M-1>:M'. Each such\n"
" difference is applied to TARGET_WCPATH.\n"
"\n"
" If the mergeinfo in TARGET_WCPATH indicates that revisions within the\n"
" range were already merged, changes made in those revisions are not\n"
" merged again. If needed, the range is broken into multiple sub-ranges,\n"
" and each sub-range is merged separately.\n"
"\n"
" A 'reverse range' can be used to undo changes. For example, when\n"
" source and target refer to the same branch, a previously committed\n"
" revision can be 'undone'. In a reverse range, N is greater than M in\n"
" '-r N:M', or the '-c' option is used with a negative number: '-c -M'\n"
" is equivalent to '-r M:<M-1>'. Undoing changes like this is also known\n"
" as performing a 'reverse merge'.\n"
"\n"
" Multiple '-c' and/or '-r' options may be specified and mixing of\n"
" forward and reverse ranges is allowed.\n"
"\n"
" - Cherry-pick Merge Example -\n"
"\n"
" A bug has been fixed on trunk in revision 50. This fix needs to\n"
" be merged from trunk onto the release branch.\n"
"\n"
" 1.x-release +-----------------------o-----\n"
" / ^\n"
" / |\n"
" / |\n"
" trunk ------+--------------------------LR-----\n"
" r50\n"
"\n"
" In the above diagram, L marks the left side (trunk@49) and R marks the\n"
" right side (trunk@50) of the merge. The difference between the left\n"
" and right side is applied to the target working copy path.\n"
"\n"
" Note that the difference between revision 49 and 50 is exactly those\n"
" changes that were committed in revision 50, not including changes\n"
" committed in revision 49.\n"
"\n"
" To perform the merge, have a clean working copy of the release branch\n"
" and run the following command in its top-level directory; remember\n"
" that the default target is '.':\n"
"\n"
" svn merge -c50 ^/trunk\n"
"\n"
" You can also cherry-pick several revisions and/or revision ranges:\n"
"\n"
" svn merge -c50,54,60 -r65:68 ^/trunk\n"
"\n"
"\n"
" 3. This form is called a '2-URL merge':\n"
"\n"
" svn merge SOURCE1[@REV1] SOURCE2[@REV2] [TARGET_WCPATH]\n"
"\n"
" You should use this merge variant only if the other variants do not\n"
" apply to your situation, as this variant can be quite complex to\n"
" master.\n"
"\n"
" Two source URLs are specified, identifying two trees on the same\n"
" branch or on different branches. The trees are compared and the\n"
" difference from SOURCE1@REV1 to SOURCE2@REV2 is applied to the\n"
" working copy of the target branch at TARGET_WCPATH. The target\n"
" branch may be the same as one or both sources, or different again.\n"
" The three branches involved can be completely unrelated.\n"
"\n"
" TARGET_WCPATH is a working copy path; if omitted, '.' is generally\n"
" assumed. The special cases noted above in the 'complete' merge form\n"
" also apply here.\n"
"\n"
" SOURCE1 and/or SOURCE2 can also be specified as a working copy path,\n"
" in which case the merge source URL is derived from the working copy.\n"
"\n"
" - 2-URL Merge Example -\n"
"\n"
" Two features have been developed on separate branches called 'foo' and\n"
" 'bar'. It has since become clear that 'bar' should be combined with\n"
" the 'foo' branch for further development before reintegration.\n"
"\n"
" Although both feature branches originate from trunk, they are not\n"
" directly related -- one is not a direct copy of the other. A 2-URL\n"
" merge is necessary.\n"
"\n"
" The 'bar' branch has been synced with trunk up to revision 500.\n"
" (If this revision number is not known, it can be located using the\n"
" 'svn log' and/or 'svn mergeinfo' commands.)\n"
" The difference between trunk@500 and bar@HEAD contains the complete\n"
" set of changes related to feature 'bar', and no other changes. These\n"
" changes are applied to the 'foo' branch.\n"
"\n"
" foo +-----------------------------------o\n"
" / ^\n"
" / /\n"
" / r500 /\n"
" trunk ------+------+-----------------L---------> /\n"
" \\ . /\n"
" \\ ............ /\n"
" \\ . /\n"
" bar +-----------------------------------R\n"
"\n"
" In the diagram above, L marks the left side (trunk@500) and R marks\n"
" the right side (bar@HEAD) of the merge. The difference between the\n"
" left and right side is applied to the target working copy path, in\n"
" this case a working copy of the 'foo' branch.\n"
"\n"
" To perform the merge, have a clean working copy of the 'foo' branch\n"
" and run the following command in its top-level directory:\n"
"\n"
" svn merge ^/trunk@500 ^/bar\n"
"\n"
" The exact changes applied by a 2-URL merge can be previewed with svn's\n"
" diff command, which is a good idea to verify if you do not have the\n"
" luxury of a clean working copy to merge to. In this case:\n"
"\n"
" svn diff ^/trunk@500 ^/bar@HEAD\n"
"\n"
"\n"
" The following applies to all types of merges:\n"
"\n"
" To prevent unnecessary merge conflicts, svn merge requires that\n"
" TARGET_WCPATH is not a mixed-revision working copy. Running 'svn update'\n"
" before starting a merge ensures that all items in the working copy are\n"
" based on the same revision.\n"
"\n"
" If possible, you should have no local modifications in the merge's target\n"
" working copy prior to the merge, to keep things simpler. It will be\n"
" easier to revert the merge and to understand the branch's history.\n"
"\n"
" Switched sub-paths should also be avoided during merging, as they may\n"
" cause incomplete merges and create subtree mergeinfo.\n"
"\n"
" For each merged item a line will be printed with characters reporting the\n"
" action taken. These characters have the following meaning:\n"
"\n"
" A Added\n"
" D Deleted\n"
" U Updated\n"
" C Conflict\n"
" G Merged\n"
" E Existed\n"
" R Replaced\n"
"\n"
" Characters in the first column report about the item itself.\n"
" Characters in the second column report about properties of the item.\n"
" A 'C' in the third column indicates a tree conflict, while a 'C' in\n"
" the first and second columns indicate textual conflicts in files\n"
" and in property values, respectively.\n"
"\n"
" - Merge Tracking -\n"
"\n"
" Subversion uses the svn:mergeinfo property to track merge history. This\n"
" property is considered at the start of a merge to determine what to merge\n"
" and it is updated at the conclusion of the merge to describe the merge\n"
" that took place. Mergeinfo is used only if the two sources are on the\n"
" same line of history -- if the first source is an ancestor of the second,\n"
" or vice-versa (i.e. if one has originally been created by copying the\n"
" other). This is verified and enforced when using sync merges and\n"
" reintegrate merges.\n"
"\n"
" The --ignore-ancestry option prevents merge tracking and thus ignores\n"
" mergeinfo, neither considering it nor recording it.\n"
"\n"
" - Merging from foreign repositories -\n"
"\n"
" Subversion does support merging from foreign repositories.\n"
" While all merge source URLs must point to the same repository, the merge\n"
" target working copy may come from a different repository than the source.\n"
" However, there are some caveats. Most notably, copies made in the\n"
" merge source will be transformed into plain additions in the merge\n"
" target. Also, merge-tracking is not supported for merges from foreign\n"
" repositories.\n"),
{'r', 'c', 'N', opt_depth, 'q', opt_force, opt_dry_run, opt_merge_cmd,
opt_record_only, 'x', opt_ignore_ancestry, opt_accept, opt_reintegrate,
opt_allow_mixed_revisions, 'v'} },
{ "mergeinfo", svn_cl__mergeinfo, {0}, N_
("Display merge-related information.\n"
"usage: 1. mergeinfo SOURCE[@REV] [TARGET[@REV]]\n"
" 2. mergeinfo --show-revs=WHICH SOURCE[@REV] [TARGET[@REV]]\n"
"\n"
" 1. Summarize the history of merging between SOURCE and TARGET. The graph\n"
" shows, from left to right:\n"
" the youngest common ancestor of the branches;\n"
" the latest full merge in either direction, and thus the common base\n"
" that will be used for the next complete merge;\n"
" the repository path and revision number of the tip of each branch.\n"
"\n"
" 2. Print the revision numbers on SOURCE that have been merged to TARGET\n"
" (with --show-revs=merged), or that have not been merged to TARGET\n"
" (with --show-revs=eligible). Print only revisions in which there was\n"
" at least one change in SOURCE.\n"
"\n"
" If --revision (-r) is provided, filter the displayed information to\n"
" show only that which is associated with the revisions within the\n"
" specified range. Revision numbers, dates, and the 'HEAD' keyword are\n"
" valid range values.\n"
"\n"
" SOURCE and TARGET are the source and target branch URLs, respectively.\n"
" (If a WC path is given, the corresponding base URL is used.) The default\n"
" TARGET is the current working directory ('.'). REV specifies the revision\n"
" to be considered the tip of the branch; the default for SOURCE is HEAD,\n"
" and the default for TARGET is HEAD for a URL or BASE for a WC path.\n"
"\n"
" The depth can be 'empty' or 'infinity'; the default is 'empty'.\n"),
{'r', 'R', opt_depth, opt_show_revs} },
{ "mkdir", svn_cl__mkdir, {0}, N_
("Create a new directory under version control.\n"
"usage: 1. mkdir PATH...\n"
" 2. mkdir URL...\n"
"\n"
" Create version controlled directories.\n"
"\n"
" 1. Each directory specified by a working copy PATH is created locally\n"
" and scheduled for addition upon the next commit.\n"
"\n"
" 2. Each directory specified by a URL is created in the repository via\n"
" an immediate commit.\n"
"\n"
" In both cases, all the intermediate directories must already exist,\n"
" unless the --parents option is given.\n"),
{'q', opt_parents, SVN_CL__LOG_MSG_OPTIONS} },
{ "move", svn_cl__move, {"mv", "rename", "ren"}, N_
("Move (rename) an item in a working copy or repository.\n"
"usage: move SRC... DST\n"
"\n"
" SRC and DST can both be working copy (WC) paths or URLs:\n"
" WC -> WC: move an item in a working copy, as a local change to\n"
" be committed later (with or without further changes)\n"
" URL -> URL: move an item in the repository directly, immediately\n"
" creating a new revision in the repository\n"
" All the SRCs must be of the same type. When moving multiple sources,\n"
" they will be added as children of DST, which must be a directory.\n"
"\n"
" SRC and DST of WC -> WC moves must be committed in the same revision.\n"
" Furthermore, WC -> WC moves will refuse to move a mixed-revision subtree.\n"
" To avoid unnecessary conflicts, it is recommended to run 'svn update'\n"
" to update the subtree to a single revision before moving it.\n"
" The --allow-mixed-revisions option is provided for backward compatibility.\n"
"\n"
" The --revision option has no use and is deprecated.\n"),
{'r', 'q', opt_force, opt_parents, opt_allow_mixed_revisions,
SVN_CL__LOG_MSG_OPTIONS} },
{ "patch", svn_cl__patch, {0}, N_
("Apply a patch to a working copy.\n"
"usage: patch PATCHFILE [WCPATH]\n"
"\n"
" Apply a unidiff patch in PATCHFILE to the working copy WCPATH.\n"
" If WCPATH is omitted, '.' is assumed.\n"
"\n"
" A unidiff patch suitable for application to a working copy can be\n"
" produced with the 'svn diff' command or third-party diffing tools.\n"
" Any non-unidiff content of PATCHFILE is ignored, except for Subversion\n"
" property diffs as produced by 'svn diff'.\n"
"\n"
" Changes listed in the patch will either be applied or rejected.\n"
" If a change does not match at its exact line offset, it may be applied\n"
" earlier or later in the file if a match is found elsewhere for the\n"
" surrounding lines of context provided by the patch.\n"
" A change may also be applied with fuzz, which means that one\n"
" or more lines of context are ignored when matching the change.\n"
" If no matching context can be found for a change, the change conflicts\n"
" and will be written to a reject file with the extension .svnpatch.rej.\n"
"\n"
" For each patched file a line will be printed with characters reporting\n"
" the action taken. These characters have the following meaning:\n"
"\n"
" A Added\n"
" D Deleted\n"
" U Updated\n"
" C Conflict\n"
" G Merged (with local uncommitted changes)\n"
"\n"
" Changes applied with an offset or fuzz are reported on lines starting\n"
" with the '>' symbol. You should review such changes carefully.\n"
"\n"
" If the patch removes all content from a file, that file is scheduled\n"
" for deletion. If the patch creates a new file, that file is scheduled\n"
" for addition. Use 'svn revert' to undo deletions and additions you\n"
" do not agree with.\n"
"\n"
" Hint: If the patch file was created with Subversion, it will contain\n"
" the number of a revision N the patch will cleanly apply to\n"
" (look for lines like '--- foo/bar.txt (revision N)').\n"
" To avoid rejects, first update to the revision N using\n"
" 'svn update -r N', apply the patch, and then update back to the\n"
" HEAD revision. This way, conflicts can be resolved interactively.\n"
),
{'q', opt_dry_run, opt_strip, opt_reverse_diff,
opt_ignore_whitespace} },
{ "propdel", svn_cl__propdel, {"pdel", "pd"}, N_
("Remove a property from files, dirs, or revisions.\n"
"usage: 1. propdel PROPNAME [PATH...]\n"
" 2. propdel PROPNAME --revprop -r REV [TARGET]\n"
"\n"
" 1. Removes versioned props in working copy.\n"
" 2. Removes unversioned remote prop on repos revision.\n"
" TARGET only determines which repository to access.\n"),
{'q', 'R', opt_depth, 'r', opt_revprop, opt_changelist} },
{ "propedit", svn_cl__propedit, {"pedit", "pe"}, N_
("Edit a property with an external editor.\n"
"usage: 1. propedit PROPNAME TARGET...\n"
" 2. propedit PROPNAME --revprop -r REV [TARGET]\n"
"\n"
" 1. Edits versioned prop in working copy or repository.\n"
" 2. Edits unversioned remote prop on repos revision.\n"
" TARGET only determines which repository to access.\n"
"\n"
" See 'svn help propset' for more on setting properties.\n"),
{'r', opt_revprop, SVN_CL__LOG_MSG_OPTIONS, opt_force} },
{ "propget", svn_cl__propget, {"pget", "pg"}, N_
("Print the value of a property on files, dirs, or revisions.\n"
"usage: 1. propget PROPNAME [TARGET[@REV]...]\n"
" 2. propget PROPNAME --revprop -r REV [TARGET]\n"
"\n"
" 1. Prints versioned props. If specified, REV determines in which\n"
" revision the target is first looked up.\n"
" 2. Prints unversioned remote prop on repos revision.\n"
" TARGET only determines which repository to access.\n"
"\n"
" With --verbose, the target path and the property name are printed on\n"
" separate lines before each value, like 'svn proplist --verbose'.\n"
" Otherwise, if there is more than one TARGET or a depth other than\n"
" 'empty', the target path is printed on the same line before each value.\n"
"\n"
" By default, an extra newline is printed after the property value so that\n"
" the output looks pretty. With a single TARGET and depth 'empty', you can\n"
" use the --strict option to disable this (useful when redirecting a binary\n"
" property value to a file, for example).\n"),
{'v', 'R', opt_depth, 'r', opt_revprop, opt_strict, opt_xml,
opt_changelist, opt_show_inherited_props },
{{'v', N_("print path, name and value on separate lines")},
{opt_strict, N_("don't print an extra newline")}} },
{ "proplist", svn_cl__proplist, {"plist", "pl"}, N_
("List all properties on files, dirs, or revisions.\n"
"usage: 1. proplist [TARGET[@REV]...]\n"
" 2. proplist --revprop -r REV [TARGET]\n"
"\n"
" 1. Lists versioned props. If specified, REV determines in which\n"
" revision the target is first looked up.\n"
" 2. Lists unversioned remote props on repos revision.\n"
" TARGET only determines which repository to access.\n"
"\n"
" With --verbose, the property values are printed as well, like 'svn propget\n"
" --verbose'. With --quiet, the paths are not printed.\n"),
{'v', 'R', opt_depth, 'r', 'q', opt_revprop, opt_xml, opt_changelist,
opt_show_inherited_props },
{{'v', N_("print path, name and value on separate lines")},
{'q', N_("don't print the path")}} },
{ "propset", svn_cl__propset, {"pset", "ps"}, N_
("Set the value of a property on files, dirs, or revisions.\n"
"usage: 1. propset PROPNAME PROPVAL PATH...\n"
" 2. propset PROPNAME --revprop -r REV PROPVAL [TARGET]\n"
"\n"
" 1. Changes a versioned file or directory property in a working copy.\n"
" 2. Changes an unversioned property on a repository revision.\n"
" (TARGET only determines which repository to access.)\n"
"\n"
" The value may be provided with the --file option instead of PROPVAL.\n"
"\n"
" Property names starting with 'svn:' are reserved. Subversion recognizes\n"
" the following special versioned properties on a file:\n"
" svn:keywords - Keywords to be expanded. Valid keywords are:\n"
" URL, HeadURL - The URL for the head version of the file.\n"
" Author, LastChangedBy - The last person to modify the file.\n"
" Date, LastChangedDate - The date/time the file was last modified.\n"
" Rev, Revision, - The last revision the file changed.\n"
" LastChangedRevision\n"
" Id - A compressed summary of the previous four.\n"
" Header - Similar to Id but includes the full URL.\n"
"\n"
" Custom keywords can be defined with a format string separated from\n"
" the keyword name with '='. Valid format substitutions are:\n"
" %a - The author of the revision given by %r.\n"
" %b - The basename of the URL of the file.\n"
" %d - Short format of the date of the revision given by %r.\n"
" %D - Long format of the date of the revision given by %r.\n"
" %P - The file's path, relative to the repository root.\n"
" %r - The number of the revision which last changed the file.\n"
" %R - The URL to the root of the repository.\n"
" %u - The URL of the file.\n"
" %_ - A space (keyword definitions cannot contain a literal space).\n"
" %% - A literal '%'.\n"
" %H - Equivalent to %P%_%r%_%d%_%a.\n"
" %I - Equivalent to %b%_%r%_%d%_%a.\n"
" Example custom keyword definition: MyKeyword=%r%_%a%_%P\n"
" Once a custom keyword has been defined for a file, it can be used\n"
" within the file like any other keyword: $MyKeyword$\n"
"\n"
" svn:executable - If present, make the file executable. Use\n"
" 'svn propdel svn:executable PATH...' to clear.\n"
" svn:eol-style - One of 'native', 'LF', 'CR', 'CRLF'.\n"
" svn:mime-type - The mimetype of the file. Used to determine\n"
" whether to merge the file, and how to serve it from Apache.\n"
" A mimetype beginning with 'text/' (or an absent mimetype) is\n"
" treated as text. Anything else is treated as binary.\n"
" svn:needs-lock - If present, indicates that the file should be locked\n"
" before it is modified. Makes the working copy file read-only\n"
" when it is not locked. Use 'svn propdel svn:needs-lock PATH...'\n"
" to clear.\n"
"\n"
" Subversion recognizes the following special versioned properties on a\n"
" directory:\n"
" svn:ignore - A list of file glob patterns to ignore, one per line.\n"
" svn:global-ignores - Like svn:ignore, but inheritable.\n"
+ " svn:auto-props - Automatically set properties on files when they are\n"
+ " added or imported. Contains key-value pairs, one per line, in the format:\n"
+ " PATTERN = PROPNAME=VALUE[;PROPNAME=VALUE ...]\n"
+ " Example (where a literal ';' is escaped by adding another ';'):\n"
+ " *.html = svn:eol-style=native;svn:mime-type=text/html;; charset=UTF8\n"
+ " Applies recursively to all files added or imported under the directory\n"
+ " it is set on. See also [auto-props] in the client configuration file.\n"
" svn:externals - A list of module specifiers, one per line, in the\n"
" following format similar to the syntax of 'svn checkout':\n"
" [-r REV] URL[@PEG] LOCALPATH\n"
" Example:\n"
" http://example.com/repos/zig foo/bar\n"
" The LOCALPATH is relative to the directory having this property.\n"
" To pin the external to a known revision, specify the optional REV:\n"
" -r25 http://example.com/repos/zig foo/bar\n"
" To unambiguously identify an element at a path which may have been\n"
" subsequently deleted or renamed, specify the optional PEG revision:\n"
" -r25 http://example.com/repos/zig@42 foo/bar\n"
" The URL may be a full URL or a relative URL starting with one of:\n"
" ../ to the parent directory of the extracted external\n"
" ^/ to the repository root\n"
" / to the server root\n"
" // to the URL scheme\n"
" Use of the following format is discouraged but is supported for\n"
" interoperability with Subversion 1.4 and earlier clients:\n"
" LOCALPATH [-r PEG] URL\n"
" The ambiguous format 'relative_path relative_path' is taken as\n"
" 'relative_url relative_path' with peg revision support.\n"
" Lines starting with a '#' character are ignored.\n"),
{'F', opt_encoding, 'q', 'r', opt_targets, 'R', opt_depth, opt_revprop,
opt_force, opt_changelist },
{{'F', N_("read property value from file ARG")}} },
{ "relocate", svn_cl__relocate, {0}, N_
("Relocate the working copy to point to a different repository root URL.\n"
"usage: 1. relocate FROM-PREFIX TO-PREFIX [PATH...]\n"
" 2. relocate TO-URL [PATH]\n"
"\n"
" Rewrite working copy URL metadata to reflect a syntactic change only.\n"
" This is used when a repository's root URL changes (such as a scheme\n"
" or hostname change) but your working copy still reflects the same\n"
" directory within the same repository.\n"
"\n"
" 1. FROM-PREFIX and TO-PREFIX are initial substrings of the working\n"
" copy's current and new URLs, respectively. (You may specify the\n"
" complete old and new URLs if you wish.) Use 'svn info' to determine\n"
" the current working copy URL.\n"
"\n"
" 2. TO-URL is the (complete) new repository URL to use for PATH.\n"
"\n"
" Examples:\n"
" svn relocate http:// svn:// project1 project2\n"
" svn relocate http://www.example.com/repo/project \\\n"
" svn://svn.example.com/repo/project\n"),
{opt_ignore_externals} },
{ "resolve", svn_cl__resolve, {0}, N_
("Resolve conflicts on working copy files or directories.\n"
"usage: resolve [PATH...]\n"
"\n"
" By default, perform interactive conflict resolution on PATH.\n"
" In this mode, the command is recursive by default (depth 'infinity').\n"
"\n"
" The --accept=ARG option prevents interactive prompting and forces\n"
" conflicts on PATH to be resolved in the manner specified by ARG.\n"
" In this mode, the command is not recursive by default (depth 'empty').\n"),
{opt_targets, 'R', opt_depth, 'q', opt_accept},
{{opt_accept, N_("specify automatic conflict resolution source\n"
" "
"('base', 'working', 'mine-conflict',\n"
" "
"'theirs-conflict', 'mine-full', 'theirs-full')")}} },
{ "resolved", svn_cl__resolved, {0}, N_
("Remove 'conflicted' state on working copy files or directories.\n"
"usage: resolved PATH...\n"
"\n"
" Note: this subcommand does not semantically resolve conflicts or\n"
" remove conflict markers; it merely removes the conflict-related\n"
" artifact files and allows PATH to be committed again. It has been\n"
" deprecated in favor of running 'svn resolve --accept working'.\n"),
{opt_targets, 'R', opt_depth, 'q'} },
{ "revert", svn_cl__revert, {0}, N_
("Restore pristine working copy state (undo local changes).\n"
"usage: revert PATH...\n"
"\n"
" Revert changes in the working copy at or within PATH, and remove\n"
" conflict markers as well, if any.\n"
"\n"
" This subcommand does not revert already committed changes.\n"
" For information about undoing already committed changes, search\n"
" the output of 'svn help merge' for 'undo'.\n"),
{opt_targets, 'R', opt_depth, 'q', opt_changelist} },
{ "status", svn_cl__status, {"stat", "st"}, N_
("Print the status of working copy files and directories.\n"
"usage: status [PATH...]\n"
"\n"
" With no args, print only locally modified items (no network access).\n"
" With -q, print only summary information about locally modified items.\n"
" With -u, add working revision and server out-of-date information.\n"
" With -v, print full revision information on every item.\n"
"\n"
" The first seven columns in the output are each one character wide:\n"
" First column: Says if item was added, deleted, or otherwise changed\n"
" ' ' no modifications\n"
" 'A' Added\n"
" 'C' Conflicted\n"
" 'D' Deleted\n"
" 'I' Ignored\n"
" 'M' Modified\n"
" 'R' Replaced\n"
" 'X' an unversioned directory created by an externals definition\n"
" '?' item is not under version control\n"
" '!' item is missing (removed by non-svn command) or incomplete\n"
" '~' versioned item obstructed by some item of a different kind\n"
" Second column: Modifications of a file's or directory's properties\n"
" ' ' no modifications\n"
" 'C' Conflicted\n"
" 'M' Modified\n"
" Third column: Whether the working copy is locked for writing by\n"
" another Subversion client modifying the working copy\n"
" ' ' not locked for writing\n"
" 'L' locked for writing\n"
" Fourth column: Scheduled commit will contain addition-with-history\n"
" ' ' no history scheduled with commit\n"
" '+' history scheduled with commit\n"
" Fifth column: Whether the item is switched or a file external\n"
" ' ' normal\n"
" 'S' the item has a Switched URL relative to the parent\n"
" 'X' a versioned file created by an eXternals definition\n"
" Sixth column: Whether the item is locked in repository for exclusive commit\n"
" (without -u)\n"
" ' ' not locked by this working copy\n"
" 'K' locked by this working copy, but lock might be stolen or broken\n"
" (with -u)\n"
" ' ' not locked in repository, not locked by this working copy\n"
" 'K' locked in repository, lock owned by this working copy\n"
" 'O' locked in repository, lock owned by another working copy\n"
" 'T' locked in repository, lock owned by this working copy was stolen\n"
" 'B' not locked in repository, lock owned by this working copy is broken\n"
" Seventh column: Whether the item is the victim of a tree conflict\n"
" ' ' normal\n"
" 'C' tree-Conflicted\n"
" If the item is a tree conflict victim, an additional line is printed\n"
" after the item's status line, explaining the nature of the conflict.\n"
"\n"
" The out-of-date information appears in the ninth column (with -u):\n"
" '*' a newer revision exists on the server\n"
" ' ' the working copy is up to date\n"
"\n"
" Remaining fields are variable width and delimited by spaces:\n"
" The working revision (with -u or -v; '-' if the item is copied)\n"
" The last committed revision and last committed author (with -v)\n"
" The working copy path is always the final field, so it can\n"
" include spaces.\n"
"\n"
" The presence of a question mark ('?') where a working revision, last\n"
" committed revision, or last committed author was expected indicates\n"
" that the information is unknown or irrelevant given the state of the\n"
" item (for example, when the item is the result of a copy operation).\n"
" The question mark serves as a visual placeholder to facilitate parsing.\n"
"\n"
" Example output:\n"
" svn status wc\n"
" M wc/bar.c\n"
" A + wc/qax.c\n"
"\n"
" svn status -u wc\n"
" M 965 wc/bar.c\n"
" * 965 wc/foo.c\n"
" A + - wc/qax.c\n"
" Status against revision: 981\n"
"\n"
" svn status --show-updates --verbose wc\n"
" M 965 938 kfogel wc/bar.c\n"
" * 965 922 sussman wc/foo.c\n"
" A + - 687 joe wc/qax.c\n"
" 965 687 joe wc/zig.c\n"
" Status against revision: 981\n"
"\n"
" svn status\n"
" M wc/bar.c\n"
" ! C wc/qaz.c\n"
" > local missing, incoming edit upon update\n"
" D wc/qax.c\n"),
{ 'u', 'v', 'N', opt_depth, 'q', opt_no_ignore, opt_incremental, opt_xml,
opt_ignore_externals, opt_changelist},
{{'q', N_("don't print unversioned items")}} },
{ "switch", svn_cl__switch, {"sw"}, N_
("Update the working copy to a different URL within the same repository.\n"
"usage: 1. switch URL[@PEGREV] [PATH]\n"
" 2. switch --relocate FROM-PREFIX TO-PREFIX [PATH...]\n"
"\n"
" 1. Update the working copy to mirror a new URL within the repository.\n"
" This behavior is similar to 'svn update', and is the way to\n"
" move a working copy to a branch or tag within the same repository.\n"
" If specified, PEGREV determines in which revision the target is first\n"
" looked up.\n"
"\n"
" If --force is used, unversioned obstructing paths in the working\n"
" copy do not automatically cause a failure if the switch attempts to\n"
" add the same path. If the obstructing path is the same type (file\n"
" or directory) as the corresponding path in the repository it becomes\n"
" versioned but its contents are left 'as-is' in the working copy.\n"
" This means that an obstructing directory's unversioned children may\n"
" also obstruct and become versioned. For files, any content differences\n"
" between the obstruction and the repository are treated like a local\n"
" modification to the working copy. All properties from the repository\n"
" are applied to the obstructing path.\n"
"\n"
" Use the --set-depth option to set a new working copy depth on the\n"
" targets of this operation.\n"
"\n"
" By default, Subversion will refuse to switch a working copy path to\n"
" a new URL with which it shares no common version control ancestry.\n"
" Use the '--ignore-ancestry' option to override this sanity check.\n"
"\n"
" 2. The '--relocate' option is deprecated. This syntax is equivalent to\n"
" 'svn relocate FROM-PREFIX TO-PREFIX [PATH]'.\n"
"\n"
" See also 'svn help update' for a list of possible characters\n"
" reporting the action taken.\n"
"\n"
" Examples:\n"
" svn switch ^/branches/1.x-release\n"
" svn switch --relocate http:// svn://\n"
" svn switch --relocate http://www.example.com/repo/project \\\n"
" svn://svn.example.com/repo/project\n"),
{ 'r', 'N', opt_depth, opt_set_depth, 'q', opt_merge_cmd, opt_relocate,
opt_ignore_externals, opt_ignore_ancestry, opt_force, opt_accept},
{{opt_ignore_ancestry,
N_("allow switching to a node with no common ancestor")}}
},
{ "unlock", svn_cl__unlock, {0}, N_
("Unlock working copy paths or URLs.\n"
"usage: unlock TARGET...\n"
"\n"
" Use --force to break the lock.\n"),
{ opt_targets, opt_force } },
{ "update", svn_cl__update, {"up"}, N_
("Bring changes from the repository into the working copy.\n"
"usage: update [PATH...]\n"
"\n"
" If no revision is given, bring working copy up-to-date with HEAD rev.\n"
" Else synchronize working copy to revision given by -r.\n"
"\n"
" For each updated item a line will be printed with characters reporting\n"
" the action taken. These characters have the following meaning:\n"
"\n"
" A Added\n"
" D Deleted\n"
" U Updated\n"
" C Conflict\n"
" G Merged\n"
" E Existed\n"
" R Replaced\n"
"\n"
" Characters in the first column report about the item itself.\n"
" Characters in the second column report about properties of the item.\n"
" A 'B' in the third column signifies that the lock for the file has\n"
" been broken or stolen.\n"
" A 'C' in the fourth column indicates a tree conflict, while a 'C' in\n"
" the first and second columns indicate textual conflicts in files\n"
" and in property values, respectively.\n"
"\n"
" If --force is used, unversioned obstructing paths in the working\n"
" copy do not automatically cause a failure if the update attempts to\n"
" add the same path. If the obstructing path is the same type (file\n"
" or directory) as the corresponding path in the repository it becomes\n"
" versioned but its contents are left 'as-is' in the working copy.\n"
" This means that an obstructing directory's unversioned children may\n"
" also obstruct and become versioned. For files, any content differences\n"
" between the obstruction and the repository are treated like a local\n"
" modification to the working copy. All properties from the repository\n"
" are applied to the obstructing path. Obstructing paths are reported\n"
" in the first column with code 'E'.\n"
"\n"
" If the specified update target is missing from the working copy but its\n"
" immediate parent directory is present, checkout the target into its\n"
" parent directory at the specified depth. If --parents is specified,\n"
" create any missing parent directories of the target by checking them\n"
" out, too, at depth=empty.\n"
"\n"
" Use the --set-depth option to set a new working copy depth on the\n"
" targets of this operation.\n"),
{'r', 'N', opt_depth, opt_set_depth, 'q', opt_merge_cmd, opt_force,
opt_ignore_externals, opt_changelist, opt_editor_cmd, opt_accept,
opt_parents} },
{ "upgrade", svn_cl__upgrade, {0}, N_
("Upgrade the metadata storage format for a working copy.\n"
"usage: upgrade [WCPATH...]\n"
"\n"
" Local modifications are preserved.\n"),
{ 'q' } },
{ NULL, NULL, {0}, NULL, {0} }
};
/* Version compatibility check */
static svn_error_t *
check_lib_versions(void)
{
static const svn_version_checklist_t checklist[] =
{
{ "svn_subr", svn_subr_version },
{ "svn_client", svn_client_version },
{ "svn_wc", svn_wc_version },
{ "svn_ra", svn_ra_version },
{ "svn_delta", svn_delta_version },
{ "svn_diff", svn_diff_version },
{ NULL, NULL }
};
SVN_VERSION_DEFINE(my_version);
return svn_ver_check_list2(&my_version, checklist, svn_ver_equal);
}
/* A flag to see if we've been cancelled by the client or not. */
static volatile sig_atomic_t cancelled = FALSE;
/* A signal handler to support cancellation. */
static void
signal_handler(int signum)
{
apr_signal(signum, SIG_IGN);
cancelled = TRUE;
}
/* Our cancellation callback. */
svn_error_t *
svn_cl__check_cancel(void *baton)
{
if (cancelled)
return svn_error_create(SVN_ERR_CANCELLED, NULL, _("Caught signal"));
else
return SVN_NO_ERROR;
}
/* Add a --search argument to OPT_STATE.
* These options start a new search pattern group. */
static void
add_search_pattern_group(svn_cl__opt_state_t *opt_state,
const char *pattern,
apr_pool_t *result_pool)
{
apr_array_header_t *group = NULL;
if (opt_state->search_patterns == NULL)
opt_state->search_patterns = apr_array_make(result_pool, 1,
sizeof(apr_array_header_t *));
group = apr_array_make(result_pool, 1, sizeof(const char *));
APR_ARRAY_PUSH(group, const char *) = pattern;
APR_ARRAY_PUSH(opt_state->search_patterns, apr_array_header_t *) = group;
}
/* Add a --search-and argument to OPT_STATE.
* These patterns are added to an existing pattern group, if any. */
static void
add_search_pattern_to_latest_group(svn_cl__opt_state_t *opt_state,
const char *pattern,
apr_pool_t *result_pool)
{
apr_array_header_t *group;
if (opt_state->search_patterns == NULL)
{
add_search_pattern_group(opt_state, pattern, result_pool);
return;
}
group = APR_ARRAY_IDX(opt_state->search_patterns,
opt_state->search_patterns->nelts - 1,
apr_array_header_t *);
APR_ARRAY_PUSH(group, const char *) = pattern;
}
/*** Main. ***/
/* Report and clear the error ERR, and return EXIT_FAILURE. Suppress the
* error message if it is SVN_ERR_IO_PIPE_WRITE_ERROR. */
#define EXIT_ERROR(err) \
svn_cmdline_handle_exit_error(err, NULL, "svn: ")
/* A redefinition of the public SVN_INT_ERR macro, that suppresses the
* error message if it is SVN_ERR_IO_PIPE_WRITE_ERROR. */
#undef SVN_INT_ERR
#define SVN_INT_ERR(expr) \
do { \
svn_error_t *svn_err__temp = (expr); \
if (svn_err__temp) \
return EXIT_ERROR(svn_err__temp); \
} while (0)
static int
sub_main(int argc, const char *argv[], apr_pool_t *pool)
{
svn_error_t *err;
int opt_id;
apr_getopt_t *os;
svn_cl__opt_state_t opt_state = { 0, { 0 } };
svn_client_ctx_t *ctx;
apr_array_header_t *received_opts;
int i;
const svn_opt_subcommand_desc2_t *subcommand = NULL;
const char *dash_m_arg = NULL, *dash_F_arg = NULL;
svn_cl__cmd_baton_t command_baton;
svn_auth_baton_t *ab;
svn_config_t *cfg_config;
svn_boolean_t descend = TRUE;
svn_boolean_t interactive_conflicts = FALSE;
svn_boolean_t force_interactive = FALSE;
svn_cl__conflict_stats_t *conflict_stats
= svn_cl__conflict_stats_create(pool);
svn_boolean_t use_notifier = TRUE;
svn_boolean_t reading_file_from_stdin = FALSE;
apr_hash_t *changelists;
apr_hash_t *cfg_hash;
received_opts = apr_array_make(pool, SVN_OPT_MAX_OPTIONS, sizeof(int));
/* Check library versions */
SVN_INT_ERR(check_lib_versions());
#if defined(WIN32) || defined(__CYGWIN__)
/* Set the working copy administrative directory name. */
if (getenv("SVN_ASP_DOT_NET_HACK"))
{
SVN_INT_ERR(svn_wc_set_adm_dir("_svn", pool));
}
#endif
/* Initialize the RA library. */
SVN_INT_ERR(svn_ra_initialize(pool));
/* Init our changelists hash. */
changelists = apr_hash_make(pool);
/* Begin processing arguments. */
opt_state.start_revision.kind = svn_opt_revision_unspecified;
opt_state.end_revision.kind = svn_opt_revision_unspecified;
opt_state.revision_ranges =
apr_array_make(pool, 0, sizeof(svn_opt_revision_range_t *));
opt_state.depth = svn_depth_unknown;
opt_state.set_depth = svn_depth_unknown;
opt_state.accept_which = svn_cl__accept_unspecified;
opt_state.show_revs = svn_cl__show_revs_invalid;
/* No args? Show usage. */
if (argc <= 1)
{
SVN_INT_ERR(svn_cl__help(NULL, NULL, pool));
return EXIT_FAILURE;
}
/* Else, parse options. */
SVN_INT_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool));
os->interleave = 1;
while (1)
{
const char *opt_arg;
const char *utf8_opt_arg;
/* Parse the next option. */
apr_status_t apr_err = apr_getopt_long(os, svn_cl__options, &opt_id,
&opt_arg);
if (APR_STATUS_IS_EOF(apr_err))
break;
else if (apr_err)
{
SVN_INT_ERR(svn_cl__help(NULL, NULL, pool));
return EXIT_FAILURE;
}
/* Stash the option code in an array before parsing it. */
APR_ARRAY_PUSH(received_opts, int) = opt_id;
switch (opt_id) {
case 'l':
{
err = svn_cstring_atoi(&opt_state.limit, opt_arg);
if (err)
{
err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, err,
_("Non-numeric limit argument given"));
return EXIT_ERROR(err);
}
if (opt_state.limit <= 0)
{
err = svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
_("Argument to --limit must be positive"));
return EXIT_ERROR(err);
}
}
break;
case 'm':
/* Note that there's no way here to detect if the log message
contains a zero byte -- if it does, then opt_arg will just
be shorter than the user intended. Oh well. */
opt_state.message = apr_pstrdup(pool, opt_arg);
dash_m_arg = opt_arg;
break;
case 'c':
{
apr_array_header_t *change_revs =
svn_cstring_split(opt_arg, ", \n\r\t\v", TRUE, pool);
if (opt_state.old_target)
{
err = svn_error_create
(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("Can't specify -c with --old"));
return EXIT_ERROR(err);
}
for (i = 0; i < change_revs->nelts; i++)
{
char *end;
svn_revnum_t changeno, changeno_end;
const char *change_str =
APR_ARRAY_IDX(change_revs, i, const char *);
const char *s = change_str;
svn_boolean_t is_negative;
/* Check for a leading minus to allow "-c -r42".
* The is_negative flag is used to handle "-c -42" and "-c -r42".
* The "-c r-42" case is handled by strtol() returning a
* negative number. */
is_negative = (*s == '-');
if (is_negative)
s++;
/* Allow any number of 'r's to prefix a revision number. */
while (*s == 'r')
s++;
changeno = changeno_end = strtol(s, &end, 10);
if (end != s && *end == '-')
{
if (changeno < 0 || is_negative)
{
err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR,
NULL,
_("Negative number in range (%s)"
" not supported with -c"),
change_str);
return EXIT_ERROR(err);
}
s = end + 1;
while (*s == 'r')
s++;
changeno_end = strtol(s, &end, 10);
}
if (end == change_str || *end != '\0')
{
err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("Non-numeric change argument (%s) "
"given to -c"), change_str);
return EXIT_ERROR(err);
}
if (changeno == 0)
{
err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("There is no change 0"));
return EXIT_ERROR(err);
}
if (is_negative)
changeno = -changeno;
/* Figure out the range:
-c N -> -r N-1:N
-c -N -> -r N:N-1
-c M-N -> -r M-1:N for M < N
-c M-N -> -r M:N-1 for M > N
-c -M-N -> error (too confusing/no valid use case)
*/
if (changeno > 0)
{
if (changeno <= changeno_end)
changeno--;
else
changeno_end--;
}
else
{
changeno = -changeno;
changeno_end = changeno - 1;
}
opt_state.used_change_arg = TRUE;
APR_ARRAY_PUSH(opt_state.revision_ranges,
svn_opt_revision_range_t *)
= svn_opt__revision_range_from_revnums(changeno, changeno_end,
pool);
}
}
break;
case 'r':
opt_state.used_revision_arg = TRUE;
if (svn_opt_parse_revision_to_range(opt_state.revision_ranges,
opt_arg, pool) != 0)
{
SVN_INT_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool));
err = svn_error_createf
(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("Syntax error in revision argument '%s'"),
utf8_opt_arg);
return EXIT_ERROR(err);
}
break;
case 'v':
opt_state.verbose = TRUE;
break;
case 'u':
opt_state.update = TRUE;
break;
case 'h':
case '?':
opt_state.help = TRUE;
break;
case 'q':
opt_state.quiet = TRUE;
break;
case opt_incremental:
opt_state.incremental = TRUE;
break;
case 'F':
SVN_INT_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool));
SVN_INT_ERR(svn_stringbuf_from_file2(&(opt_state.filedata),
utf8_opt_arg, pool));
reading_file_from_stdin = (strcmp(utf8_opt_arg, "-") == 0);
dash_F_arg = opt_arg;
break;
case opt_targets:
{
svn_stringbuf_t *buffer, *buffer_utf8;
/* We need to convert to UTF-8 now, even before we divide
the targets into an array, because otherwise we wouldn't
know what delimiter to use for svn_cstring_split(). */
SVN_INT_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool));
SVN_INT_ERR(svn_stringbuf_from_file2(&buffer, utf8_opt_arg, pool));
SVN_INT_ERR(svn_utf_stringbuf_to_utf8(&buffer_utf8, buffer, pool));
opt_state.targets = svn_cstring_split(buffer_utf8->data, "\n\r",
TRUE, pool);
}
break;
case opt_force:
opt_state.force = TRUE;
break;
case opt_force_log:
opt_state.force_log = TRUE;
break;
case opt_dry_run:
opt_state.dry_run = TRUE;
break;
case opt_revprop:
opt_state.revprop = TRUE;
break;
case 'R':
opt_state.depth = svn_depth_infinity;
break;
case 'N':
descend = FALSE;
break;
case opt_depth:
err = svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool);
if (err)
return EXIT_ERROR
(svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, err,
_("Error converting depth "
"from locale to UTF-8")));
opt_state.depth = svn_depth_from_word(utf8_opt_arg);
if (opt_state.depth == svn_depth_unknown
|| opt_state.depth == svn_depth_exclude)
{
return EXIT_ERROR
(svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("'%s' is not a valid depth; try "
"'empty', 'files', 'immediates', "
"or 'infinity'"),
utf8_opt_arg));
}
break;
case opt_set_depth:
err = svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool);
if (err)
return EXIT_ERROR
(svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, err,
_("Error converting depth "
"from locale to UTF-8")));
opt_state.set_depth = svn_depth_from_word(utf8_opt_arg);
/* svn_depth_exclude is okay for --set-depth. */
if (opt_state.set_depth == svn_depth_unknown)
{
return EXIT_ERROR
(svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("'%s' is not a valid depth; try "
"'exclude', 'empty', 'files', "
"'immediates', or 'infinity'"),
utf8_opt_arg));
}
break;
case opt_version:
opt_state.version = TRUE;
break;
case opt_auth_username:
SVN_INT_ERR(svn_utf_cstring_to_utf8(&opt_state.auth_username,
opt_arg, pool));
break;
case opt_auth_password:
SVN_INT_ERR(svn_utf_cstring_to_utf8(&opt_state.auth_password,
opt_arg, pool));
break;
case opt_encoding:
opt_state.encoding = apr_pstrdup(pool, opt_arg);
break;
case opt_xml:
opt_state.xml = TRUE;
break;
case opt_stop_on_copy:
opt_state.stop_on_copy = TRUE;
break;
case opt_strict:
opt_state.strict = TRUE;
break;
case opt_no_ignore:
opt_state.no_ignore = TRUE;
break;
case opt_no_auth_cache:
opt_state.no_auth_cache = TRUE;
break;
case opt_non_interactive:
opt_state.non_interactive = TRUE;
break;
case opt_force_interactive:
force_interactive = TRUE;
break;
case opt_trust_server_cert:
opt_state.trust_server_cert = TRUE;
break;
case opt_no_diff_added:
opt_state.diff.no_diff_added = TRUE;
break;
case opt_no_diff_deleted:
opt_state.diff.no_diff_deleted = TRUE;
break;
case opt_ignore_properties:
opt_state.diff.ignore_properties = TRUE;
break;
case opt_show_copies_as_adds:
opt_state.diff.show_copies_as_adds = TRUE;
break;
case opt_notice_ancestry:
opt_state.diff.notice_ancestry = TRUE;
break;
case opt_ignore_ancestry:
opt_state.ignore_ancestry = TRUE;
break;
case opt_ignore_externals:
opt_state.ignore_externals = TRUE;
break;
case opt_relocate:
opt_state.relocate = TRUE;
break;
case 'x':
SVN_INT_ERR(svn_utf_cstring_to_utf8(&opt_state.extensions,
opt_arg, pool));
break;
case opt_diff_cmd:
opt_state.diff.diff_cmd = apr_pstrdup(pool, opt_arg);
break;
case opt_merge_cmd:
opt_state.merge_cmd = apr_pstrdup(pool, opt_arg);
break;
case opt_record_only:
opt_state.record_only = TRUE;
break;
case opt_editor_cmd:
opt_state.editor_cmd = apr_pstrdup(pool, opt_arg);
break;
case opt_old_cmd:
if (opt_state.used_change_arg)
{
err = svn_error_create
(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("Can't specify -c with --old"));
return EXIT_ERROR(err);
}
opt_state.old_target = apr_pstrdup(pool, opt_arg);
break;
case opt_new_cmd:
opt_state.new_target = apr_pstrdup(pool, opt_arg);
break;
case opt_config_dir:
{
const char *path_utf8;
SVN_INT_ERR(svn_utf_cstring_to_utf8(&path_utf8, opt_arg, pool));
opt_state.config_dir = svn_dirent_internal_style(path_utf8, pool);
}
break;
case opt_config_options:
if (!opt_state.config_options)
opt_state.config_options =
apr_array_make(pool, 1,
sizeof(svn_cmdline__config_argument_t*));
SVN_INT_ERR(svn_utf_cstring_to_utf8(&opt_arg, opt_arg, pool));
SVN_INT_ERR(svn_cmdline__parse_config_option(opt_state.config_options,
opt_arg, pool));
break;
case opt_autoprops:
opt_state.autoprops = TRUE;
break;
case opt_no_autoprops:
opt_state.no_autoprops = TRUE;
break;
case opt_native_eol:
if ( !strcmp("LF", opt_arg) || !strcmp("CR", opt_arg) ||
!strcmp("CRLF", opt_arg))
opt_state.native_eol = apr_pstrdup(pool, opt_arg);
else
{
SVN_INT_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool));
err = svn_error_createf
(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("Syntax error in native-eol argument '%s'"),
utf8_opt_arg);
return EXIT_ERROR(err);
}
break;
case opt_no_unlock:
opt_state.no_unlock = TRUE;
break;
case opt_summarize:
opt_state.diff.summarize = TRUE;
break;
case opt_remove:
opt_state.remove = TRUE;
break;
case opt_changelist:
SVN_INT_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool));
opt_state.changelist = utf8_opt_arg;
if (opt_state.changelist[0] == '\0')
{
err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("Changelist names must not be empty"));
return EXIT_ERROR(err);
}
svn_hash_sets(changelists, opt_state.changelist, (void *)1);
break;
case opt_keep_changelists:
opt_state.keep_changelists = TRUE;
break;
case opt_keep_local:
opt_state.keep_local = TRUE;
break;
case opt_with_all_revprops:
/* If --with-all-revprops is specified along with one or more
* --with-revprops options, --with-all-revprops takes precedence. */
opt_state.all_revprops = TRUE;
break;
case opt_with_no_revprops:
opt_state.no_revprops = TRUE;
break;
case opt_with_revprop:
SVN_INT_ERR(svn_opt_parse_revprop(&opt_state.revprop_table,
opt_arg, pool));
break;
case opt_parents:
opt_state.parents = TRUE;
break;
case 'g':
opt_state.use_merge_history = TRUE;
break;
case opt_accept:
opt_state.accept_which = svn_cl__accept_from_word(opt_arg);
if (opt_state.accept_which == svn_cl__accept_invalid)
return EXIT_ERROR
(svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("'%s' is not a valid --accept value"),
opt_arg));
break;
case opt_show_revs:
opt_state.show_revs = svn_cl__show_revs_from_word(opt_arg);
if (opt_state.show_revs == svn_cl__show_revs_invalid)
return EXIT_ERROR
(svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("'%s' is not a valid --show-revs value"),
opt_arg));
break;
case opt_reintegrate:
opt_state.reintegrate = TRUE;
break;
case opt_strip:
{
err = svn_cstring_atoi(&opt_state.strip, opt_arg);
if (err)
{
err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, err,
_("Invalid strip count '%s'"), opt_arg);
return EXIT_ERROR(err);
}
if (opt_state.strip < 0)
{
err = svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
_("Argument to --strip must be positive"));
return EXIT_ERROR(err);
}
}
break;
case opt_ignore_keywords:
opt_state.ignore_keywords = TRUE;
break;
case opt_reverse_diff:
opt_state.reverse_diff = TRUE;
break;
case opt_ignore_whitespace:
opt_state.ignore_whitespace = TRUE;
break;
case opt_diff:
opt_state.show_diff = TRUE;
break;
case opt_internal_diff:
opt_state.diff.internal_diff = TRUE;
break;
case opt_patch_compatible:
opt_state.diff.patch_compatible = TRUE;
break;
case opt_use_git_diff_format:
opt_state.diff.use_git_diff_format = TRUE;
break;
case opt_allow_mixed_revisions:
opt_state.allow_mixed_rev = TRUE;
break;
case opt_include_externals:
opt_state.include_externals = TRUE;
break;
case opt_show_inherited_props:
opt_state.show_inherited_props = TRUE;
break;
case opt_properties_only:
opt_state.diff.properties_only = TRUE;
break;
case opt_search:
add_search_pattern_group(&opt_state, opt_arg, pool);
break;
case opt_search_and:
add_search_pattern_to_latest_group(&opt_state, opt_arg, pool);
default:
/* Hmmm. Perhaps this would be a good place to squirrel away
opts that commands like svn diff might need. Hmmm indeed. */
break;
}
}
/* The --non-interactive and --force-interactive options are mutually
* exclusive. */
if (opt_state.non_interactive && force_interactive)
{
err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("--non-interactive and --force-interactive "
"are mutually exclusive"));
return EXIT_ERROR(err);
}
else
opt_state.non_interactive = !svn_cmdline__be_interactive(
opt_state.non_interactive,
force_interactive);
/* Turn our hash of changelists into an array of unique ones. */
SVN_INT_ERR(svn_hash_keys(&(opt_state.changelists), changelists, pool));
/* ### This really belongs in libsvn_client. The trouble is,
there's no one place there to run it from, no
svn_client_init(). We'd have to add it to all the public
functions that a client might call. It's unmaintainable to do
initialization from within libsvn_client itself, but it seems
burdensome to demand that all clients call svn_client_init()
before calling any other libsvn_client function... On the other
hand, the alternative is effectively to demand that they call
svn_config_ensure() instead, so maybe we should have a generic
init function anyway. Thoughts? */
SVN_INT_ERR(svn_config_ensure(opt_state.config_dir, pool));
/* If the user asked for help, then the rest of the arguments are
the names of subcommands to get help on (if any), or else they're
just typos/mistakes. Whatever the case, the subcommand to
actually run is svn_cl__help(). */
if (opt_state.help)
subcommand = svn_opt_get_canonical_subcommand2(svn_cl__cmd_table, "help");
/* If we're not running the `help' subcommand, then look for a
subcommand in the first argument. */
if (subcommand == NULL)
{
if (os->ind >= os->argc)
{
if (opt_state.version)
{
/* Use the "help" subcommand to handle the "--version" option. */
static const svn_opt_subcommand_desc2_t pseudo_cmd =
{ "--version", svn_cl__help, {0}, "",
{opt_version, /* must accept its own option */
'q', /* brief output */
'v', /* verbose output */
opt_config_dir /* all commands accept this */
} };
subcommand = &pseudo_cmd;
}
else
{
svn_error_clear
(svn_cmdline_fprintf(stderr, pool,
_("Subcommand argument required\n")));
svn_error_clear(svn_cl__help(NULL, NULL, pool));
return EXIT_FAILURE;
}
}
else
{
const char *first_arg = os->argv[os->ind++];
subcommand = svn_opt_get_canonical_subcommand2(svn_cl__cmd_table,
first_arg);
if (subcommand == NULL)
{
const char *first_arg_utf8;
SVN_INT_ERR(svn_utf_cstring_to_utf8(&first_arg_utf8,
first_arg, pool));
svn_error_clear
(svn_cmdline_fprintf(stderr, pool,
_("Unknown subcommand: '%s'\n"),
first_arg_utf8));
svn_error_clear(svn_cl__help(NULL, NULL, pool));
/* Be kind to people who try 'svn undo'. */
if (strcmp(first_arg_utf8, "undo") == 0)
{
svn_error_clear
(svn_cmdline_fprintf(stderr, pool,
_("Undo is done using either the "
"'svn revert' or the 'svn merge' "
"command.\n")));
}
return EXIT_FAILURE;
}
}
}
/* Check that the subcommand wasn't passed any inappropriate options. */
for (i = 0; i < received_opts->nelts; i++)
{
opt_id = APR_ARRAY_IDX(received_opts, i, int);
/* All commands implicitly accept --help, so just skip over this
when we see it. Note that we don't want to include this option
in their "accepted options" list because it would be awfully
redundant to display it in every commands' help text. */
if (opt_id == 'h' || opt_id == '?')
continue;
if (! svn_opt_subcommand_takes_option3(subcommand, opt_id,
svn_cl__global_options))
{
const char *optstr;
const apr_getopt_option_t *badopt =
svn_opt_get_option_from_code2(opt_id, svn_cl__options,
subcommand, pool);
svn_opt_format_option(&optstr, badopt, FALSE, pool);
if (subcommand->name[0] == '-')
svn_error_clear(svn_cl__help(NULL, NULL, pool));
else
svn_error_clear
(svn_cmdline_fprintf
(stderr, pool, _("Subcommand '%s' doesn't accept option '%s'\n"
"Type 'svn help %s' for usage.\n"),
subcommand->name, optstr, subcommand->name));
return EXIT_FAILURE;
}
}
/* Only merge and log support multiple revisions/revision ranges. */
if (subcommand->cmd_func != svn_cl__merge
&& subcommand->cmd_func != svn_cl__log)
{
if (opt_state.revision_ranges->nelts > 1)
{
err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("Multiple revision arguments "
"encountered; can't specify -c twice, "
"or both -c and -r"));
return EXIT_ERROR(err);
}
}
/* Disallow simultaneous use of both --depth and --set-depth. */
if ((opt_state.depth != svn_depth_unknown)
&& (opt_state.set_depth != svn_depth_unknown))
{
err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("--depth and --set-depth are mutually "
"exclusive"));
return EXIT_ERROR(err);
}
/* Disallow simultaneous use of both --with-all-revprops and
--with-no-revprops. */
if (opt_state.all_revprops && opt_state.no_revprops)
{
err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("--with-all-revprops and --with-no-revprops "
"are mutually exclusive"));
return EXIT_ERROR(err);
}
/* Disallow simultaneous use of both --with-revprop and
--with-no-revprops. */
if (opt_state.revprop_table && opt_state.no_revprops)
{
err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("--with-revprop and --with-no-revprops "
"are mutually exclusive"));
return EXIT_ERROR(err);
}
/* Disallow simultaneous use of both -m and -F, when they are
both used to pass a commit message or lock comment. ('propset'
takes the property value, not a commit message, from -F.)
*/
if (opt_state.filedata && opt_state.message
&& subcommand->cmd_func != svn_cl__propset)
{
err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("--message (-m) and --file (-F) "
"are mutually exclusive"));
return EXIT_ERROR(err);
}
/* --trust-server-cert can only be used with --non-interactive */
if (opt_state.trust_server_cert && !opt_state.non_interactive)
{
err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("--trust-server-cert requires "
"--non-interactive"));
return EXIT_ERROR(err);
}
/* Disallow simultaneous use of both --diff-cmd and
--internal-diff. */
if (opt_state.diff.diff_cmd && opt_state.diff.internal_diff)
{
err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("--diff-cmd and --internal-diff "
"are mutually exclusive"));
return EXIT_ERROR(err);
}
/* Ensure that 'revision_ranges' has at least one item, and make
'start_revision' and 'end_revision' match that item. */
if (opt_state.revision_ranges->nelts == 0)
{
svn_opt_revision_range_t *range = apr_palloc(pool, sizeof(*range));
range->start.kind = svn_opt_revision_unspecified;
range->end.kind = svn_opt_revision_unspecified;
APR_ARRAY_PUSH(opt_state.revision_ranges,
svn_opt_revision_range_t *) = range;
}
opt_state.start_revision = APR_ARRAY_IDX(opt_state.revision_ranges, 0,
svn_opt_revision_range_t *)->start;
opt_state.end_revision = APR_ARRAY_IDX(opt_state.revision_ranges, 0,
svn_opt_revision_range_t *)->end;
err = svn_config_get_config(&cfg_hash, opt_state.config_dir, pool);
if (err)
{
/* Fallback to default config if the config directory isn't readable
or is not a directory. */
if (APR_STATUS_IS_EACCES(err->apr_err)
|| SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))
{
+ svn_config_t *empty_cfg;
+
svn_handle_warning2(stderr, err, "svn: ");
svn_error_clear(err);
- cfg_hash = NULL;
+ cfg_hash = apr_hash_make(pool);
+ SVN_INT_ERR(svn_config_create2(&empty_cfg, FALSE, FALSE, pool));
+ svn_hash_sets(cfg_hash, SVN_CONFIG_CATEGORY_CONFIG, empty_cfg);
+ SVN_INT_ERR(svn_config_create2(&empty_cfg, FALSE, FALSE, pool));
+ svn_hash_sets(cfg_hash, SVN_CONFIG_CATEGORY_SERVERS, empty_cfg);
}
else
return EXIT_ERROR(err);
}
/* Relocation is infinite-depth only. */
if (opt_state.relocate)
{
if (opt_state.depth != svn_depth_unknown)
{
err = svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
_("--relocate and --depth are mutually "
"exclusive"));
return EXIT_ERROR(err);
}
if (! descend)
{
err = svn_error_create(
SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
_("--relocate and --non-recursive (-N) are mutually "
"exclusive"));
return EXIT_ERROR(err);
}
}
/* Only a few commands can accept a revision range; the rest can take at
most one revision number. */
if (subcommand->cmd_func != svn_cl__blame
&& subcommand->cmd_func != svn_cl__diff
&& subcommand->cmd_func != svn_cl__log
&& subcommand->cmd_func != svn_cl__mergeinfo
&& subcommand->cmd_func != svn_cl__merge)
{
if (opt_state.end_revision.kind != svn_opt_revision_unspecified)
{
err = svn_error_create(SVN_ERR_CLIENT_REVISION_RANGE, NULL, NULL);
return EXIT_ERROR(err);
}
}
/* -N has a different meaning depending on the command */
if (!descend)
{
if (subcommand->cmd_func == svn_cl__status)
{
opt_state.depth = svn_depth_immediates;
}
else if (subcommand->cmd_func == svn_cl__revert
|| subcommand->cmd_func == svn_cl__add
|| subcommand->cmd_func == svn_cl__commit)
{
/* In pre-1.5 Subversion, some commands treated -N like
--depth=empty, so force that mapping here. Anyway, with
revert it makes sense to be especially conservative,
since revert can lose data. */
opt_state.depth = svn_depth_empty;
}
else
{
opt_state.depth = svn_depth_files;
}
}
cfg_config = svn_hash_gets(cfg_hash, SVN_CONFIG_CATEGORY_CONFIG);
/* Update the options in the config */
if (opt_state.config_options)
{
svn_error_clear(
svn_cmdline__apply_config_options(cfg_hash,
opt_state.config_options,
"svn: ", "--config-option"));
}
#if !defined(SVN_CL_NO_EXCLUSIVE_LOCK)
{
const char *exclusive_clients_option;
apr_array_header_t *exclusive_clients;
svn_config_get(cfg_config, &exclusive_clients_option,
SVN_CONFIG_SECTION_WORKING_COPY,
SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE_CLIENTS,
NULL);
exclusive_clients = svn_cstring_split(exclusive_clients_option,
" ,", TRUE, pool);
for (i = 0; i < exclusive_clients->nelts; ++i)
{
const char *exclusive_client = APR_ARRAY_IDX(exclusive_clients, i,
const char *);
/* This blocks other clients from accessing the wc.db so it must
be explicitly enabled.*/
if (!strcmp(exclusive_client, "svn"))
svn_config_set(cfg_config,
SVN_CONFIG_SECTION_WORKING_COPY,
SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE,
"true");
}
}
#endif
/* Create a client context object. */
command_baton.opt_state = &opt_state;
SVN_INT_ERR(svn_client_create_context2(&ctx, cfg_hash, pool));
command_baton.ctx = ctx;
/* If we're running a command that could result in a commit, verify
that any log message we were given on the command line makes
sense (unless we've also been instructed not to care). This may
access the working copy so do it after setting the locking mode. */
if ((! opt_state.force_log)
&& (subcommand->cmd_func == svn_cl__commit
|| subcommand->cmd_func == svn_cl__copy
|| subcommand->cmd_func == svn_cl__delete
|| subcommand->cmd_func == svn_cl__import
|| subcommand->cmd_func == svn_cl__mkdir
|| subcommand->cmd_func == svn_cl__move
|| subcommand->cmd_func == svn_cl__lock
|| subcommand->cmd_func == svn_cl__propedit))
{
/* If the -F argument is a file that's under revision control,
that's probably not what the user intended. */
if (dash_F_arg)
{
svn_node_kind_t kind;
const char *local_abspath;
const char *fname_utf8 = svn_dirent_internal_style(dash_F_arg, pool);
err = svn_dirent_get_absolute(&local_abspath, fname_utf8, pool);
if (!err)
{
err = svn_wc_read_kind2(&kind, ctx->wc_ctx, local_abspath, TRUE,
FALSE, pool);
if (!err && kind != svn_node_none && kind != svn_node_unknown)
{
if (subcommand->cmd_func != svn_cl__lock)
{
err = svn_error_create(
SVN_ERR_CL_LOG_MESSAGE_IS_VERSIONED_FILE, NULL,
_("Log message file is a versioned file; "
"use '--force-log' to override"));
}
else
{
err = svn_error_create(
SVN_ERR_CL_LOG_MESSAGE_IS_VERSIONED_FILE, NULL,
_("Lock comment file is a versioned file; "
"use '--force-log' to override"));
}
return EXIT_ERROR(err);
}
}
svn_error_clear(err);
}
/* If the -m argument is a file at all, that's probably not what
the user intended. */
if (dash_m_arg)
{
apr_finfo_t finfo;
if (apr_stat(&finfo, dash_m_arg,
APR_FINFO_MIN, pool) == APR_SUCCESS)
{
if (subcommand->cmd_func != svn_cl__lock)
{
err = svn_error_create
(SVN_ERR_CL_LOG_MESSAGE_IS_PATHNAME, NULL,
_("The log message is a pathname "
"(was -F intended?); use '--force-log' to override"));
}
else
{
err = svn_error_create
(SVN_ERR_CL_LOG_MESSAGE_IS_PATHNAME, NULL,
_("The lock comment is a pathname "
"(was -F intended?); use '--force-log' to override"));
}
return EXIT_ERROR(err);
}
}
}
/* XXX: Only diff_cmd for now, overlay rest later and stop passing
opt_state altogether? */
if (opt_state.diff.diff_cmd)
svn_config_set(cfg_config, SVN_CONFIG_SECTION_HELPERS,
SVN_CONFIG_OPTION_DIFF_CMD, opt_state.diff.diff_cmd);
if (opt_state.merge_cmd)
svn_config_set(cfg_config, SVN_CONFIG_SECTION_HELPERS,
SVN_CONFIG_OPTION_DIFF3_CMD, opt_state.merge_cmd);
if (opt_state.diff.internal_diff)
svn_config_set(cfg_config, SVN_CONFIG_SECTION_HELPERS,
SVN_CONFIG_OPTION_DIFF_CMD, NULL);
/* Check for mutually exclusive args --auto-props and --no-auto-props */
if (opt_state.autoprops && opt_state.no_autoprops)
{
err = svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
_("--auto-props and --no-auto-props are "
"mutually exclusive"));
return EXIT_ERROR(err);
}
/* Update auto-props-enable option, and populate the MIME types map,
for add/import commands */
if (subcommand->cmd_func == svn_cl__add
|| subcommand->cmd_func == svn_cl__import)
{
const char *mimetypes_file;
svn_config_get(cfg_config, &mimetypes_file,
SVN_CONFIG_SECTION_MISCELLANY,
SVN_CONFIG_OPTION_MIMETYPES_FILE, FALSE);
if (mimetypes_file && *mimetypes_file)
{
SVN_INT_ERR(svn_io_parse_mimetypes_file(&(ctx->mimetypes_map),
mimetypes_file, pool));
}
if (opt_state.autoprops)
{
svn_config_set_bool(cfg_config, SVN_CONFIG_SECTION_MISCELLANY,
SVN_CONFIG_OPTION_ENABLE_AUTO_PROPS, TRUE);
}
if (opt_state.no_autoprops)
{
svn_config_set_bool(cfg_config, SVN_CONFIG_SECTION_MISCELLANY,
SVN_CONFIG_OPTION_ENABLE_AUTO_PROPS, FALSE);
}
}
/* Update the 'keep-locks' runtime option */
if (opt_state.no_unlock)
svn_config_set_bool(cfg_config, SVN_CONFIG_SECTION_MISCELLANY,
SVN_CONFIG_OPTION_NO_UNLOCK, TRUE);
/* Set the log message callback function. Note that individual
subcommands will populate the ctx->log_msg_baton3. */
ctx->log_msg_func3 = svn_cl__get_log_message;
/* Set up the notifier.
In general, we use it any time we aren't in --quiet mode. 'svn
status' is unique, though, in that we don't want it in --quiet mode
unless we're also in --verbose mode. When in --xml mode,
though, we never want it. */
if (opt_state.quiet)
use_notifier = FALSE;
if ((subcommand->cmd_func == svn_cl__status) && opt_state.verbose)
use_notifier = TRUE;
if (opt_state.xml)
use_notifier = FALSE;
if (use_notifier)
{
SVN_INT_ERR(svn_cl__get_notifier(&ctx->notify_func2, &ctx->notify_baton2,
conflict_stats, pool));
}
/* Set up our cancellation support. */
ctx->cancel_func = svn_cl__check_cancel;
apr_signal(SIGINT, signal_handler);
#ifdef SIGBREAK
/* SIGBREAK is a Win32 specific signal generated by ctrl-break. */
apr_signal(SIGBREAK, signal_handler);
#endif
#ifdef SIGHUP
apr_signal(SIGHUP, signal_handler);
#endif
#ifdef SIGTERM
apr_signal(SIGTERM, signal_handler);
#endif
#ifdef SIGPIPE
/* Disable SIGPIPE generation for the platforms that have it. */
apr_signal(SIGPIPE, SIG_IGN);
#endif
#ifdef SIGXFSZ
/* Disable SIGXFSZ generation for the platforms that have it, otherwise
* working with large files when compiled against an APR that doesn't have
* large file support will crash the program, which is uncool. */
apr_signal(SIGXFSZ, SIG_IGN);
#endif
/* Set up Authentication stuff. */
SVN_INT_ERR(svn_cmdline_create_auth_baton(&ab,
opt_state.non_interactive,
opt_state.auth_username,
opt_state.auth_password,
opt_state.config_dir,
opt_state.no_auth_cache,
opt_state.trust_server_cert,
cfg_config,
ctx->cancel_func,
ctx->cancel_baton,
pool));
ctx->auth_baton = ab;
if (opt_state.non_interactive)
{
if (opt_state.accept_which == svn_cl__accept_edit)
return EXIT_ERROR(
svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("--accept=%s incompatible with"
" --non-interactive"),
SVN_CL__ACCEPT_EDIT));
if (opt_state.accept_which == svn_cl__accept_launch)
return EXIT_ERROR(
svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("--accept=%s incompatible with"
" --non-interactive"),
SVN_CL__ACCEPT_LAUNCH));
/* The default action when we're non-interactive is to postpone
* conflict resolution. */
if (opt_state.accept_which == svn_cl__accept_unspecified)
opt_state.accept_which = svn_cl__accept_postpone;
}
/* Check whether interactive conflict resolution is disabled by
* the configuration file. If no --accept option was specified
* we postpone all conflicts in this case. */
SVN_INT_ERR(svn_config_get_bool(cfg_config, &interactive_conflicts,
SVN_CONFIG_SECTION_MISCELLANY,
SVN_CONFIG_OPTION_INTERACTIVE_CONFLICTS,
TRUE));
if (!interactive_conflicts)
{
/* Make 'svn resolve' non-interactive. */
if (subcommand->cmd_func == svn_cl__resolve)
opt_state.non_interactive = TRUE;
/* We're not resolving conflicts interactively. If no --accept option
* was provided the default behaviour is to postpone all conflicts. */
if (opt_state.accept_which == svn_cl__accept_unspecified)
opt_state.accept_which = svn_cl__accept_postpone;
}
/* Install the default conflict handler. */
{
svn_cl__interactive_conflict_baton_t *b;
ctx->conflict_func = NULL;
ctx->conflict_baton = NULL;
ctx->conflict_func2 = svn_cl__conflict_func_interactive;
SVN_INT_ERR(svn_cl__get_conflict_func_interactive_baton(
&b,
opt_state.accept_which,
ctx->config, opt_state.editor_cmd, conflict_stats,
ctx->cancel_func, ctx->cancel_baton, pool));
ctx->conflict_baton2 = b;
}
/* And now we finally run the subcommand. */
err = (*subcommand->cmd_func)(os, &command_baton, pool);
if (err)
{
/* For argument-related problems, suggest using the 'help'
subcommand. */
if (err->apr_err == SVN_ERR_CL_INSUFFICIENT_ARGS
|| err->apr_err == SVN_ERR_CL_ARG_PARSING_ERROR)
{
err = svn_error_quick_wrap(
err, apr_psprintf(pool,
_("Try 'svn help %s' for more information"),
subcommand->name));
}
if (err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
{
err = svn_error_quick_wrap(err,
_("Please see the 'svn upgrade' command"));
}
if (err->apr_err == SVN_ERR_AUTHN_FAILED && opt_state.non_interactive)
{
err = svn_error_quick_wrap(err,
_("Authentication failed and interactive"
" prompting is disabled; see the"
" --force-interactive option"));
if (reading_file_from_stdin)
err = svn_error_quick_wrap(err,
_("Reading file from standard input "
"because of -F option; this can "
"interfere with interactive "
"prompting"));
}
/* Tell the user about 'svn cleanup' if any error on the stack
was about locked working copies. */
if (svn_error_find_cause(err, SVN_ERR_WC_LOCKED))
{
err = svn_error_quick_wrap(
err, _("Run 'svn cleanup' to remove locks "
"(type 'svn help cleanup' for details)"));
}
if (err->apr_err == SVN_ERR_SQLITE_BUSY)
{
err = svn_error_quick_wrap(err,
_("Another process is blocking the "
"working copy database, or the "
"underlying filesystem does not "
"support file locking; if the working "
"copy is on a network filesystem, make "
"sure file locking has been enabled "
"on the file server"));
}
if (svn_error_find_cause(err, SVN_ERR_RA_CANNOT_CREATE_TUNNEL) &&
(opt_state.auth_username || opt_state.auth_password))
{
err = svn_error_quick_wrap(
err, _("When using svn+ssh:// URLs, keep in mind that the "
"--username and --password options are ignored "
"because authentication is performed by SSH, not "
"Subversion"));
}
/* Ensure that stdout is flushed, so the user will see any write errors.
This makes sure that output is not silently lost. */
err = svn_error_compose_create(err, svn_cmdline_fflush(stdout));
return EXIT_ERROR(err);
}
else
{
/* Ensure that stdout is flushed, so the user will see any write errors.
This makes sure that output is not silently lost. */
SVN_INT_ERR(svn_cmdline_fflush(stdout));
return EXIT_SUCCESS;
}
}
int
main(int argc, const char *argv[])
{
apr_pool_t *pool;
int exit_code;
/* Initialize the app. */
if (svn_cmdline_init("svn", stderr) != EXIT_SUCCESS)
return EXIT_FAILURE;
/* Create our top-level pool. Use a separate mutexless allocator,
* given this application is single threaded.
*/
pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
exit_code = sub_main(argc, argv, pool);
svn_pool_destroy(pool);
return exit_code;
}
Index: vendor/subversion/dist/subversion/svn_private_config.h.in
===================================================================
--- vendor/subversion/dist/subversion/svn_private_config.h.in (revision 286500)
+++ vendor/subversion/dist/subversion/svn_private_config.h.in (revision 286501)
@@ -1,257 +1,259 @@
/* subversion/svn_private_config.h.in. Generated from configure.ac by autoheader. */
/* The fs type to use by default */
#undef DEFAULT_FS_TYPE
/* The http library to use by default */
#undef DEFAULT_HTTP_LIBRARY
/* Define to 1 if Ev2 implementations should be used. */
#undef ENABLE_EV2_IMPL
/* Define to 1 if translation of program messages to the user's native
language is requested. */
#undef ENABLE_NLS
/* Define to 1 if you have the `bind_textdomain_codeset' function. */
#undef HAVE_BIND_TEXTDOMAIN_CODESET
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
#undef HAVE_DOPRNT
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the `iconv' library (-liconv). */
#undef HAVE_LIBICONV
/* Define to 1 if you have the `socket' library (-lsocket). */
#undef HAVE_LIBSOCKET
/* Define to 1 if you have the <magic.h> header file. */
#undef HAVE_MAGIC_H
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define to 1 if you have the `rb_errinfo' function. */
#undef HAVE_RB_ERRINFO
/* Define to 1 if you have the `readlink' function. */
#undef HAVE_READLINK
/* Define to 1 if you have the <serf.h> header file. */
#undef HAVE_SERF_H
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the `symlink' function. */
#undef HAVE_SYMLINK
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have the <sys/utsname.h> header file. */
#undef HAVE_SYS_UTSNAME_H
/* Define to 1 if you have the `tcgetattr' function. */
#undef HAVE_TCGETATTR
/* Define to 1 if you have the `tcsetattr' function. */
#undef HAVE_TCSETATTR
/* Defined if we have a usable termios library. */
#undef HAVE_TERMIOS_H
/* Define to 1 if you have the `uname' function. */
#undef HAVE_UNAME
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to 1 if you have the `vprintf' function. */
#undef HAVE_VPRINTF
/* Define to 1 if you have the <zlib.h> header file. */
#undef HAVE_ZLIB_H
-/* Define to the sub-directory in which libtool stores uninstalled libraries.
- */
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
#undef LT_OBJDIR
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the home page for this package. */
#undef PACKAGE_URL
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
+
+/* Defined to allow building against httpd 2.4 with broken auth */
+#undef SVN_ALLOW_BROKEN_HTTPD_AUTH
/* Define to the Python/C API format character suitable for apr_int64_t */
#undef SVN_APR_INT64_T_PYCFMT
/* Define if circular linkage is not possible on this platform. */
#undef SVN_AVOID_CIRCULAR_LINKAGE_AT_ALL_COSTS_HACK
/* Defined to be the path to the installed binaries */
#undef SVN_BINDIR
/* Defined to the config.guess name of the build system */
#undef SVN_BUILD_HOST
/* Defined to the config.guess name of the build target */
#undef SVN_BUILD_TARGET
/* The path of a default editor for the client. */
#undef SVN_CLIENT_EDITOR
/* Defined if the full version matching rules are disabled */
#undef SVN_DISABLE_FULL_VERSION_MATCH
/* Defined if plaintext password/passphrase storage is disabled */
#undef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE
/* The desired major version for the Berkeley DB */
#undef SVN_FS_WANT_DB_MAJOR
/* The desired minor version for the Berkeley DB */
#undef SVN_FS_WANT_DB_MINOR
/* The desired patch version for the Berkeley DB */
#undef SVN_FS_WANT_DB_PATCH
/* Define if compiler provides atomic builtins */
#undef SVN_HAS_ATOMIC_BUILTINS
/* Is GNOME Keyring support enabled? */
#undef SVN_HAVE_GNOME_KEYRING
/* Is GPG Agent support enabled? */
#undef SVN_HAVE_GPG_AGENT
/* Is Mac OS KeyChain support enabled? */
#undef SVN_HAVE_KEYCHAIN_SERVICES
/* Defined if KWallet support is enabled */
#undef SVN_HAVE_KWALLET
/* Defined if libmagic support is enabled */
#undef SVN_HAVE_LIBMAGIC
/* Is Mach-O low-level _dyld API available? */
#undef SVN_HAVE_MACHO_ITERATE
/* Is Mac OS property list API available? */
#undef SVN_HAVE_MACOS_PLIST
/* Defined if apr_memcache (standalone or in apr-util) is present */
#undef SVN_HAVE_MEMCACHE
/* Defined if Expat 1.0 or 1.1 was found */
#undef SVN_HAVE_OLD_EXPAT
/* Defined if Cyrus SASL v2 is present on the system */
#undef SVN_HAVE_SASL
/* Defined if support for Serf is enabled */
#undef SVN_HAVE_SERF
/* Defined if libsvn_client should link against libsvn_ra_local */
#undef SVN_LIBSVN_CLIENT_LINKS_RA_LOCAL
/* Defined if libsvn_client should link against libsvn_ra_serf */
#undef SVN_LIBSVN_CLIENT_LINKS_RA_SERF
/* Defined if libsvn_client should link against libsvn_ra_svn */
#undef SVN_LIBSVN_CLIENT_LINKS_RA_SVN
/* Defined if libsvn_fs should link against libsvn_fs_base */
#undef SVN_LIBSVN_FS_LINKS_FS_BASE
/* Defined if libsvn_fs should link against libsvn_fs_fs */
#undef SVN_LIBSVN_FS_LINKS_FS_FS
/* Defined to be the path to the installed locale dirs */
#undef SVN_LOCALE_DIR
/* Defined to be the null device for the system */
#undef SVN_NULL_DEVICE_NAME
/* Defined to be the path separator used on your local filesystem */
#undef SVN_PATH_LOCAL_SEPARATOR
/* Subversion library major verson */
#undef SVN_SOVERSION
/* Defined if svn should use the amalgamated version of sqlite */
#undef SVN_SQLITE_INLINE
/* Defined if svn should try to load DSOs */
#undef SVN_USE_DSO
/* Define to empty if `const' does not conform to ANSI C. */
#undef const
/* Define to `unsigned int' if <sys/types.h> does not define. */
#undef size_t
#ifdef SVN_WANT_BDB
#define APU_WANT_DB
@SVN_DB_HEADER@
#endif
/* Indicate to translators that string X should be translated. Do not look
up the translation at run time; just expand to X. This macro is suitable
for use where a constant string is required at compile time. */
#define N_(x) x
/* Indicate to translators that we have decided the string X should not be
translated. Expand to X. */
#define U_(x) x
#ifdef ENABLE_NLS
#include <locale.h>
#include <libintl.h>
/* Indicate to translators that string X should be translated. At run time,
look up and return the translation of X. */
#define _(x) dgettext(PACKAGE_NAME, x)
/* Indicate to translators that strings X1 and X2 are singular and plural
forms of the same message, and should be translated. At run time, return
an appropriate translation depending on the number N. */
#define Q_(x1, x2, n) dngettext(PACKAGE_NAME, x1, x2, n)
#else
#define _(x) (x)
#define Q_(x1, x2, n) (((n) == 1) ? x1 : x2)
#define gettext(x) (x)
#define dgettext(domain, x) (x)
#endif
Index: vendor/subversion/dist/subversion/svnadmin/svnadmin.c
===================================================================
--- vendor/subversion/dist/subversion/svnadmin/svnadmin.c (revision 286500)
+++ vendor/subversion/dist/subversion/svnadmin/svnadmin.c (revision 286501)
@@ -1,2362 +1,2362 @@
/*
* svnadmin.c: Subversion server administration tool main file.
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include <apr_file_io.h>
#include <apr_signal.h>
#include "svn_hash.h"
#include "svn_pools.h"
#include "svn_cmdline.h"
#include "svn_error.h"
#include "svn_opt.h"
#include "svn_utf.h"
#include "svn_subst.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_config.h"
#include "svn_repos.h"
#include "svn_cache_config.h"
#include "svn_version.h"
#include "svn_props.h"
#include "svn_time.h"
#include "svn_user.h"
#include "svn_xml.h"
#include "private/svn_opt_private.h"
#include "private/svn_subr_private.h"
#include "private/svn_cmdline_private.h"
#include "svn_private_config.h"
/*** Code. ***/
/* A flag to see if we've been cancelled by the client or not. */
static volatile sig_atomic_t cancelled = FALSE;
/* A signal handler to support cancellation. */
static void
signal_handler(int signum)
{
apr_signal(signum, SIG_IGN);
cancelled = TRUE;
}
/* A helper to set up the cancellation signal handlers. */
static void
setup_cancellation_signals(void (*handler)(int signum))
{
apr_signal(SIGINT, handler);
#ifdef SIGBREAK
/* SIGBREAK is a Win32 specific signal generated by ctrl-break. */
apr_signal(SIGBREAK, handler);
#endif
#ifdef SIGHUP
apr_signal(SIGHUP, handler);
#endif
#ifdef SIGTERM
apr_signal(SIGTERM, handler);
#endif
}
/* Our cancellation callback. */
static svn_error_t *
check_cancel(void *baton)
{
if (cancelled)
return svn_error_create(SVN_ERR_CANCELLED, NULL, _("Caught signal"));
else
return SVN_NO_ERROR;
}
/* Custom filesystem warning function. */
static void
warning_func(void *baton,
svn_error_t *err)
{
if (! err)
return;
svn_handle_error2(err, stderr, FALSE, "svnadmin: ");
}
/* Helper to open a repository and set a warning func (so we don't
* SEGFAULT when libsvn_fs's default handler gets run). */
static svn_error_t *
open_repos(svn_repos_t **repos,
const char *path,
apr_pool_t *pool)
{
/* construct FS configuration parameters: enable caches for r/o data */
apr_hash_t *fs_config = apr_hash_make(pool);
svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_DELTAS, "1");
svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS, "1");
svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS, "2");
svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_NS,
svn_uuid_generate(pool));
/* now, open the requested repository */
SVN_ERR(svn_repos_open2(repos, path, fs_config, pool));
svn_fs_set_warning_func(svn_repos_fs(*repos), warning_func, NULL);
return SVN_NO_ERROR;
}
/* Version compatibility check */
static svn_error_t *
check_lib_versions(void)
{
static const svn_version_checklist_t checklist[] =
{
{ "svn_subr", svn_subr_version },
{ "svn_repos", svn_repos_version },
{ "svn_fs", svn_fs_version },
{ "svn_delta", svn_delta_version },
{ NULL, NULL }
};
SVN_VERSION_DEFINE(my_version);
return svn_ver_check_list2(&my_version, checklist, svn_ver_equal);
}
/** Subcommands. **/
static svn_opt_subcommand_t
subcommand_crashtest,
subcommand_create,
subcommand_deltify,
subcommand_dump,
subcommand_freeze,
subcommand_help,
subcommand_hotcopy,
subcommand_load,
subcommand_list_dblogs,
subcommand_list_unused_dblogs,
subcommand_lock,
subcommand_lslocks,
subcommand_lstxns,
subcommand_pack,
subcommand_recover,
subcommand_rmlocks,
subcommand_rmtxns,
subcommand_setlog,
subcommand_setrevprop,
subcommand_setuuid,
subcommand_unlock,
subcommand_upgrade,
subcommand_verify;
enum svnadmin__cmdline_options_t
{
svnadmin__version = SVN_OPT_FIRST_LONGOPT_ID,
svnadmin__incremental,
svnadmin__deltas,
svnadmin__ignore_uuid,
svnadmin__force_uuid,
svnadmin__fs_type,
svnadmin__parent_dir,
svnadmin__bdb_txn_nosync,
svnadmin__bdb_log_keep,
svnadmin__config_dir,
svnadmin__bypass_hooks,
svnadmin__bypass_prop_validation,
svnadmin__use_pre_commit_hook,
svnadmin__use_post_commit_hook,
svnadmin__use_pre_revprop_change_hook,
svnadmin__use_post_revprop_change_hook,
svnadmin__clean_logs,
svnadmin__wait,
svnadmin__pre_1_4_compatible,
svnadmin__pre_1_5_compatible,
svnadmin__pre_1_6_compatible,
svnadmin__compatible_version
};
/* Option codes and descriptions.
*
* The entire list must be terminated with an entry of nulls.
*/
static const apr_getopt_option_t options_table[] =
{
{"help", 'h', 0,
N_("show help on a subcommand")},
{NULL, '?', 0,
N_("show help on a subcommand")},
{"version", svnadmin__version, 0,
N_("show program version information")},
{"revision", 'r', 1,
N_("specify revision number ARG (or X:Y range)")},
{"transaction", 't', 1,
N_("specify transaction name ARG")},
{"incremental", svnadmin__incremental, 0,
N_("dump or hotcopy incrementally")},
{"deltas", svnadmin__deltas, 0,
N_("use deltas in dump output")},
{"bypass-hooks", svnadmin__bypass_hooks, 0,
N_("bypass the repository hook system")},
{"bypass-prop-validation", svnadmin__bypass_prop_validation, 0,
N_("bypass property validation logic")},
{"quiet", 'q', 0,
N_("no progress (only errors) to stderr")},
{"ignore-uuid", svnadmin__ignore_uuid, 0,
N_("ignore any repos UUID found in the stream")},
{"force-uuid", svnadmin__force_uuid, 0,
N_("set repos UUID to that found in stream, if any")},
{"fs-type", svnadmin__fs_type, 1,
N_("type of repository: 'fsfs' (default) or 'bdb'")},
{"parent-dir", svnadmin__parent_dir, 1,
N_("load at specified directory in repository")},
{"bdb-txn-nosync", svnadmin__bdb_txn_nosync, 0,
N_("disable fsync at transaction commit [Berkeley DB]")},
{"bdb-log-keep", svnadmin__bdb_log_keep, 0,
N_("disable automatic log file removal [Berkeley DB]")},
{"config-dir", svnadmin__config_dir, 1,
N_("read user configuration files from directory ARG")},
{"clean-logs", svnadmin__clean_logs, 0,
N_("remove redundant Berkeley DB log files\n"
" from source repository [Berkeley DB]")},
{"use-pre-commit-hook", svnadmin__use_pre_commit_hook, 0,
N_("call pre-commit hook before committing revisions")},
{"use-post-commit-hook", svnadmin__use_post_commit_hook, 0,
N_("call post-commit hook after committing revisions")},
{"use-pre-revprop-change-hook", svnadmin__use_pre_revprop_change_hook, 0,
N_("call hook before changing revision property")},
{"use-post-revprop-change-hook", svnadmin__use_post_revprop_change_hook, 0,
N_("call hook after changing revision property")},
{"wait", svnadmin__wait, 0,
N_("wait instead of exit if the repository is in\n"
" use by another process")},
{"pre-1.4-compatible", svnadmin__pre_1_4_compatible, 0,
N_("deprecated; see --compatible-version")},
{"pre-1.5-compatible", svnadmin__pre_1_5_compatible, 0,
N_("deprecated; see --compatible-version")},
{"pre-1.6-compatible", svnadmin__pre_1_6_compatible, 0,
N_("deprecated; see --compatible-version")},
{"memory-cache-size", 'M', 1,
N_("size of the extra in-memory cache in MB used to\n"
" minimize redundant operations. Default: 16.\n"
" [used for FSFS repositories only]")},
{"compatible-version", svnadmin__compatible_version, 1,
N_("use repository format compatible with Subversion\n"
" version ARG (\"1.5.5\", \"1.7\", etc.)")},
{"file", 'F', 1, N_("read repository paths from file ARG")},
{NULL}
};
/* Array of available subcommands.
* The entire list must be terminated with an entry of nulls.
*/
static const svn_opt_subcommand_desc2_t cmd_table[] =
{
{"crashtest", subcommand_crashtest, {0}, N_
("usage: svnadmin crashtest REPOS_PATH\n\n"
"Open the repository at REPOS_PATH, then abort, thus simulating\n"
"a process that crashes while holding an open repository handle.\n"),
{0} },
{"create", subcommand_create, {0}, N_
("usage: svnadmin create REPOS_PATH\n\n"
"Create a new, empty repository at REPOS_PATH.\n"),
{svnadmin__bdb_txn_nosync, svnadmin__bdb_log_keep,
svnadmin__config_dir, svnadmin__fs_type, svnadmin__compatible_version,
svnadmin__pre_1_4_compatible, svnadmin__pre_1_5_compatible,
svnadmin__pre_1_6_compatible
} },
{"deltify", subcommand_deltify, {0}, N_
("usage: svnadmin deltify [-r LOWER[:UPPER]] REPOS_PATH\n\n"
"Run over the requested revision range, performing predecessor delti-\n"
"fication on the paths changed in those revisions. Deltification in\n"
"essence compresses the repository by only storing the differences or\n"
"delta from the preceding revision. If no revisions are specified,\n"
"this will simply deltify the HEAD revision.\n"),
{'r', 'q', 'M'} },
{"dump", subcommand_dump, {0}, N_
("usage: svnadmin dump REPOS_PATH [-r LOWER[:UPPER] [--incremental]]\n\n"
"Dump the contents of filesystem to stdout in a 'dumpfile'\n"
"portable format, sending feedback to stderr. Dump revisions\n"
"LOWER rev through UPPER rev. If no revisions are given, dump all\n"
"revision trees. If only LOWER is given, dump that one revision tree.\n"
"If --incremental is passed, the first revision dumped will describe\n"
"only the paths changed in that revision; otherwise it will describe\n"
"every path present in the repository as of that revision. (In either\n"
"case, the second and subsequent revisions, if any, describe only paths\n"
"changed in those revisions.)\n"),
{'r', svnadmin__incremental, svnadmin__deltas, 'q', 'M'} },
{"freeze", subcommand_freeze, {0}, N_
("usage: 1. svnadmin freeze REPOS_PATH PROGRAM [ARG...]\n"
" 2. svnadmin freeze -F FILE PROGRAM [ARG...]\n\n"
"1. Run PROGRAM passing ARGS while holding a write-lock on REPOS_PATH.\n"
"\n"
"2. Like 1 except all repositories listed in FILE are locked. The file\n"
" format is repository paths separated by newlines. Repositories are\n"
" locked in the same order as they are listed in the file.\n"),
{'F'} },
{"help", subcommand_help, {"?", "h"}, N_
("usage: svnadmin help [SUBCOMMAND...]\n\n"
"Describe the usage of this program or its subcommands.\n"),
{0} },
{"hotcopy", subcommand_hotcopy, {0}, N_
("usage: svnadmin hotcopy REPOS_PATH NEW_REPOS_PATH\n\n"
"Make a hot copy of a repository.\n"
"If --incremental is passed, data which already exists at the destination\n"
"is not copied again. Incremental mode is implemented for FSFS repositories.\n"),
{svnadmin__clean_logs, svnadmin__incremental} },
{"list-dblogs", subcommand_list_dblogs, {0}, N_
("usage: svnadmin list-dblogs REPOS_PATH\n\n"
"List all Berkeley DB log files.\n\n"
"WARNING: Modifying or deleting logfiles which are still in use\n"
"will cause your repository to be corrupted.\n"),
{0} },
{"list-unused-dblogs", subcommand_list_unused_dblogs, {0}, N_
("usage: svnadmin list-unused-dblogs REPOS_PATH\n\n"
"List unused Berkeley DB log files.\n\n"),
{0} },
{"load", subcommand_load, {0}, N_
("usage: svnadmin load REPOS_PATH\n\n"
"Read a 'dumpfile'-formatted stream from stdin, committing\n"
"new revisions into the repository's filesystem. If the repository\n"
"was previously empty, its UUID will, by default, be changed to the\n"
"one specified in the stream. Progress feedback is sent to stdout.\n"
"If --revision is specified, limit the loaded revisions to only those\n"
"in the dump stream whose revision numbers match the specified range.\n"),
{'q', 'r', svnadmin__ignore_uuid, svnadmin__force_uuid,
svnadmin__use_pre_commit_hook, svnadmin__use_post_commit_hook,
svnadmin__parent_dir, svnadmin__bypass_prop_validation, 'M'} },
{"lock", subcommand_lock, {0}, N_
("usage: svnadmin lock REPOS_PATH PATH USERNAME COMMENT-FILE [TOKEN]\n\n"
"Lock PATH by USERNAME setting comments from COMMENT-FILE.\n"
"If provided, use TOKEN as lock token. Use --bypass-hooks to avoid\n"
"triggering the pre-lock and post-lock hook scripts.\n"),
{svnadmin__bypass_hooks} },
{"lslocks", subcommand_lslocks, {0}, N_
("usage: svnadmin lslocks REPOS_PATH [PATH-IN-REPOS]\n\n"
"Print descriptions of all locks on or under PATH-IN-REPOS (which,\n"
"if not provided, is the root of the repository).\n"),
{0} },
{"lstxns", subcommand_lstxns, {0}, N_
("usage: svnadmin lstxns REPOS_PATH\n\n"
"Print the names of all uncommitted transactions.\n"),
{0} },
{"pack", subcommand_pack, {0}, N_
("usage: svnadmin pack REPOS_PATH\n\n"
"Possibly compact the repository into a more efficient storage model.\n"
"This may not apply to all repositories, in which case, exit.\n"),
{'q'} },
{"recover", subcommand_recover, {0}, N_
("usage: svnadmin recover REPOS_PATH\n\n"
"Run the recovery procedure on a repository. Do this if you've\n"
"been getting errors indicating that recovery ought to be run.\n"
"Berkeley DB recovery requires exclusive access and will\n"
"exit if the repository is in use by another process.\n"),
{svnadmin__wait} },
{"rmlocks", subcommand_rmlocks, {0}, N_
("usage: svnadmin rmlocks REPOS_PATH LOCKED_PATH...\n\n"
"Unconditionally remove lock from each LOCKED_PATH.\n"),
{0} },
{"rmtxns", subcommand_rmtxns, {0}, N_
("usage: svnadmin rmtxns REPOS_PATH TXN_NAME...\n\n"
"Delete the named transaction(s).\n"),
{'q'} },
{"setlog", subcommand_setlog, {0}, N_
("usage: svnadmin setlog REPOS_PATH -r REVISION FILE\n\n"
"Set the log-message on revision REVISION to the contents of FILE. Use\n"
"--bypass-hooks to avoid triggering the revision-property-related hooks\n"
"(for example, if you do not want an email notification sent\n"
"from your post-revprop-change hook, or because the modification of\n"
"revision properties has not been enabled in the pre-revprop-change\n"
"hook).\n\n"
"NOTE: Revision properties are not versioned, so this command will\n"
"overwrite the previous log message.\n"),
{'r', svnadmin__bypass_hooks} },
{"setrevprop", subcommand_setrevprop, {0}, N_
("usage: svnadmin setrevprop REPOS_PATH -r REVISION NAME FILE\n\n"
"Set the property NAME on revision REVISION to the contents of FILE. Use\n"
"--use-pre-revprop-change-hook/--use-post-revprop-change-hook to trigger\n"
"the revision property-related hooks (for example, if you want an email\n"
"notification sent from your post-revprop-change hook).\n\n"
"NOTE: Revision properties are not versioned, so this command will\n"
"overwrite the previous value of the property.\n"),
{'r', svnadmin__use_pre_revprop_change_hook,
svnadmin__use_post_revprop_change_hook} },
{"setuuid", subcommand_setuuid, {0}, N_
("usage: svnadmin setuuid REPOS_PATH [NEW_UUID]\n\n"
"Reset the repository UUID for the repository located at REPOS_PATH. If\n"
"NEW_UUID is provided, use that as the new repository UUID; otherwise,\n"
"generate a brand new UUID for the repository.\n"),
{0} },
{"unlock", subcommand_unlock, {0}, N_
("usage: svnadmin unlock REPOS_PATH LOCKED_PATH USERNAME TOKEN\n\n"
"Unlock LOCKED_PATH (as USERNAME) after verifying that the token\n"
"associated with the lock matches TOKEN. Use --bypass-hooks to avoid\n"
"triggering the pre-unlock and post-unlock hook scripts.\n"),
{svnadmin__bypass_hooks} },
{"upgrade", subcommand_upgrade, {0}, N_
("usage: svnadmin upgrade REPOS_PATH\n\n"
"Upgrade the repository located at REPOS_PATH to the latest supported\n"
"schema version.\n\n"
"This functionality is provided as a convenience for repository\n"
"administrators who wish to make use of new Subversion functionality\n"
"without having to undertake a potentially costly full repository dump\n"
"and load operation. As such, the upgrade performs only the minimum\n"
"amount of work needed to accomplish this while still maintaining the\n"
"integrity of the repository. It does not guarantee the most optimized\n"
"repository state as a dump and subsequent load would.\n"),
{0} },
{"verify", subcommand_verify, {0}, N_
("usage: svnadmin verify REPOS_PATH\n\n"
"Verify the data stored in the repository.\n"),
{'t', 'r', 'q', 'M'} },
{ NULL, NULL, {0}, NULL, {0} }
};
/* Baton for passing option/argument state to a subcommand function. */
struct svnadmin_opt_state
{
const char *repository_path;
const char *fs_type; /* --fs-type */
svn_boolean_t pre_1_4_compatible; /* --pre-1.4-compatible */
svn_boolean_t pre_1_5_compatible; /* --pre-1.5-compatible */
svn_boolean_t pre_1_6_compatible; /* --pre-1.6-compatible */
svn_version_t *compatible_version; /* --compatible-version */
svn_opt_revision_t start_revision, end_revision; /* -r X[:Y] */
const char *txn_id; /* -t TXN */
svn_boolean_t help; /* --help or -? */
svn_boolean_t version; /* --version */
svn_boolean_t incremental; /* --incremental */
svn_boolean_t use_deltas; /* --deltas */
svn_boolean_t use_pre_commit_hook; /* --use-pre-commit-hook */
svn_boolean_t use_post_commit_hook; /* --use-post-commit-hook */
svn_boolean_t use_pre_revprop_change_hook; /* --use-pre-revprop-change-hook */
svn_boolean_t use_post_revprop_change_hook; /* --use-post-revprop-change-hook */
svn_boolean_t quiet; /* --quiet */
svn_boolean_t bdb_txn_nosync; /* --bdb-txn-nosync */
svn_boolean_t bdb_log_keep; /* --bdb-log-keep */
svn_boolean_t clean_logs; /* --clean-logs */
svn_boolean_t bypass_hooks; /* --bypass-hooks */
svn_boolean_t wait; /* --wait */
svn_boolean_t bypass_prop_validation; /* --bypass-prop-validation */
enum svn_repos_load_uuid uuid_action; /* --ignore-uuid,
--force-uuid */
apr_uint64_t memory_cache_size; /* --memory-cache-size M */
const char *parent_dir;
svn_stringbuf_t *filedata; /* --file */
const char *config_dir; /* Overriding Configuration Directory */
};
/* Set *REVNUM to the revision specified by REVISION (or to
SVN_INVALID_REVNUM if that has the type 'unspecified'),
possibly making use of the YOUNGEST revision number in REPOS. */
static svn_error_t *
get_revnum(svn_revnum_t *revnum, const svn_opt_revision_t *revision,
svn_revnum_t youngest, svn_repos_t *repos, apr_pool_t *pool)
{
if (revision->kind == svn_opt_revision_number)
*revnum = revision->value.number;
else if (revision->kind == svn_opt_revision_head)
*revnum = youngest;
else if (revision->kind == svn_opt_revision_date)
SVN_ERR(svn_repos_dated_revision(revnum, repos, revision->value.date,
pool));
else if (revision->kind == svn_opt_revision_unspecified)
*revnum = SVN_INVALID_REVNUM;
else
return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("Invalid revision specifier"));
if (*revnum > youngest)
return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("Revisions must not be greater than the youngest revision (%ld)"),
youngest);
return SVN_NO_ERROR;
}
/* Set *PATH to an internal-style, UTF8-encoded, local dirent path
allocated from POOL and parsed from raw command-line argument ARG. */
static svn_error_t *
target_arg_to_dirent(const char **dirent,
const char *arg,
apr_pool_t *pool)
{
const char *path;
SVN_ERR(svn_utf_cstring_to_utf8(&path, arg, pool));
if (svn_path_is_url(path))
return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
"Path '%s' is not a local path", path);
*dirent = svn_dirent_internal_style(path, pool);
return SVN_NO_ERROR;
}
/* Parse the remaining command-line arguments from OS, returning them
in a new array *ARGS (allocated from POOL) and optionally verifying
that we got the expected number thereof. If MIN_EXPECTED is not
negative, return an error if the function would return fewer than
MIN_EXPECTED arguments. If MAX_EXPECTED is not negative, return an
error if the function would return more than MAX_EXPECTED
arguments.
As a special case, when MIN_EXPECTED and MAX_EXPECTED are both 0,
allow ARGS to be NULL. */
static svn_error_t *
parse_args(apr_array_header_t **args,
apr_getopt_t *os,
int min_expected,
int max_expected,
apr_pool_t *pool)
{
int num_args = os ? (os->argc - os->ind) : 0;
if (min_expected || max_expected)
SVN_ERR_ASSERT(args);
if ((min_expected >= 0) && (num_args < min_expected))
return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0,
"Not enough arguments");
if ((max_expected >= 0) && (num_args > max_expected))
return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0,
"Too many arguments");
if (args)
{
*args = apr_array_make(pool, num_args, sizeof(const char *));
if (num_args)
while (os->ind < os->argc)
APR_ARRAY_PUSH(*args, const char *) =
apr_pstrdup(pool, os->argv[os->ind++]);
}
return SVN_NO_ERROR;
}
/* This implements `svn_opt_subcommand_t'. */
static svn_error_t *
subcommand_crashtest(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
struct svnadmin_opt_state *opt_state = baton;
svn_repos_t *repos;
SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
SVN_ERR_MALFUNCTION();
/* merely silence a compiler warning (this will never be executed) */
return SVN_NO_ERROR;
}
/* This implements `svn_opt_subcommand_t'. */
static svn_error_t *
subcommand_create(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
struct svnadmin_opt_state *opt_state = baton;
svn_repos_t *repos;
apr_hash_t *fs_config = apr_hash_make(pool);
/* Expect no more arguments. */
SVN_ERR(parse_args(NULL, os, 0, 0, pool));
svn_hash_sets(fs_config, SVN_FS_CONFIG_BDB_TXN_NOSYNC,
(opt_state->bdb_txn_nosync ? "1" :"0"));
svn_hash_sets(fs_config, SVN_FS_CONFIG_BDB_LOG_AUTOREMOVE,
(opt_state->bdb_log_keep ? "0" :"1"));
if (opt_state->fs_type)
{
/* With 1.8 we are announcing that BDB is deprecated. No support
* has been removed and it will continue to work until some future
* date. The purpose here is to discourage people from creating
* new BDB repositories which they will need to dump/load into
* FSFS or some new FS type in the future. */
if (0 == strcmp(opt_state->fs_type, SVN_FS_TYPE_BDB))
{
SVN_ERR(svn_cmdline_fprintf(
stderr, pool,
_("%swarning:"
" The \"%s\" repository back-end is deprecated,"
" consider using \"%s\" instead.\n"),
"svnadmin: ", SVN_FS_TYPE_BDB, SVN_FS_TYPE_FSFS));
fflush(stderr);
}
svn_hash_sets(fs_config, SVN_FS_CONFIG_FS_TYPE, opt_state->fs_type);
}
/* Prior to 1.8, we had explicit options to specify compatibility
with a handful of prior Subversion releases. */
if (opt_state->pre_1_4_compatible)
svn_hash_sets(fs_config, SVN_FS_CONFIG_PRE_1_4_COMPATIBLE, "1");
if (opt_state->pre_1_5_compatible)
svn_hash_sets(fs_config, SVN_FS_CONFIG_PRE_1_5_COMPATIBLE, "1");
if (opt_state->pre_1_6_compatible)
svn_hash_sets(fs_config, SVN_FS_CONFIG_PRE_1_6_COMPATIBLE, "1");
/* In 1.8, we figured out that we didn't have to keep extending this
madness indefinitely. */
if (opt_state->compatible_version)
{
if (! svn_version__at_least(opt_state->compatible_version, 1, 4, 0))
svn_hash_sets(fs_config, SVN_FS_CONFIG_PRE_1_4_COMPATIBLE, "1");
if (! svn_version__at_least(opt_state->compatible_version, 1, 5, 0))
svn_hash_sets(fs_config, SVN_FS_CONFIG_PRE_1_5_COMPATIBLE, "1");
if (! svn_version__at_least(opt_state->compatible_version, 1, 6, 0))
svn_hash_sets(fs_config, SVN_FS_CONFIG_PRE_1_6_COMPATIBLE, "1");
if (! svn_version__at_least(opt_state->compatible_version, 1, 8, 0))
svn_hash_sets(fs_config, SVN_FS_CONFIG_PRE_1_8_COMPATIBLE, "1");
}
if (opt_state->compatible_version
&& ! svn_version__at_least(opt_state->compatible_version, 1, 1, 0)
/* ### TODO: this NULL check hard-codes knowledge of the library's
default fs-type value */
&& (opt_state->fs_type == NULL
|| !strcmp(opt_state->fs_type, SVN_FS_TYPE_FSFS)))
{
return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("Repositories compatible with 1.0.x must use "
"--fs-type=bdb"));
}
SVN_ERR(svn_repos_create(&repos, opt_state->repository_path,
NULL, NULL, NULL, fs_config, pool));
svn_fs_set_warning_func(svn_repos_fs(repos), warning_func, NULL);
return SVN_NO_ERROR;
}
/* This implements `svn_opt_subcommand_t'. */
static svn_error_t *
subcommand_deltify(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
struct svnadmin_opt_state *opt_state = baton;
svn_repos_t *repos;
svn_fs_t *fs;
svn_revnum_t start = SVN_INVALID_REVNUM, end = SVN_INVALID_REVNUM;
svn_revnum_t youngest, revision;
apr_pool_t *subpool = svn_pool_create(pool);
/* Expect no more arguments. */
SVN_ERR(parse_args(NULL, os, 0, 0, pool));
SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
fs = svn_repos_fs(repos);
SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool));
/* Find the revision numbers at which to start and end. */
SVN_ERR(get_revnum(&start, &opt_state->start_revision,
youngest, repos, pool));
SVN_ERR(get_revnum(&end, &opt_state->end_revision,
youngest, repos, pool));
/* Fill in implied revisions if necessary. */
if (start == SVN_INVALID_REVNUM)
start = youngest;
if (end == SVN_INVALID_REVNUM)
end = start;
if (start > end)
return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("First revision cannot be higher than second"));
/* Loop over the requested revision range, performing the
predecessor deltification on paths changed in each. */
for (revision = start; revision <= end; revision++)
{
svn_pool_clear(subpool);
SVN_ERR(check_cancel(NULL));
if (! opt_state->quiet)
SVN_ERR(svn_cmdline_printf(subpool, _("Deltifying revision %ld..."),
revision));
SVN_ERR(svn_fs_deltify_revision(fs, revision, subpool));
if (! opt_state->quiet)
SVN_ERR(svn_cmdline_printf(subpool, _("done.\n")));
}
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
/* Implementation of svn_repos_notify_func_t to wrap the output to a
response stream for svn_repos_dump_fs2() and svn_repos_verify_fs() */
static void
repos_notify_handler(void *baton,
const svn_repos_notify_t *notify,
apr_pool_t *scratch_pool)
{
svn_stream_t *feedback_stream = baton;
apr_size_t len;
switch (notify->action)
{
case svn_repos_notify_warning:
svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool,
"WARNING 0x%04x: %s\n", notify->warning,
notify->warning_str));
return;
case svn_repos_notify_dump_rev_end:
svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool,
_("* Dumped revision %ld.\n"),
notify->revision));
return;
case svn_repos_notify_verify_rev_end:
svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool,
_("* Verified revision %ld.\n"),
notify->revision));
return;
case svn_repos_notify_verify_rev_structure:
if (notify->revision == SVN_INVALID_REVNUM)
svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool,
_("* Verifying repository metadata ...\n")));
else
svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool,
_("* Verifying metadata at revision %ld ...\n"),
notify->revision));
return;
case svn_repos_notify_pack_shard_start:
{
const char *shardstr = apr_psprintf(scratch_pool,
"%" APR_INT64_T_FMT,
notify->shard);
svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool,
_("Packing revisions in shard %s..."),
shardstr));
}
return;
case svn_repos_notify_pack_shard_end:
svn_error_clear(svn_stream_puts(feedback_stream, _("done.\n")));
return;
case svn_repos_notify_pack_shard_start_revprop:
{
const char *shardstr = apr_psprintf(scratch_pool,
"%" APR_INT64_T_FMT,
notify->shard);
svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool,
_("Packing revprops in shard %s..."),
shardstr));
}
return;
case svn_repos_notify_pack_shard_end_revprop:
svn_error_clear(svn_stream_puts(feedback_stream, _("done.\n")));
return;
case svn_repos_notify_load_txn_committed:
if (notify->old_revision == SVN_INVALID_REVNUM)
{
svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool,
_("\n------- Committed revision %ld >>>\n\n"),
notify->new_revision));
}
else
{
svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool,
_("\n------- Committed new rev %ld"
" (loaded from original rev %ld"
") >>>\n\n"), notify->new_revision,
notify->old_revision));
}
return;
case svn_repos_notify_load_node_start:
{
switch (notify->node_action)
{
case svn_node_action_change:
svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool,
_(" * editing path : %s ..."),
notify->path));
break;
case svn_node_action_delete:
svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool,
_(" * deleting path : %s ..."),
notify->path));
break;
case svn_node_action_add:
svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool,
_(" * adding path : %s ..."),
notify->path));
break;
case svn_node_action_replace:
svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool,
_(" * replacing path : %s ..."),
notify->path));
break;
}
}
return;
case svn_repos_notify_load_node_done:
svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool,
"%s", _(" done.\n")));
return;
case svn_repos_notify_load_copied_node:
len = 9;
svn_error_clear(svn_stream_write(feedback_stream, "COPIED...", &len));
return;
case svn_repos_notify_load_txn_start:
svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool,
_("<<< Started new transaction, based on "
"original revision %ld\n"),
notify->old_revision));
return;
case svn_repos_notify_load_skipped_rev:
svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool,
_("<<< Skipped original revision %ld\n"),
notify->old_revision));
return;
case svn_repos_notify_load_normalized_mergeinfo:
svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool,
_(" removing '\\r' from %s ..."),
SVN_PROP_MERGEINFO));
return;
case svn_repos_notify_mutex_acquired:
/* Enable cancellation signal handlers. */
setup_cancellation_signals(signal_handler);
return;
case svn_repos_notify_recover_start:
svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool,
_("Repository lock acquired.\n"
"Please wait; recovering the"
" repository may take some time...\n")));
return;
case svn_repos_notify_upgrade_start:
svn_error_clear(svn_stream_puts(feedback_stream,
_("Repository lock acquired.\n"
"Please wait; upgrading the"
" repository may take some time...\n")));
return;
default:
return;
}
}
/* Baton for recode_write(). */
struct recode_write_baton
{
apr_pool_t *pool;
FILE *out;
};
/* This implements the 'svn_write_fn_t' interface.
Write DATA to ((struct recode_write_baton *) BATON)->out, in the
console encoding, using svn_cmdline_fprintf(). DATA is a
UTF8-encoded C string, therefore ignore LEN.
### This recoding mechanism might want to be abstracted into
### svn_io.h or svn_cmdline.h, if it proves useful elsewhere. */
static svn_error_t *recode_write(void *baton,
const char *data,
apr_size_t *len)
{
struct recode_write_baton *rwb = baton;
svn_pool_clear(rwb->pool);
return svn_cmdline_fputs(data, rwb->out, rwb->pool);
}
/* Create a stream, to write to STD_STREAM, that uses recode_write()
to perform UTF-8 to console encoding translation. */
static svn_stream_t *
recode_stream_create(FILE *std_stream, apr_pool_t *pool)
{
struct recode_write_baton *std_stream_rwb =
apr_palloc(pool, sizeof(struct recode_write_baton));
svn_stream_t *rw_stream = svn_stream_create(std_stream_rwb, pool);
std_stream_rwb->pool = svn_pool_create(pool);
std_stream_rwb->out = std_stream;
svn_stream_set_write(rw_stream, recode_write);
return rw_stream;
}
/* This implements `svn_opt_subcommand_t'. */
static svn_error_t *
subcommand_dump(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
struct svnadmin_opt_state *opt_state = baton;
svn_repos_t *repos;
svn_fs_t *fs;
svn_stream_t *stdout_stream;
svn_revnum_t lower = SVN_INVALID_REVNUM, upper = SVN_INVALID_REVNUM;
svn_revnum_t youngest;
svn_stream_t *progress_stream = NULL;
/* Expect no more arguments. */
SVN_ERR(parse_args(NULL, os, 0, 0, pool));
SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
fs = svn_repos_fs(repos);
SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool));
/* Find the revision numbers at which to start and end. */
SVN_ERR(get_revnum(&lower, &opt_state->start_revision,
youngest, repos, pool));
SVN_ERR(get_revnum(&upper, &opt_state->end_revision,
youngest, repos, pool));
/* Fill in implied revisions if necessary. */
if (lower == SVN_INVALID_REVNUM)
{
lower = 0;
upper = youngest;
}
else if (upper == SVN_INVALID_REVNUM)
{
upper = lower;
}
if (lower > upper)
return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("First revision cannot be higher than second"));
SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool));
/* Progress feedback goes to STDERR, unless they asked to suppress it. */
if (! opt_state->quiet)
progress_stream = recode_stream_create(stderr, pool);
SVN_ERR(svn_repos_dump_fs3(repos, stdout_stream, lower, upper,
opt_state->incremental, opt_state->use_deltas,
!opt_state->quiet ? repos_notify_handler : NULL,
progress_stream, check_cancel, NULL, pool));
return SVN_NO_ERROR;
}
struct freeze_baton_t {
const char *command;
const char **args;
int status;
};
/* Implements svn_repos_freeze_func_t */
static svn_error_t *
freeze_body(void *baton,
apr_pool_t *pool)
{
struct freeze_baton_t *b = baton;
apr_status_t apr_err;
apr_file_t *infile, *outfile, *errfile;
apr_err = apr_file_open_stdin(&infile, pool);
if (apr_err)
return svn_error_wrap_apr(apr_err, "Can't open stdin");
apr_err = apr_file_open_stdout(&outfile, pool);
if (apr_err)
return svn_error_wrap_apr(apr_err, "Can't open stdout");
apr_err = apr_file_open_stderr(&errfile, pool);
if (apr_err)
return svn_error_wrap_apr(apr_err, "Can't open stderr");
SVN_ERR(svn_io_run_cmd(NULL, b->command, b->args, &b->status,
NULL, TRUE,
infile, outfile, errfile, pool));
return SVN_NO_ERROR;
}
static svn_error_t *
subcommand_freeze(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
struct svnadmin_opt_state *opt_state = baton;
apr_array_header_t *paths;
apr_array_header_t *args;
int i;
struct freeze_baton_t b;
SVN_ERR(svn_opt_parse_all_args(&args, os, pool));
if (!args->nelts)
return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0,
_("No program provided"));
if (!opt_state->filedata)
{
/* One repository on the command line. */
paths = apr_array_make(pool, 1, sizeof(const char *));
APR_ARRAY_PUSH(paths, const char *) = opt_state->repository_path;
}
else
{
/* All repositories in filedata. */
paths = svn_cstring_split(opt_state->filedata->data, "\n", FALSE, pool);
}
b.command = APR_ARRAY_IDX(args, 0, const char *);
- b.args = apr_palloc(pool, sizeof(char *) * args->nelts + 1);
+ b.args = apr_palloc(pool, sizeof(char *) * (args->nelts + 1));
for (i = 0; i < args->nelts; ++i)
b.args[i] = APR_ARRAY_IDX(args, i, const char *);
b.args[args->nelts] = NULL;
SVN_ERR(svn_repos_freeze(paths, freeze_body, &b, pool));
/* Make any non-zero status visible to the user. */
if (b.status)
exit(b.status);
return SVN_NO_ERROR;
}
/* This implements `svn_opt_subcommand_t'. */
static svn_error_t *
subcommand_help(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
struct svnadmin_opt_state *opt_state = baton;
const char *header =
_("general usage: svnadmin SUBCOMMAND REPOS_PATH [ARGS & OPTIONS ...]\n"
"Type 'svnadmin help <subcommand>' for help on a specific subcommand.\n"
"Type 'svnadmin --version' to see the program version and FS modules.\n"
"\n"
"Available subcommands:\n");
const char *fs_desc_start
= _("The following repository back-end (FS) modules are available:\n\n");
svn_stringbuf_t *version_footer;
version_footer = svn_stringbuf_create(fs_desc_start, pool);
SVN_ERR(svn_fs_print_modules(version_footer, pool));
SVN_ERR(svn_opt_print_help4(os, "svnadmin",
opt_state ? opt_state->version : FALSE,
opt_state ? opt_state->quiet : FALSE,
/*###opt_state ? opt_state->verbose :*/ FALSE,
version_footer->data,
header, cmd_table, options_table, NULL, NULL,
pool));
return SVN_NO_ERROR;
}
/* Set *REVNUM to the revision number of a numeric REV, or to
SVN_INVALID_REVNUM if REV is unspecified. */
static svn_error_t *
optrev_to_revnum(svn_revnum_t *revnum, const svn_opt_revision_t *opt_rev)
{
if (opt_rev->kind == svn_opt_revision_number)
{
*revnum = opt_rev->value.number;
if (! SVN_IS_VALID_REVNUM(*revnum))
return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("Invalid revision number (%ld) specified"),
*revnum);
}
else if (opt_rev->kind == svn_opt_revision_unspecified)
{
*revnum = SVN_INVALID_REVNUM;
}
else
{
return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("Non-numeric revision specified"));
}
return SVN_NO_ERROR;
}
/* This implements `svn_opt_subcommand_t'. */
static svn_error_t *
subcommand_load(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
svn_error_t *err;
struct svnadmin_opt_state *opt_state = baton;
svn_repos_t *repos;
svn_revnum_t lower = SVN_INVALID_REVNUM, upper = SVN_INVALID_REVNUM;
svn_stream_t *stdin_stream, *stdout_stream = NULL;
/* Expect no more arguments. */
SVN_ERR(parse_args(NULL, os, 0, 0, pool));
/* Find the revision numbers at which to start and end. We only
support a limited set of revision kinds: number and unspecified. */
SVN_ERR(optrev_to_revnum(&lower, &opt_state->start_revision));
SVN_ERR(optrev_to_revnum(&upper, &opt_state->end_revision));
/* Fill in implied revisions if necessary. */
if ((upper == SVN_INVALID_REVNUM) && (lower != SVN_INVALID_REVNUM))
{
upper = lower;
}
else if ((upper != SVN_INVALID_REVNUM) && (lower == SVN_INVALID_REVNUM))
{
lower = upper;
}
/* Ensure correct range ordering. */
if (lower > upper)
{
return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("First revision cannot be higher than second"));
}
SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
/* Read the stream from STDIN. Users can redirect a file. */
SVN_ERR(svn_stream_for_stdin(&stdin_stream, pool));
/* Progress feedback goes to STDOUT, unless they asked to suppress it. */
if (! opt_state->quiet)
stdout_stream = recode_stream_create(stdout, pool);
err = svn_repos_load_fs4(repos, stdin_stream, lower, upper,
opt_state->uuid_action, opt_state->parent_dir,
opt_state->use_pre_commit_hook,
opt_state->use_post_commit_hook,
!opt_state->bypass_prop_validation,
opt_state->quiet ? NULL : repos_notify_handler,
stdout_stream, check_cancel, NULL, pool);
if (err && err->apr_err == SVN_ERR_BAD_PROPERTY_VALUE)
return svn_error_quick_wrap(err,
_("Invalid property value found in "
"dumpstream; consider repairing the source "
"or using --bypass-prop-validation while "
"loading."));
return err;
}
/* This implements `svn_opt_subcommand_t'. */
static svn_error_t *
subcommand_lstxns(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
struct svnadmin_opt_state *opt_state = baton;
svn_repos_t *repos;
svn_fs_t *fs;
apr_array_header_t *txns;
int i;
/* Expect no more arguments. */
SVN_ERR(parse_args(NULL, os, 0, 0, pool));
SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
fs = svn_repos_fs(repos);
SVN_ERR(svn_fs_list_transactions(&txns, fs, pool));
/* Loop, printing revisions. */
for (i = 0; i < txns->nelts; i++)
{
SVN_ERR(svn_cmdline_printf(pool, "%s\n",
APR_ARRAY_IDX(txns, i, const char *)));
}
return SVN_NO_ERROR;
}
/* This implements `svn_opt_subcommand_t'. */
static svn_error_t *
subcommand_recover(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
svn_revnum_t youngest_rev;
svn_repos_t *repos;
svn_error_t *err;
struct svnadmin_opt_state *opt_state = baton;
svn_stream_t *stdout_stream;
/* Expect no more arguments. */
SVN_ERR(parse_args(NULL, os, 0, 0, pool));
SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool));
/* Restore default signal handlers until after we have acquired the
* exclusive lock so that the user interrupt before we actually
* touch the repository. */
setup_cancellation_signals(SIG_DFL);
err = svn_repos_recover4(opt_state->repository_path, TRUE,
repos_notify_handler, stdout_stream,
check_cancel, NULL, pool);
if (err)
{
if (! APR_STATUS_IS_EAGAIN(err->apr_err))
return err;
svn_error_clear(err);
if (! opt_state->wait)
return svn_error_create(SVN_ERR_REPOS_LOCKED, NULL,
_("Failed to get exclusive repository "
"access; perhaps another process\n"
"such as httpd, svnserve or svn "
"has it open?"));
SVN_ERR(svn_cmdline_printf(pool,
_("Waiting on repository lock; perhaps"
" another process has it open?\n")));
SVN_ERR(svn_cmdline_fflush(stdout));
SVN_ERR(svn_repos_recover4(opt_state->repository_path, FALSE,
repos_notify_handler, stdout_stream,
check_cancel, NULL, pool));
}
SVN_ERR(svn_cmdline_printf(pool, _("\nRecovery completed.\n")));
/* Since db transactions may have been replayed, it's nice to tell
people what the latest revision is. It also proves that the
recovery actually worked. */
SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
SVN_ERR(svn_fs_youngest_rev(&youngest_rev, svn_repos_fs(repos), pool));
SVN_ERR(svn_cmdline_printf(pool, _("The latest repos revision is %ld.\n"),
youngest_rev));
return SVN_NO_ERROR;
}
/* This implements `svn_opt_subcommand_t'. */
static svn_error_t *
list_dblogs(apr_getopt_t *os, void *baton, svn_boolean_t only_unused,
apr_pool_t *pool)
{
struct svnadmin_opt_state *opt_state = baton;
apr_array_header_t *logfiles;
int i;
/* Expect no more arguments. */
SVN_ERR(parse_args(NULL, os, 0, 0, pool));
SVN_ERR(svn_repos_db_logfiles(&logfiles,
opt_state->repository_path,
only_unused,
pool));
/* Loop, printing log files. We append the log paths to the
repository path, making sure to return everything to the native
style before printing. */
for (i = 0; i < logfiles->nelts; i++)
{
const char *log_utf8;
log_utf8 = svn_dirent_join(opt_state->repository_path,
APR_ARRAY_IDX(logfiles, i, const char *),
pool);
log_utf8 = svn_dirent_local_style(log_utf8, pool);
SVN_ERR(svn_cmdline_printf(pool, "%s\n", log_utf8));
}
return SVN_NO_ERROR;
}
/* This implements `svn_opt_subcommand_t'. */
static svn_error_t *
subcommand_list_dblogs(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
SVN_ERR(list_dblogs(os, baton, FALSE, pool));
return SVN_NO_ERROR;
}
/* This implements `svn_opt_subcommand_t'. */
static svn_error_t *
subcommand_list_unused_dblogs(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
/* Expect no more arguments. */
SVN_ERR(parse_args(NULL, os, 0, 0, pool));
SVN_ERR(list_dblogs(os, baton, TRUE, pool));
return SVN_NO_ERROR;
}
/* This implements `svn_opt_subcommand_t'. */
static svn_error_t *
subcommand_rmtxns(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
struct svnadmin_opt_state *opt_state = baton;
svn_repos_t *repos;
svn_fs_t *fs;
svn_fs_txn_t *txn;
apr_array_header_t *args;
int i;
apr_pool_t *subpool = svn_pool_create(pool);
SVN_ERR(svn_opt_parse_all_args(&args, os, pool));
SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
fs = svn_repos_fs(repos);
/* All the rest of the arguments are transaction names. */
for (i = 0; i < args->nelts; i++)
{
const char *txn_name = APR_ARRAY_IDX(args, i, const char *);
const char *txn_name_utf8;
svn_error_t *err;
svn_pool_clear(subpool);
SVN_ERR(svn_utf_cstring_to_utf8(&txn_name_utf8, txn_name, subpool));
/* Try to open the txn. If that succeeds, try to abort it. */
err = svn_fs_open_txn(&txn, fs, txn_name_utf8, subpool);
if (! err)
err = svn_fs_abort_txn(txn, subpool);
/* If either the open or the abort of the txn fails because that
transaction is dead, just try to purge the thing. Else,
there was either an error worth reporting, or not error at
all. */
if (err && (err->apr_err == SVN_ERR_FS_TRANSACTION_DEAD))
{
svn_error_clear(err);
err = svn_fs_purge_txn(fs, txn_name_utf8, subpool);
}
/* If we had a real from the txn open, abort, or purge, we clear
that error and just report to the user that we had an issue
with this particular txn. */
if (err)
{
svn_handle_error2(err, stderr, FALSE /* non-fatal */, "svnadmin: ");
svn_error_clear(err);
}
else if (! opt_state->quiet)
{
SVN_ERR(svn_cmdline_printf(subpool, _("Transaction '%s' removed.\n"),
txn_name));
}
}
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
/* A helper for the 'setrevprop' and 'setlog' commands. Expects
OPT_STATE->use_pre_revprop_change_hook and
OPT_STATE->use_post_revprop_change_hook to be set appropriately. */
static svn_error_t *
set_revprop(const char *prop_name, const char *filename,
struct svnadmin_opt_state *opt_state, apr_pool_t *pool)
{
svn_repos_t *repos;
svn_string_t *prop_value = svn_string_create_empty(pool);
svn_stringbuf_t *file_contents;
SVN_ERR(svn_stringbuf_from_file2(&file_contents, filename, pool));
prop_value->data = file_contents->data;
prop_value->len = file_contents->len;
SVN_ERR(svn_subst_translate_string2(&prop_value, NULL, NULL, prop_value,
NULL, FALSE, pool, pool));
/* Open the filesystem */
SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
/* If we are bypassing the hooks system, we just hit the filesystem
directly. */
SVN_ERR(svn_repos_fs_change_rev_prop4(
repos, opt_state->start_revision.value.number,
NULL, prop_name, NULL, prop_value,
opt_state->use_pre_revprop_change_hook,
opt_state->use_post_revprop_change_hook,
NULL, NULL, pool));
return SVN_NO_ERROR;
}
/* This implements `svn_opt_subcommand_t'. */
static svn_error_t *
subcommand_setrevprop(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
struct svnadmin_opt_state *opt_state = baton;
apr_array_header_t *args;
const char *prop_name, *filename;
/* Expect two more arguments: NAME FILE */
SVN_ERR(parse_args(&args, os, 2, 2, pool));
prop_name = APR_ARRAY_IDX(args, 0, const char *);
filename = APR_ARRAY_IDX(args, 1, const char *);
SVN_ERR(target_arg_to_dirent(&filename, filename, pool));
if (opt_state->start_revision.kind != svn_opt_revision_number)
return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("Missing revision"));
else if (opt_state->end_revision.kind != svn_opt_revision_unspecified)
return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("Only one revision allowed"));
return set_revprop(prop_name, filename, opt_state, pool);
}
/* This implements `svn_opt_subcommand_t'. */
static svn_error_t *
subcommand_setuuid(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
struct svnadmin_opt_state *opt_state = baton;
apr_array_header_t *args;
svn_repos_t *repos;
svn_fs_t *fs;
const char *uuid = NULL;
/* Expect zero or one more arguments: [UUID] */
SVN_ERR(parse_args(&args, os, 0, 1, pool));
if (args->nelts == 1)
uuid = APR_ARRAY_IDX(args, 0, const char *);
SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
fs = svn_repos_fs(repos);
return svn_fs_set_uuid(fs, uuid, pool);
}
/* This implements `svn_opt_subcommand_t'. */
static svn_error_t *
subcommand_setlog(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
struct svnadmin_opt_state *opt_state = baton;
apr_array_header_t *args;
const char *filename;
/* Expect one more argument: FILE */
SVN_ERR(parse_args(&args, os, 1, 1, pool));
filename = APR_ARRAY_IDX(args, 0, const char *);
SVN_ERR(target_arg_to_dirent(&filename, filename, pool));
if (opt_state->start_revision.kind != svn_opt_revision_number)
return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("Missing revision"));
else if (opt_state->end_revision.kind != svn_opt_revision_unspecified)
return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("Only one revision allowed"));
/* set_revprop() responds only to pre-/post-revprop-change opts. */
if (!opt_state->bypass_hooks)
{
opt_state->use_pre_revprop_change_hook = TRUE;
opt_state->use_post_revprop_change_hook = TRUE;
}
return set_revprop(SVN_PROP_REVISION_LOG, filename, opt_state, pool);
}
/* This implements 'svn_opt_subcommand_t'. */
static svn_error_t *
subcommand_pack(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
struct svnadmin_opt_state *opt_state = baton;
svn_repos_t *repos;
svn_stream_t *progress_stream = NULL;
/* Expect no more arguments. */
SVN_ERR(parse_args(NULL, os, 0, 0, pool));
SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
/* Progress feedback goes to STDOUT, unless they asked to suppress it. */
if (! opt_state->quiet)
progress_stream = recode_stream_create(stdout, pool);
return svn_error_trace(
svn_repos_fs_pack2(repos, !opt_state->quiet ? repos_notify_handler : NULL,
progress_stream, check_cancel, NULL, pool));
}
/* This implements `svn_opt_subcommand_t'. */
static svn_error_t *
subcommand_verify(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
struct svnadmin_opt_state *opt_state = baton;
svn_repos_t *repos;
svn_fs_t *fs;
svn_revnum_t youngest, lower, upper;
svn_stream_t *progress_stream = NULL;
/* Expect no more arguments. */
SVN_ERR(parse_args(NULL, os, 0, 0, pool));
if (opt_state->txn_id
&& (opt_state->start_revision.kind != svn_opt_revision_unspecified
|| opt_state->end_revision.kind != svn_opt_revision_unspecified))
{
return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("--revision (-r) and --transaction (-t) "
"are mutually exclusive"));
}
SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
fs = svn_repos_fs(repos);
SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool));
/* Usage 2. */
if (opt_state->txn_id)
{
svn_fs_txn_t *txn;
svn_fs_root_t *root;
SVN_ERR(svn_fs_open_txn(&txn, fs, opt_state->txn_id, pool));
SVN_ERR(svn_fs_txn_root(&root, txn, pool));
SVN_ERR(svn_fs_verify_root(root, pool));
return SVN_NO_ERROR;
}
else
/* Usage 1. */
;
/* Find the revision numbers at which to start and end. */
SVN_ERR(get_revnum(&lower, &opt_state->start_revision,
youngest, repos, pool));
SVN_ERR(get_revnum(&upper, &opt_state->end_revision,
youngest, repos, pool));
if (upper == SVN_INVALID_REVNUM)
{
upper = lower;
}
if (! opt_state->quiet)
progress_stream = recode_stream_create(stderr, pool);
return svn_repos_verify_fs2(repos, lower, upper,
!opt_state->quiet
? repos_notify_handler : NULL,
progress_stream, check_cancel, NULL, pool);
}
/* This implements `svn_opt_subcommand_t'. */
svn_error_t *
subcommand_hotcopy(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
struct svnadmin_opt_state *opt_state = baton;
apr_array_header_t *targets;
const char *new_repos_path;
/* Expect one more argument: NEW_REPOS_PATH */
SVN_ERR(parse_args(&targets, os, 1, 1, pool));
new_repos_path = APR_ARRAY_IDX(targets, 0, const char *);
SVN_ERR(target_arg_to_dirent(&new_repos_path, new_repos_path, pool));
return svn_repos_hotcopy2(opt_state->repository_path, new_repos_path,
opt_state->clean_logs, opt_state->incremental,
check_cancel, NULL, pool);
}
/* This implements `svn_opt_subcommand_t'. */
static svn_error_t *
subcommand_lock(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
struct svnadmin_opt_state *opt_state = baton;
svn_repos_t *repos;
svn_fs_t *fs;
svn_fs_access_t *access;
apr_array_header_t *args;
const char *username;
const char *lock_path;
const char *comment_file_name;
svn_stringbuf_t *file_contents;
const char *lock_path_utf8;
svn_lock_t *lock;
const char *lock_token = NULL;
/* Expect three more arguments: PATH USERNAME COMMENT-FILE */
SVN_ERR(parse_args(&args, os, 3, 4, pool));
lock_path = APR_ARRAY_IDX(args, 0, const char *);
username = APR_ARRAY_IDX(args, 1, const char *);
comment_file_name = APR_ARRAY_IDX(args, 2, const char *);
/* Expect one more optional argument: TOKEN */
if (args->nelts == 4)
lock_token = APR_ARRAY_IDX(args, 3, const char *);
SVN_ERR(target_arg_to_dirent(&comment_file_name, comment_file_name, pool));
SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
fs = svn_repos_fs(repos);
/* Create an access context describing the user. */
SVN_ERR(svn_fs_create_access(&access, username, pool));
/* Attach the access context to the filesystem. */
SVN_ERR(svn_fs_set_access(fs, access));
SVN_ERR(svn_stringbuf_from_file2(&file_contents, comment_file_name, pool));
SVN_ERR(svn_utf_cstring_to_utf8(&lock_path_utf8, lock_path, pool));
if (opt_state->bypass_hooks)
SVN_ERR(svn_fs_lock(&lock, fs, lock_path_utf8,
lock_token,
file_contents->data, /* comment */
0, /* is_dav_comment */
0, /* no expiration time. */
SVN_INVALID_REVNUM,
FALSE, pool));
else
SVN_ERR(svn_repos_fs_lock(&lock, repos, lock_path_utf8,
lock_token,
file_contents->data, /* comment */
0, /* is_dav_comment */
0, /* no expiration time. */
SVN_INVALID_REVNUM,
FALSE, pool));
SVN_ERR(svn_cmdline_printf(pool, _("'%s' locked by user '%s'.\n"),
lock_path, username));
return SVN_NO_ERROR;
}
static svn_error_t *
subcommand_lslocks(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
struct svnadmin_opt_state *opt_state = baton;
apr_array_header_t *targets;
svn_repos_t *repos;
const char *fs_path = "/";
apr_hash_t *locks;
apr_hash_index_t *hi;
SVN_ERR(svn_opt__args_to_target_array(&targets, os,
apr_array_make(pool, 0,
sizeof(const char *)),
pool));
if (targets->nelts > 1)
return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0,
_("Too many arguments given"));
if (targets->nelts)
fs_path = APR_ARRAY_IDX(targets, 0, const char *);
SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
/* Fetch all locks on or below the root directory. */
SVN_ERR(svn_repos_fs_get_locks2(&locks, repos, fs_path, svn_depth_infinity,
NULL, NULL, pool));
for (hi = apr_hash_first(pool, locks); hi; hi = apr_hash_next(hi))
{
const char *cr_date, *exp_date = "";
const char *path = svn__apr_hash_index_key(hi);
svn_lock_t *lock = svn__apr_hash_index_val(hi);
int comment_lines = 0;
cr_date = svn_time_to_human_cstring(lock->creation_date, pool);
if (lock->expiration_date)
exp_date = svn_time_to_human_cstring(lock->expiration_date, pool);
if (lock->comment)
comment_lines = svn_cstring_count_newlines(lock->comment) + 1;
SVN_ERR(svn_cmdline_printf(pool, _("Path: %s\n"), path));
SVN_ERR(svn_cmdline_printf(pool, _("UUID Token: %s\n"), lock->token));
SVN_ERR(svn_cmdline_printf(pool, _("Owner: %s\n"), lock->owner));
SVN_ERR(svn_cmdline_printf(pool, _("Created: %s\n"), cr_date));
SVN_ERR(svn_cmdline_printf(pool, _("Expires: %s\n"), exp_date));
SVN_ERR(svn_cmdline_printf(pool,
Q_("Comment (%i line):\n%s\n\n",
"Comment (%i lines):\n%s\n\n",
comment_lines),
comment_lines,
lock->comment ? lock->comment : ""));
}
return SVN_NO_ERROR;
}
static svn_error_t *
subcommand_rmlocks(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
struct svnadmin_opt_state *opt_state = baton;
svn_repos_t *repos;
svn_fs_t *fs;
svn_fs_access_t *access;
svn_error_t *err;
apr_array_header_t *args;
int i;
const char *username;
apr_pool_t *subpool = svn_pool_create(pool);
SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
fs = svn_repos_fs(repos);
/* svn_fs_unlock() demands that some username be associated with the
filesystem, so just use the UID of the person running 'svnadmin'.*/
username = svn_user_get_name(pool);
if (! username)
username = "administrator";
/* Create an access context describing the current user. */
SVN_ERR(svn_fs_create_access(&access, username, pool));
/* Attach the access context to the filesystem. */
SVN_ERR(svn_fs_set_access(fs, access));
/* Parse out any options. */
SVN_ERR(svn_opt_parse_all_args(&args, os, pool));
/* Our usage requires at least one FS path. */
if (args->nelts == 0)
return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0,
_("No paths to unlock provided"));
/* All the rest of the arguments are paths from which to remove locks. */
for (i = 0; i < args->nelts; i++)
{
const char *lock_path = APR_ARRAY_IDX(args, i, const char *);
const char *lock_path_utf8;
svn_lock_t *lock;
SVN_ERR(svn_utf_cstring_to_utf8(&lock_path_utf8, lock_path, subpool));
/* Fetch the path's svn_lock_t. */
err = svn_fs_get_lock(&lock, fs, lock_path_utf8, subpool);
if (err)
goto move_on;
if (! lock)
{
SVN_ERR(svn_cmdline_printf(subpool,
_("Path '%s' isn't locked.\n"),
lock_path));
continue;
}
/* Now forcibly destroy the lock. */
err = svn_fs_unlock(fs, lock_path_utf8,
lock->token, 1 /* force */, subpool);
if (err)
goto move_on;
SVN_ERR(svn_cmdline_printf(subpool,
_("Removed lock on '%s'.\n"), lock->path));
move_on:
if (err)
{
/* Print the error, but move on to the next lock. */
svn_handle_error2(err, stderr, FALSE /* non-fatal */, "svnadmin: ");
svn_error_clear(err);
}
svn_pool_clear(subpool);
}
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
/* This implements `svn_opt_subcommand_t'. */
static svn_error_t *
subcommand_unlock(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
struct svnadmin_opt_state *opt_state = baton;
svn_repos_t *repos;
svn_fs_t *fs;
svn_fs_access_t *access;
apr_array_header_t *args;
const char *username;
const char *lock_path;
const char *lock_path_utf8;
const char *lock_token = NULL;
/* Expect three more arguments: PATH USERNAME TOKEN */
SVN_ERR(parse_args(&args, os, 3, 3, pool));
lock_path = APR_ARRAY_IDX(args, 0, const char *);
username = APR_ARRAY_IDX(args, 1, const char *);
lock_token = APR_ARRAY_IDX(args, 2, const char *);
/* Open the repos/FS, and associate an access context containing
USERNAME. */
SVN_ERR(open_repos(&repos, opt_state->repository_path, pool));
fs = svn_repos_fs(repos);
SVN_ERR(svn_fs_create_access(&access, username, pool));
SVN_ERR(svn_fs_set_access(fs, access));
SVN_ERR(svn_utf_cstring_to_utf8(&lock_path_utf8, lock_path, pool));
if (opt_state->bypass_hooks)
SVN_ERR(svn_fs_unlock(fs, lock_path_utf8, lock_token,
FALSE, pool));
else
SVN_ERR(svn_repos_fs_unlock(repos, lock_path_utf8, lock_token,
FALSE, pool));
SVN_ERR(svn_cmdline_printf(pool, _("'%s' unlocked by user '%s'.\n"),
lock_path, username));
return SVN_NO_ERROR;
}
/* This implements `svn_opt_subcommand_t'. */
static svn_error_t *
subcommand_upgrade(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
svn_error_t *err;
struct svnadmin_opt_state *opt_state = baton;
svn_stream_t *stdout_stream;
/* Expect no more arguments. */
SVN_ERR(parse_args(NULL, os, 0, 0, pool));
SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool));
/* Restore default signal handlers. */
setup_cancellation_signals(SIG_DFL);
err = svn_repos_upgrade2(opt_state->repository_path, TRUE,
repos_notify_handler, stdout_stream, pool);
if (err)
{
if (APR_STATUS_IS_EAGAIN(err->apr_err))
{
svn_error_clear(err);
err = SVN_NO_ERROR;
if (! opt_state->wait)
return svn_error_create(SVN_ERR_REPOS_LOCKED, NULL,
_("Failed to get exclusive repository "
"access; perhaps another process\n"
"such as httpd, svnserve or svn "
"has it open?"));
SVN_ERR(svn_cmdline_printf(pool,
_("Waiting on repository lock; perhaps"
" another process has it open?\n")));
SVN_ERR(svn_cmdline_fflush(stdout));
SVN_ERR(svn_repos_upgrade2(opt_state->repository_path, FALSE,
repos_notify_handler, stdout_stream,
pool));
}
else if (err->apr_err == SVN_ERR_FS_UNSUPPORTED_UPGRADE)
{
return svn_error_quick_wrap(err,
_("Upgrade of this repository's underlying versioned "
"filesystem is not supported; consider "
"dumping and loading the data elsewhere"));
}
else if (err->apr_err == SVN_ERR_REPOS_UNSUPPORTED_UPGRADE)
{
return svn_error_quick_wrap(err,
_("Upgrade of this repository is not supported; consider "
"dumping and loading the data elsewhere"));
}
}
SVN_ERR(err);
SVN_ERR(svn_cmdline_printf(pool, _("\nUpgrade completed.\n")));
return SVN_NO_ERROR;
}
/** Main. **/
/* Report and clear the error ERR, and return EXIT_FAILURE. */
#define EXIT_ERROR(err) \
svn_cmdline_handle_exit_error(err, NULL, "svnadmin: ")
/* A redefinition of the public SVN_INT_ERR macro, that suppresses the
* error message if it is SVN_ERR_IO_PIPE_WRITE_ERROR, amd with the
* program name 'svnadmin' instead of 'svn'. */
#undef SVN_INT_ERR
#define SVN_INT_ERR(expr) \
do { \
svn_error_t *svn_err__temp = (expr); \
if (svn_err__temp) \
return EXIT_ERROR(svn_err__temp); \
} while (0)
static int
sub_main(int argc, const char *argv[], apr_pool_t *pool)
{
svn_error_t *err;
apr_status_t apr_err;
const svn_opt_subcommand_desc2_t *subcommand = NULL;
struct svnadmin_opt_state opt_state = { 0 };
apr_getopt_t *os;
int opt_id;
apr_array_header_t *received_opts;
int i;
svn_boolean_t dash_F_arg = FALSE;
received_opts = apr_array_make(pool, SVN_OPT_MAX_OPTIONS, sizeof(int));
/* Check library versions */
SVN_INT_ERR(check_lib_versions());
/* Initialize the FS library. */
SVN_INT_ERR(svn_fs_initialize(pool));
if (argc <= 1)
{
SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
return EXIT_FAILURE;
}
/* Initialize opt_state. */
opt_state.start_revision.kind = svn_opt_revision_unspecified;
opt_state.end_revision.kind = svn_opt_revision_unspecified;
opt_state.memory_cache_size = svn_cache_config_get()->cache_size;
/* Parse options. */
SVN_INT_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool));
os->interleave = 1;
while (1)
{
const char *opt_arg;
const char *utf8_opt_arg;
/* Parse the next option. */
apr_err = apr_getopt_long(os, options_table, &opt_id, &opt_arg);
if (APR_STATUS_IS_EOF(apr_err))
break;
else if (apr_err)
{
SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
return EXIT_FAILURE;
}
/* Stash the option code in an array before parsing it. */
APR_ARRAY_PUSH(received_opts, int) = opt_id;
switch (opt_id) {
case 'r':
{
if (opt_state.start_revision.kind != svn_opt_revision_unspecified)
{
err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("Multiple revision arguments encountered; "
"try '-r N:M' instead of '-r N -r M'"));
return EXIT_ERROR(err);
}
if (svn_opt_parse_revision(&(opt_state.start_revision),
&(opt_state.end_revision),
opt_arg, pool) != 0)
{
err = svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg,
pool);
if (! err)
err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("Syntax error in revision argument '%s'"),
utf8_opt_arg);
return EXIT_ERROR(err);
}
}
break;
case 't':
opt_state.txn_id = opt_arg;
break;
case 'q':
opt_state.quiet = TRUE;
break;
case 'h':
case '?':
opt_state.help = TRUE;
break;
case 'M':
opt_state.memory_cache_size
= 0x100000 * apr_strtoi64(opt_arg, NULL, 0);
break;
case 'F':
SVN_INT_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool));
SVN_INT_ERR(svn_stringbuf_from_file2(&(opt_state.filedata),
utf8_opt_arg, pool));
dash_F_arg = TRUE;
case svnadmin__version:
opt_state.version = TRUE;
break;
case svnadmin__incremental:
opt_state.incremental = TRUE;
break;
case svnadmin__deltas:
opt_state.use_deltas = TRUE;
break;
case svnadmin__ignore_uuid:
opt_state.uuid_action = svn_repos_load_uuid_ignore;
break;
case svnadmin__force_uuid:
opt_state.uuid_action = svn_repos_load_uuid_force;
break;
case svnadmin__pre_1_4_compatible:
opt_state.pre_1_4_compatible = TRUE;
break;
case svnadmin__pre_1_5_compatible:
opt_state.pre_1_5_compatible = TRUE;
break;
case svnadmin__pre_1_6_compatible:
opt_state.pre_1_6_compatible = TRUE;
break;
case svnadmin__compatible_version:
{
svn_version_t latest = { SVN_VER_MAJOR, SVN_VER_MINOR,
SVN_VER_PATCH, NULL };
svn_version_t *compatible_version;
/* Parse the version string which carries our target
compatibility. */
SVN_INT_ERR(svn_version__parse_version_string(&compatible_version,
opt_arg, pool));
/* We can't create repository with a version older than 1.0.0. */
if (! svn_version__at_least(compatible_version, 1, 0, 0))
{
err = svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Cannot create pre-1.0-compatible "
"repositories"));
return EXIT_ERROR(err);
}
/* We can't create repository with a version newer than what
the running version of Subversion supports. */
if (! svn_version__at_least(&latest,
compatible_version->major,
compatible_version->minor,
compatible_version->patch))
{
err = svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Cannot guarantee compatibility "
"beyond the current running version "
"(%s)"),
SVN_VER_NUM );
return EXIT_ERROR(err);
}
opt_state.compatible_version = compatible_version;
}
break;
case svnadmin__fs_type:
SVN_INT_ERR(svn_utf_cstring_to_utf8(&opt_state.fs_type, opt_arg, pool));
break;
case svnadmin__parent_dir:
SVN_INT_ERR(svn_utf_cstring_to_utf8(&opt_state.parent_dir, opt_arg,
pool));
opt_state.parent_dir
= svn_dirent_internal_style(opt_state.parent_dir, pool);
break;
case svnadmin__use_pre_commit_hook:
opt_state.use_pre_commit_hook = TRUE;
break;
case svnadmin__use_post_commit_hook:
opt_state.use_post_commit_hook = TRUE;
break;
case svnadmin__use_pre_revprop_change_hook:
opt_state.use_pre_revprop_change_hook = TRUE;
break;
case svnadmin__use_post_revprop_change_hook:
opt_state.use_post_revprop_change_hook = TRUE;
break;
case svnadmin__bdb_txn_nosync:
opt_state.bdb_txn_nosync = TRUE;
break;
case svnadmin__bdb_log_keep:
opt_state.bdb_log_keep = TRUE;
break;
case svnadmin__bypass_hooks:
opt_state.bypass_hooks = TRUE;
break;
case svnadmin__bypass_prop_validation:
opt_state.bypass_prop_validation = TRUE;
break;
case svnadmin__clean_logs:
opt_state.clean_logs = TRUE;
break;
case svnadmin__config_dir:
SVN_INT_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool));
opt_state.config_dir =
apr_pstrdup(pool, svn_dirent_canonicalize(utf8_opt_arg, pool));
break;
case svnadmin__wait:
opt_state.wait = TRUE;
break;
default:
{
SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
return EXIT_FAILURE;
}
} /* close `switch' */
} /* close `while' */
/* If the user asked for help, then the rest of the arguments are
the names of subcommands to get help on (if any), or else they're
just typos/mistakes. Whatever the case, the subcommand to
actually run is subcommand_help(). */
if (opt_state.help)
subcommand = svn_opt_get_canonical_subcommand2(cmd_table, "help");
/* If we're not running the `help' subcommand, then look for a
subcommand in the first argument. */
if (subcommand == NULL)
{
if (os->ind >= os->argc)
{
if (opt_state.version)
{
/* Use the "help" subcommand to handle the "--version" option. */
static const svn_opt_subcommand_desc2_t pseudo_cmd =
{ "--version", subcommand_help, {0}, "",
{svnadmin__version, /* must accept its own option */
'q', /* --quiet */
} };
subcommand = &pseudo_cmd;
}
else
{
svn_error_clear(svn_cmdline_fprintf(stderr, pool,
_("subcommand argument required\n")));
SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
return EXIT_FAILURE;
}
}
else
{
const char *first_arg = os->argv[os->ind++];
subcommand = svn_opt_get_canonical_subcommand2(cmd_table, first_arg);
if (subcommand == NULL)
{
const char *first_arg_utf8;
SVN_INT_ERR(svn_utf_cstring_to_utf8(&first_arg_utf8,
first_arg, pool));
svn_error_clear(
svn_cmdline_fprintf(stderr, pool,
_("Unknown subcommand: '%s'\n"),
first_arg_utf8));
SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
return EXIT_FAILURE;
}
}
}
/* Every subcommand except `help' and `freeze' with '-F' require a
second argument -- the repository path. Parse it out here and
store it in opt_state. */
if (!(subcommand->cmd_func == subcommand_help
|| (subcommand->cmd_func == subcommand_freeze && dash_F_arg)))
{
const char *repos_path = NULL;
if (os->ind >= os->argc)
{
err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("Repository argument required"));
return EXIT_ERROR(err);
}
if ((err = svn_utf_cstring_to_utf8(&repos_path,
os->argv[os->ind++], pool)))
{
return EXIT_ERROR(err);
}
if (svn_path_is_url(repos_path))
{
err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("'%s' is a URL when it should be a "
"local path"), repos_path);
return EXIT_ERROR(err);
}
opt_state.repository_path = svn_dirent_internal_style(repos_path, pool);
}
/* Check that the subcommand wasn't passed any inappropriate options. */
for (i = 0; i < received_opts->nelts; i++)
{
opt_id = APR_ARRAY_IDX(received_opts, i, int);
/* All commands implicitly accept --help, so just skip over this
when we see it. Note that we don't want to include this option
in their "accepted options" list because it would be awfully
redundant to display it in every commands' help text. */
if (opt_id == 'h' || opt_id == '?')
continue;
if (! svn_opt_subcommand_takes_option3(subcommand, opt_id, NULL))
{
const char *optstr;
const apr_getopt_option_t *badopt =
svn_opt_get_option_from_code2(opt_id, options_table, subcommand,
pool);
svn_opt_format_option(&optstr, badopt, FALSE, pool);
if (subcommand->name[0] == '-')
SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
else
svn_error_clear(svn_cmdline_fprintf(stderr, pool
, _("Subcommand '%s' doesn't accept option '%s'\n"
"Type 'svnadmin help %s' for usage.\n"),
subcommand->name, optstr, subcommand->name));
return EXIT_FAILURE;
}
}
/* Set up our cancellation support. */
setup_cancellation_signals(signal_handler);
#ifdef SIGPIPE
/* Disable SIGPIPE generation for the platforms that have it. */
apr_signal(SIGPIPE, SIG_IGN);
#endif
#ifdef SIGXFSZ
/* Disable SIGXFSZ generation for the platforms that have it, otherwise
* working with large files when compiled against an APR that doesn't have
* large file support will crash the program, which is uncool. */
apr_signal(SIGXFSZ, SIG_IGN);
#endif
/* Configure FSFS caches for maximum efficiency with svnadmin.
* Also, apply the respective command line parameters, if given. */
{
svn_cache_config_t settings = *svn_cache_config_get();
settings.cache_size = opt_state.memory_cache_size;
settings.single_threaded = TRUE;
svn_cache_config_set(&settings);
}
/* Run the subcommand. */
err = (*subcommand->cmd_func)(os, &opt_state, pool);
if (err)
{
/* For argument-related problems, suggest using the 'help'
subcommand. */
if (err->apr_err == SVN_ERR_CL_INSUFFICIENT_ARGS
|| err->apr_err == SVN_ERR_CL_ARG_PARSING_ERROR)
{
err = svn_error_quick_wrap(err,
_("Try 'svnadmin help' for more info"));
}
return EXIT_ERROR(err);
}
else
{
/* Ensure that everything is written to stdout, so the user will
see any print errors. */
err = svn_cmdline_fflush(stdout);
if (err)
{
return EXIT_ERROR(err);
}
return EXIT_SUCCESS;
}
}
int
main(int argc, const char *argv[])
{
apr_pool_t *pool;
int exit_code;
/* Initialize the app. */
if (svn_cmdline_init("svnadmin", stderr) != EXIT_SUCCESS)
return EXIT_FAILURE;
/* Create our top-level pool. Use a separate mutexless allocator,
* given this application is single threaded.
*/
pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
exit_code = sub_main(argc, argv, pool);
svn_pool_destroy(pool);
return exit_code;
}
Index: vendor/subversion/dist/subversion/svndumpfilter/svndumpfilter.c
===================================================================
--- vendor/subversion/dist/subversion/svndumpfilter/svndumpfilter.c (revision 286500)
+++ vendor/subversion/dist/subversion/svndumpfilter/svndumpfilter.c (revision 286501)
@@ -1,1688 +1,1687 @@
/*
* svndumpfilter.c: Subversion dump stream filtering tool main file.
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include <stdlib.h>
#include <apr_file_io.h>
#include "svn_private_config.h"
#include "svn_cmdline.h"
#include "svn_error.h"
#include "svn_string.h"
#include "svn_opt.h"
#include "svn_utf.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_hash.h"
#include "svn_repos.h"
#include "svn_fs.h"
#include "svn_pools.h"
#include "svn_sorts.h"
#include "svn_props.h"
#include "svn_mergeinfo.h"
#include "svn_version.h"
#include "private/svn_mergeinfo_private.h"
#include "private/svn_cmdline_private.h"
#include "private/svn_subr_private.h"
#ifdef _WIN32
typedef apr_status_t (__stdcall *open_fn_t)(apr_file_t **, apr_pool_t *);
#else
typedef apr_status_t (*open_fn_t)(apr_file_t **, apr_pool_t *);
#endif
/*** Code. ***/
/* Helper to open stdio streams */
/* NOTE: we used to call svn_stream_from_stdio(), which wraps a stream
around a standard stdio.h FILE pointer. The problem is that these
pointers operate through C Run Time (CRT) on Win32, which does all
sorts of translation on them: LF's become CRLF's, and ctrl-Z's
embedded in Word documents are interpreted as premature EOF's.
So instead, we use apr_file_open_std*, which bypass the CRT and
directly wrap the OS's file-handles, which don't know or care about
translation. Thus dump/load works correctly on Win32.
*/
static svn_error_t *
create_stdio_stream(svn_stream_t **stream,
open_fn_t open_fn,
apr_pool_t *pool)
{
apr_file_t *stdio_file;
apr_status_t apr_err = open_fn(&stdio_file, pool);
if (apr_err)
return svn_error_wrap_apr(apr_err, _("Can't open stdio file"));
*stream = svn_stream_from_aprfile2(stdio_file, TRUE, pool);
return SVN_NO_ERROR;
}
/* Writes a property in dumpfile format to given stringbuf. */
static void
write_prop_to_stringbuf(svn_stringbuf_t *strbuf,
const char *name,
const svn_string_t *value)
{
int bytes_used;
size_t namelen;
char buf[SVN_KEYLINE_MAXLEN];
/* Output name length, then name. */
namelen = strlen(name);
svn_stringbuf_appendbytes(strbuf, "K ", 2);
bytes_used = apr_snprintf(buf, sizeof(buf), "%" APR_SIZE_T_FMT, namelen);
svn_stringbuf_appendbytes(strbuf, buf, bytes_used);
svn_stringbuf_appendbyte(strbuf, '\n');
svn_stringbuf_appendbytes(strbuf, name, namelen);
svn_stringbuf_appendbyte(strbuf, '\n');
/* Output value length, then value. */
svn_stringbuf_appendbytes(strbuf, "V ", 2);
bytes_used = apr_snprintf(buf, sizeof(buf), "%" APR_SIZE_T_FMT, value->len);
svn_stringbuf_appendbytes(strbuf, buf, bytes_used);
svn_stringbuf_appendbyte(strbuf, '\n');
svn_stringbuf_appendbytes(strbuf, value->data, value->len);
svn_stringbuf_appendbyte(strbuf, '\n');
}
/* Writes a property deletion in dumpfile format to given stringbuf. */
static void
write_propdel_to_stringbuf(svn_stringbuf_t **strbuf,
const char *name)
{
int bytes_used;
size_t namelen;
char buf[SVN_KEYLINE_MAXLEN];
/* Output name length, then name. */
namelen = strlen(name);
svn_stringbuf_appendbytes(*strbuf, "D ", 2);
bytes_used = apr_snprintf(buf, sizeof(buf), "%" APR_SIZE_T_FMT, namelen);
svn_stringbuf_appendbytes(*strbuf, buf, bytes_used);
svn_stringbuf_appendbyte(*strbuf, '\n');
svn_stringbuf_appendbytes(*strbuf, name, namelen);
svn_stringbuf_appendbyte(*strbuf, '\n');
}
/* Compare the node-path PATH with the (const char *) prefixes in PFXLIST.
* Return TRUE if any prefix is a prefix of PATH (matching whole path
* components); FALSE otherwise.
* PATH starts with a '/', as do the (const char *) paths in PREFIXES. */
static svn_boolean_t
ary_prefix_match(const apr_array_header_t *pfxlist, const char *path)
{
int i;
size_t path_len = strlen(path);
for (i = 0; i < pfxlist->nelts; i++)
{
const char *pfx = APR_ARRAY_IDX(pfxlist, i, const char *);
size_t pfx_len = strlen(pfx);
if (path_len < pfx_len)
continue;
if (strncmp(path, pfx, pfx_len) == 0
&& (pfx_len == 1 || path[pfx_len] == '\0' || path[pfx_len] == '/'))
return TRUE;
}
return FALSE;
}
/* Check whether we need to skip this PATH based on its presence in
the PREFIXES list, and the DO_EXCLUDE option.
PATH starts with a '/', as do the (const char *) paths in PREFIXES. */
static APR_INLINE svn_boolean_t
skip_path(const char *path, const apr_array_header_t *prefixes,
svn_boolean_t do_exclude, svn_boolean_t glob)
{
const svn_boolean_t matches =
(glob
? svn_cstring_match_glob_list(path, prefixes)
: ary_prefix_match(prefixes, path));
/* NXOR */
return (matches ? do_exclude : !do_exclude);
}
/* Note: the input stream parser calls us with events.
Output of the filtered dump occurs for the most part streamily with the
event callbacks, to avoid caching large quantities of data in memory.
The exceptions this are:
- All revision data (headers and props) must be cached until a non-skipped
node within the revision is found, or the revision is closed.
- Node headers and props must be cached until all props have been received
(to allow the Prop-content-length to be found). This is signalled either
by the node text arriving, or the node being closed.
The writing_begun members of the associated object batons track the state.
output_revision() and output_node() are called to cause this flushing of
cached data to occur.
*/
/* Filtering batons */
struct revmap_t
{
svn_revnum_t rev; /* Last non-dropped revision to which this maps. */
svn_boolean_t was_dropped; /* Was this revision dropped? */
};
struct parse_baton_t
{
/* Command-line options values. */
svn_boolean_t do_exclude;
svn_boolean_t quiet;
svn_boolean_t glob;
svn_boolean_t drop_empty_revs;
svn_boolean_t drop_all_empty_revs;
svn_boolean_t do_renumber_revs;
svn_boolean_t preserve_revprops;
svn_boolean_t skip_missing_merge_sources;
svn_boolean_t allow_deltas;
apr_array_header_t *prefixes;
/* Input and output streams. */
svn_stream_t *in_stream;
svn_stream_t *out_stream;
/* State for the filtering process. */
apr_int32_t rev_drop_count;
apr_hash_t *dropped_nodes;
apr_hash_t *renumber_history; /* svn_revnum_t -> struct revmap_t */
svn_revnum_t last_live_revision;
/* The oldest original revision, greater than r0, in the input
stream which was not filtered. */
svn_revnum_t oldest_original_rev;
};
struct revision_baton_t
{
/* Reference to the global parse baton. */
struct parse_baton_t *pb;
/* Does this revision have node or prop changes? */
svn_boolean_t has_nodes;
svn_boolean_t has_props;
/* Did we drop any nodes? */
svn_boolean_t had_dropped_nodes;
/* Written to output stream? */
svn_boolean_t writing_begun;
/* The original and new (re-mapped) revision numbers. */
svn_revnum_t rev_orig;
svn_revnum_t rev_actual;
/* Pointers to dumpfile data. */
svn_stringbuf_t *header;
apr_hash_t *props;
};
struct node_baton_t
{
/* Reference to the current revision baton. */
struct revision_baton_t *rb;
/* Are we skipping this node? */
svn_boolean_t do_skip;
/* Have we been instructed to change or remove props on, or change
the text of, this node? */
svn_boolean_t has_props;
svn_boolean_t has_text;
/* Written to output stream? */
svn_boolean_t writing_begun;
/* The text content length according to the dumpfile headers, because we
need the length before we have the actual text. */
svn_filesize_t tcl;
/* Pointers to dumpfile data. */
svn_stringbuf_t *header;
svn_stringbuf_t *props;
/* Expect deltas? */
svn_boolean_t has_prop_delta;
svn_boolean_t has_text_delta;
/* We might need the node path in a parse error message. */
char *node_path;
};
/* Filtering vtable members */
/* File-format stamp. */
static svn_error_t *
magic_header_record(int version, void *parse_baton, apr_pool_t *pool)
{
struct parse_baton_t *pb = parse_baton;
if (version >= SVN_REPOS_DUMPFILE_FORMAT_VERSION_DELTAS)
pb->allow_deltas = TRUE;
SVN_ERR(svn_stream_printf(pb->out_stream, pool,
SVN_REPOS_DUMPFILE_MAGIC_HEADER ": %d\n\n",
version));
return SVN_NO_ERROR;
}
/* New revision: set up revision_baton, decide if we skip it. */
static svn_error_t *
new_revision_record(void **revision_baton,
apr_hash_t *headers,
void *parse_baton,
apr_pool_t *pool)
{
struct revision_baton_t *rb;
apr_hash_index_t *hi;
const char *rev_orig;
svn_stream_t *header_stream;
*revision_baton = apr_palloc(pool, sizeof(struct revision_baton_t));
rb = *revision_baton;
rb->pb = parse_baton;
rb->has_nodes = FALSE;
rb->has_props = FALSE;
rb->had_dropped_nodes = FALSE;
rb->writing_begun = FALSE;
rb->header = svn_stringbuf_create_empty(pool);
rb->props = apr_hash_make(pool);
header_stream = svn_stream_from_stringbuf(rb->header, pool);
rev_orig = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_REVISION_NUMBER);
rb->rev_orig = SVN_STR_TO_REV(rev_orig);
if (rb->pb->do_renumber_revs)
rb->rev_actual = rb->rev_orig - rb->pb->rev_drop_count;
else
rb->rev_actual = rb->rev_orig;
SVN_ERR(svn_stream_printf(header_stream, pool,
SVN_REPOS_DUMPFILE_REVISION_NUMBER ": %ld\n",
rb->rev_actual));
for (hi = apr_hash_first(pool, headers); hi; hi = apr_hash_next(hi))
{
const char *key = svn__apr_hash_index_key(hi);
const char *val = svn__apr_hash_index_val(hi);
if ((!strcmp(key, SVN_REPOS_DUMPFILE_CONTENT_LENGTH))
|| (!strcmp(key, SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH))
|| (!strcmp(key, SVN_REPOS_DUMPFILE_REVISION_NUMBER)))
continue;
/* passthru: put header into header stringbuf. */
SVN_ERR(svn_stream_printf(header_stream, pool, "%s: %s\n",
key, val));
}
SVN_ERR(svn_stream_close(header_stream));
return SVN_NO_ERROR;
}
/* Output revision to dumpstream
This may be called by new_node_record(), iff rb->has_nodes has been set
to TRUE, or by close_revision() otherwise. This must only be called
if rb->writing_begun is FALSE. */
static svn_error_t *
output_revision(struct revision_baton_t *rb)
{
int bytes_used;
char buf[SVN_KEYLINE_MAXLEN];
apr_hash_index_t *hi;
svn_boolean_t write_out_rev = FALSE;
apr_pool_t *hash_pool = apr_hash_pool_get(rb->props);
svn_stringbuf_t *props = svn_stringbuf_create_empty(hash_pool);
apr_pool_t *subpool = svn_pool_create(hash_pool);
rb->writing_begun = TRUE;
/* If this revision has no nodes left because the ones it had were
dropped, and we are not dropping empty revisions, and we were not
told to preserve revision props, then we want to fixup the
revision props to only contain:
- the date
- a log message that reports that this revision is just stuffing. */
if ((! rb->pb->preserve_revprops)
&& (! rb->has_nodes)
&& rb->had_dropped_nodes
&& (! rb->pb->drop_empty_revs)
&& (! rb->pb->drop_all_empty_revs))
{
apr_hash_t *old_props = rb->props;
rb->has_props = TRUE;
rb->props = apr_hash_make(hash_pool);
svn_hash_sets(rb->props, SVN_PROP_REVISION_DATE,
svn_hash_gets(old_props, SVN_PROP_REVISION_DATE));
svn_hash_sets(rb->props, SVN_PROP_REVISION_LOG,
svn_string_create(_("This is an empty revision for "
"padding."), hash_pool));
}
/* Now, "rasterize" the props to a string, and append the property
information to the header string. */
if (rb->has_props)
{
for (hi = apr_hash_first(subpool, rb->props);
hi;
hi = apr_hash_next(hi))
{
const char *pname = svn__apr_hash_index_key(hi);
const svn_string_t *pval = svn__apr_hash_index_val(hi);
write_prop_to_stringbuf(props, pname, pval);
}
svn_stringbuf_appendcstr(props, "PROPS-END\n");
svn_stringbuf_appendcstr(rb->header,
SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH);
bytes_used = apr_snprintf(buf, sizeof(buf), ": %" APR_SIZE_T_FMT,
props->len);
svn_stringbuf_appendbytes(rb->header, buf, bytes_used);
svn_stringbuf_appendbyte(rb->header, '\n');
}
svn_stringbuf_appendcstr(rb->header, SVN_REPOS_DUMPFILE_CONTENT_LENGTH);
bytes_used = apr_snprintf(buf, sizeof(buf), ": %" APR_SIZE_T_FMT, props->len);
svn_stringbuf_appendbytes(rb->header, buf, bytes_used);
svn_stringbuf_appendbyte(rb->header, '\n');
/* put an end to headers */
svn_stringbuf_appendbyte(rb->header, '\n');
/* put an end to revision */
svn_stringbuf_appendbyte(props, '\n');
/* write out the revision */
/* Revision is written out in the following cases:
1. If the revision has nodes or
it is revision 0 (Special case: To preserve the props on r0).
2. --drop-empty-revs has been supplied,
but revision has not all nodes dropped.
3. If no --drop-empty-revs or --drop-all-empty-revs have been supplied,
write out the revision which has no nodes to begin with.
*/
if (rb->has_nodes || (rb->rev_orig == 0))
write_out_rev = TRUE;
else if (rb->pb->drop_empty_revs)
write_out_rev = ! rb->had_dropped_nodes;
else if (! rb->pb->drop_all_empty_revs)
write_out_rev = TRUE;
if (write_out_rev)
{
/* This revision is a keeper. */
SVN_ERR(svn_stream_write(rb->pb->out_stream,
rb->header->data, &(rb->header->len)));
SVN_ERR(svn_stream_write(rb->pb->out_stream,
props->data, &(props->len)));
/* Stash the oldest original rev not dropped. */
if (rb->rev_orig > 0
&& !SVN_IS_VALID_REVNUM(rb->pb->oldest_original_rev))
rb->pb->oldest_original_rev = rb->rev_orig;
if (rb->pb->do_renumber_revs)
{
svn_revnum_t *rr_key;
struct revmap_t *rr_val;
apr_pool_t *rr_pool = apr_hash_pool_get(rb->pb->renumber_history);
rr_key = apr_palloc(rr_pool, sizeof(*rr_key));
rr_val = apr_palloc(rr_pool, sizeof(*rr_val));
*rr_key = rb->rev_orig;
rr_val->rev = rb->rev_actual;
rr_val->was_dropped = FALSE;
apr_hash_set(rb->pb->renumber_history, rr_key,
sizeof(*rr_key), rr_val);
rb->pb->last_live_revision = rb->rev_actual;
}
if (! rb->pb->quiet)
SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
_("Revision %ld committed as %ld.\n"),
rb->rev_orig, rb->rev_actual));
}
else
{
/* We're dropping this revision. */
rb->pb->rev_drop_count++;
if (rb->pb->do_renumber_revs)
{
svn_revnum_t *rr_key;
struct revmap_t *rr_val;
apr_pool_t *rr_pool = apr_hash_pool_get(rb->pb->renumber_history);
rr_key = apr_palloc(rr_pool, sizeof(*rr_key));
rr_val = apr_palloc(rr_pool, sizeof(*rr_val));
*rr_key = rb->rev_orig;
rr_val->rev = rb->pb->last_live_revision;
rr_val->was_dropped = TRUE;
apr_hash_set(rb->pb->renumber_history, rr_key,
sizeof(*rr_key), rr_val);
}
if (! rb->pb->quiet)
SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
_("Revision %ld skipped.\n"),
rb->rev_orig));
}
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
/* UUID record here: dump it, as we do not filter them. */
static svn_error_t *
uuid_record(const char *uuid, void *parse_baton, apr_pool_t *pool)
{
struct parse_baton_t *pb = parse_baton;
SVN_ERR(svn_stream_printf(pb->out_stream, pool,
SVN_REPOS_DUMPFILE_UUID ": %s\n\n", uuid));
return SVN_NO_ERROR;
}
/* New node here. Set up node_baton by copying headers. */
static svn_error_t *
new_node_record(void **node_baton,
apr_hash_t *headers,
void *rev_baton,
apr_pool_t *pool)
{
struct parse_baton_t *pb;
struct node_baton_t *nb;
char *node_path, *copyfrom_path;
apr_hash_index_t *hi;
const char *tcl;
*node_baton = apr_palloc(pool, sizeof(struct node_baton_t));
nb = *node_baton;
nb->rb = rev_baton;
pb = nb->rb->pb;
node_path = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_PATH);
copyfrom_path = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH);
/* Ensure that paths start with a leading '/'. */
if (node_path[0] != '/')
node_path = apr_pstrcat(pool, "/", node_path, (char *)NULL);
if (copyfrom_path && copyfrom_path[0] != '/')
copyfrom_path = apr_pstrcat(pool, "/", copyfrom_path, (char *)NULL);
nb->do_skip = skip_path(node_path, pb->prefixes,
pb->do_exclude, pb->glob);
/* If we're skipping the node, take note of path, discarding the
rest. */
if (nb->do_skip)
{
svn_hash_sets(pb->dropped_nodes,
apr_pstrdup(apr_hash_pool_get(pb->dropped_nodes),
node_path),
(void *)1);
nb->rb->had_dropped_nodes = TRUE;
}
else
{
const char *kind;
const char *action;
tcl = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH);
/* Test if this node was copied from dropped source. */
if (copyfrom_path &&
skip_path(copyfrom_path, pb->prefixes, pb->do_exclude, pb->glob))
{
/* This node was copied from a dropped source.
We have a problem, since we did not want to drop this node too.
However, there is one special case we'll handle. If the node is
a file, and this was a copy-and-modify operation, then the
dumpfile should contain the new contents of the file. In this
scenario, we'll just do an add without history using the new
contents. */
kind = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_KIND);
/* If there is a Text-content-length header, and the kind is
"file", we just fallback to an add without history. */
if (tcl && (strcmp(kind, "file") == 0))
{
svn_hash_sets(headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH,
NULL);
svn_hash_sets(headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV,
NULL);
copyfrom_path = NULL;
}
/* Else, this is either a directory or a file whose contents we
don't have readily available. */
else
{
return svn_error_createf
(SVN_ERR_INCOMPLETE_DATA, 0,
_("Invalid copy source path '%s'"), copyfrom_path);
}
}
nb->has_props = FALSE;
nb->has_text = FALSE;
nb->has_prop_delta = FALSE;
nb->has_text_delta = FALSE;
nb->writing_begun = FALSE;
nb->tcl = tcl ? svn__atoui64(tcl) : 0;
nb->header = svn_stringbuf_create_empty(pool);
nb->props = svn_stringbuf_create_empty(pool);
nb->node_path = apr_pstrdup(pool, node_path);
/* Now we know for sure that we have a node that will not be
skipped, flush the revision if it has not already been done. */
nb->rb->has_nodes = TRUE;
if (! nb->rb->writing_begun)
SVN_ERR(output_revision(nb->rb));
/* A node record is required to begin with 'Node-path', skip the
leading '/' to match the form used by 'svnadmin dump'. */
SVN_ERR(svn_stream_printf(nb->rb->pb->out_stream,
pool, "%s: %s\n",
SVN_REPOS_DUMPFILE_NODE_PATH, node_path + 1));
/* Node-kind is next and is optional. */
kind = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_KIND);
if (kind)
SVN_ERR(svn_stream_printf(nb->rb->pb->out_stream,
pool, "%s: %s\n",
SVN_REPOS_DUMPFILE_NODE_KIND, kind));
/* Node-action is next and required. */
action = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_ACTION);
if (action)
SVN_ERR(svn_stream_printf(nb->rb->pb->out_stream,
pool, "%s: %s\n",
SVN_REPOS_DUMPFILE_NODE_ACTION, action));
else
return svn_error_createf(SVN_ERR_INCOMPLETE_DATA, 0,
_("Missing Node-action for path '%s'"),
node_path);
for (hi = apr_hash_first(pool, headers); hi; hi = apr_hash_next(hi))
{
const char *key = svn__apr_hash_index_key(hi);
const char *val = svn__apr_hash_index_val(hi);
if ((!strcmp(key, SVN_REPOS_DUMPFILE_PROP_DELTA))
&& (!strcmp(val, "true")))
nb->has_prop_delta = TRUE;
if ((!strcmp(key, SVN_REPOS_DUMPFILE_TEXT_DELTA))
&& (!strcmp(val, "true")))
nb->has_text_delta = TRUE;
if ((!strcmp(key, SVN_REPOS_DUMPFILE_CONTENT_LENGTH))
|| (!strcmp(key, SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH))
|| (!strcmp(key, SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH))
|| (!strcmp(key, SVN_REPOS_DUMPFILE_NODE_PATH))
|| (!strcmp(key, SVN_REPOS_DUMPFILE_NODE_KIND))
|| (!strcmp(key, SVN_REPOS_DUMPFILE_NODE_ACTION)))
continue;
/* Rewrite Node-Copyfrom-Rev if we are renumbering revisions.
The number points to some revision in the past. We keep track
of revision renumbering in an apr_hash, which maps original
revisions to new ones. Dropped revision are mapped to -1.
This should never happen here.
*/
if (pb->do_renumber_revs
&& (!strcmp(key, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV)))
{
svn_revnum_t cf_orig_rev;
struct revmap_t *cf_renum_val;
cf_orig_rev = SVN_STR_TO_REV(val);
cf_renum_val = apr_hash_get(pb->renumber_history,
&cf_orig_rev,
sizeof(svn_revnum_t));
if (! (cf_renum_val && SVN_IS_VALID_REVNUM(cf_renum_val->rev)))
return svn_error_createf
(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
_("No valid copyfrom revision in filtered stream"));
SVN_ERR(svn_stream_printf
(nb->rb->pb->out_stream, pool,
SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV ": %ld\n",
cf_renum_val->rev));
continue;
}
/* passthru: put header straight to output */
SVN_ERR(svn_stream_printf(nb->rb->pb->out_stream,
pool, "%s: %s\n",
key, val));
}
}
return SVN_NO_ERROR;
}
/* Output node header and props to dumpstream
This will be called by set_fulltext() after setting nb->has_text to TRUE,
if the node has any text, or by close_node() otherwise. This must only
be called if nb->writing_begun is FALSE. */
static svn_error_t *
output_node(struct node_baton_t *nb)
{
int bytes_used;
char buf[SVN_KEYLINE_MAXLEN];
nb->writing_begun = TRUE;
/* when there are no props nb->props->len would be zero and won't mess up
Content-Length. */
if (nb->has_props)
svn_stringbuf_appendcstr(nb->props, "PROPS-END\n");
/* 1. recalculate & check text-md5 if present. Passed through right now. */
/* 2. recalculate and add content-lengths */
if (nb->has_props)
{
svn_stringbuf_appendcstr(nb->header,
SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH);
bytes_used = apr_snprintf(buf, sizeof(buf), ": %" APR_SIZE_T_FMT,
nb->props->len);
svn_stringbuf_appendbytes(nb->header, buf, bytes_used);
svn_stringbuf_appendbyte(nb->header, '\n');
}
if (nb->has_text)
{
svn_stringbuf_appendcstr(nb->header,
SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH);
bytes_used = apr_snprintf(buf, sizeof(buf), ": %" SVN_FILESIZE_T_FMT,
nb->tcl);
svn_stringbuf_appendbytes(nb->header, buf, bytes_used);
svn_stringbuf_appendbyte(nb->header, '\n');
}
svn_stringbuf_appendcstr(nb->header, SVN_REPOS_DUMPFILE_CONTENT_LENGTH);
bytes_used = apr_snprintf(buf, sizeof(buf), ": %" SVN_FILESIZE_T_FMT,
(svn_filesize_t) (nb->props->len + nb->tcl));
svn_stringbuf_appendbytes(nb->header, buf, bytes_used);
svn_stringbuf_appendbyte(nb->header, '\n');
/* put an end to headers */
svn_stringbuf_appendbyte(nb->header, '\n');
/* 3. output all the stuff */
SVN_ERR(svn_stream_write(nb->rb->pb->out_stream,
nb->header->data , &(nb->header->len)));
SVN_ERR(svn_stream_write(nb->rb->pb->out_stream,
nb->props->data , &(nb->props->len)));
return SVN_NO_ERROR;
}
/* Examine the mergeinfo in INITIAL_VAL, omitting missing merge
sources or renumbering revisions in rangelists as appropriate, and
return the (possibly new) mergeinfo in *FINAL_VAL (allocated from
POOL). */
static svn_error_t *
adjust_mergeinfo(svn_string_t **final_val, const svn_string_t *initial_val,
struct revision_baton_t *rb, apr_pool_t *pool)
{
apr_hash_t *mergeinfo;
apr_hash_t *final_mergeinfo = apr_hash_make(pool);
apr_hash_index_t *hi;
apr_pool_t *subpool = svn_pool_create(pool);
SVN_ERR(svn_mergeinfo_parse(&mergeinfo, initial_val->data, subpool));
/* Issue #3020: If we are skipping missing merge sources, then also
filter mergeinfo ranges as old or older than the oldest revision in the
dump stream. Those older than the oldest obviously refer to history
outside of the dump stream. The oldest rev itself is present in the
dump, but cannot be a valid merge source revision since it is the
start of all history. E.g. if we dump -r100:400 then dumpfilter the
result with --skip-missing-merge-sources, any mergeinfo with revision
100 implies a change of -r99:100, but r99 is part of the history we
- want filtered. This is analogous to how r1 is always meaningless as
- a merge source revision.
+ want filtered.
If the oldest rev is r0 then there is nothing to filter. */
if (rb->pb->skip_missing_merge_sources && rb->pb->oldest_original_rev > 0)
SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(
&mergeinfo, mergeinfo,
rb->pb->oldest_original_rev, 0,
FALSE, subpool, subpool));
for (hi = apr_hash_first(subpool, mergeinfo); hi; hi = apr_hash_next(hi))
{
const char *merge_source = svn__apr_hash_index_key(hi);
svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
struct parse_baton_t *pb = rb->pb;
/* Determine whether the merge_source is a part of the prefix. */
if (skip_path(merge_source, pb->prefixes, pb->do_exclude, pb->glob))
{
if (pb->skip_missing_merge_sources)
continue;
else
return svn_error_createf(SVN_ERR_INCOMPLETE_DATA, 0,
_("Missing merge source path '%s'; try "
"with --skip-missing-merge-sources"),
merge_source);
}
/* Possibly renumber revisions in merge source's rangelist. */
if (pb->do_renumber_revs)
{
int i;
for (i = 0; i < rangelist->nelts; i++)
{
struct revmap_t *revmap_start;
struct revmap_t *revmap_end;
svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i,
svn_merge_range_t *);
revmap_start = apr_hash_get(pb->renumber_history,
&range->start, sizeof(svn_revnum_t));
if (! (revmap_start && SVN_IS_VALID_REVNUM(revmap_start->rev)))
return svn_error_createf
(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
_("No valid revision range 'start' in filtered stream"));
revmap_end = apr_hash_get(pb->renumber_history,
&range->end, sizeof(svn_revnum_t));
if (! (revmap_end && SVN_IS_VALID_REVNUM(revmap_end->rev)))
return svn_error_createf
(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
_("No valid revision range 'end' in filtered stream"));
range->start = revmap_start->rev;
range->end = revmap_end->rev;
}
}
svn_hash_sets(final_mergeinfo, merge_source, rangelist);
}
- SVN_ERR(svn_mergeinfo_sort(final_mergeinfo, subpool));
+ SVN_ERR(svn_mergeinfo__canonicalize_ranges(final_mergeinfo, subpool));
SVN_ERR(svn_mergeinfo_to_string(final_val, final_mergeinfo, pool));
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
static svn_error_t *
set_revision_property(void *revision_baton,
const char *name,
const svn_string_t *value)
{
struct revision_baton_t *rb = revision_baton;
apr_pool_t *hash_pool = apr_hash_pool_get(rb->props);
rb->has_props = TRUE;
svn_hash_sets(rb->props,
apr_pstrdup(hash_pool, name),
svn_string_dup(value, hash_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
set_node_property(void *node_baton,
const char *name,
const svn_string_t *value)
{
struct node_baton_t *nb = node_baton;
struct revision_baton_t *rb = nb->rb;
if (nb->do_skip)
return SVN_NO_ERROR;
if (! (nb->has_props || nb->has_prop_delta))
return svn_error_createf(SVN_ERR_STREAM_MALFORMED_DATA, NULL,
_("Delta property block detected, but deltas "
"are not enabled for node '%s' in original "
"revision %ld"),
nb->node_path, rb->rev_orig);
if (strcmp(name, SVN_PROP_MERGEINFO) == 0)
{
svn_string_t *filtered_mergeinfo; /* Avoid compiler warning. */
apr_pool_t *pool = apr_hash_pool_get(rb->props);
SVN_ERR(adjust_mergeinfo(&filtered_mergeinfo, value, rb, pool));
value = filtered_mergeinfo;
}
nb->has_props = TRUE;
write_prop_to_stringbuf(nb->props, name, value);
return SVN_NO_ERROR;
}
static svn_error_t *
delete_node_property(void *node_baton, const char *name)
{
struct node_baton_t *nb = node_baton;
struct revision_baton_t *rb = nb->rb;
if (nb->do_skip)
return SVN_NO_ERROR;
if (!nb->has_prop_delta)
return svn_error_createf(SVN_ERR_STREAM_MALFORMED_DATA, NULL,
_("Delta property block detected, but deltas "
"are not enabled for node '%s' in original "
"revision %ld"),
nb->node_path, rb->rev_orig);
nb->has_props = TRUE;
write_propdel_to_stringbuf(&(nb->props), name);
return SVN_NO_ERROR;
}
static svn_error_t *
remove_node_props(void *node_baton)
{
struct node_baton_t *nb = node_baton;
/* In this case, not actually indicating that the node *has* props,
rather that we know about all the props that it has, since it now
has none. */
nb->has_props = TRUE;
return SVN_NO_ERROR;
}
static svn_error_t *
set_fulltext(svn_stream_t **stream, void *node_baton)
{
struct node_baton_t *nb = node_baton;
if (!nb->do_skip)
{
nb->has_text = TRUE;
if (! nb->writing_begun)
SVN_ERR(output_node(nb));
*stream = nb->rb->pb->out_stream;
}
return SVN_NO_ERROR;
}
/* Finalize node */
static svn_error_t *
close_node(void *node_baton)
{
struct node_baton_t *nb = node_baton;
apr_size_t len = 2;
/* Get out of here if we can. */
if (nb->do_skip)
return SVN_NO_ERROR;
/* If the node was not flushed already to output its text, do it now. */
if (! nb->writing_begun)
SVN_ERR(output_node(nb));
/* put an end to node. */
SVN_ERR(svn_stream_write(nb->rb->pb->out_stream, "\n\n", &len));
return SVN_NO_ERROR;
}
/* Finalize revision */
static svn_error_t *
close_revision(void *revision_baton)
{
struct revision_baton_t *rb = revision_baton;
/* If no node has yet flushed the revision, do it now. */
if (! rb->writing_begun)
return output_revision(rb);
else
return SVN_NO_ERROR;
}
/* Filtering vtable */
svn_repos_parse_fns3_t filtering_vtable =
{
magic_header_record,
uuid_record,
new_revision_record,
new_node_record,
set_revision_property,
set_node_property,
delete_node_property,
remove_node_props,
set_fulltext,
NULL,
close_node,
close_revision
};
/** Subcommands. **/
static svn_opt_subcommand_t
subcommand_help,
subcommand_exclude,
subcommand_include;
enum
{
svndumpfilter__drop_empty_revs = SVN_OPT_FIRST_LONGOPT_ID,
svndumpfilter__drop_all_empty_revs,
svndumpfilter__renumber_revs,
svndumpfilter__preserve_revprops,
svndumpfilter__skip_missing_merge_sources,
svndumpfilter__targets,
svndumpfilter__quiet,
svndumpfilter__glob,
svndumpfilter__version
};
/* Option codes and descriptions.
*
* The entire list must be terminated with an entry of nulls.
*/
static const apr_getopt_option_t options_table[] =
{
{"help", 'h', 0,
N_("show help on a subcommand")},
{NULL, '?', 0,
N_("show help on a subcommand")},
{"version", svndumpfilter__version, 0,
N_("show program version information") },
{"quiet", svndumpfilter__quiet, 0,
N_("Do not display filtering statistics.") },
{"pattern", svndumpfilter__glob, 0,
N_("Treat the path prefixes as file glob patterns.") },
{"drop-empty-revs", svndumpfilter__drop_empty_revs, 0,
N_("Remove revisions emptied by filtering.")},
{"drop-all-empty-revs", svndumpfilter__drop_all_empty_revs, 0,
N_("Remove all empty revisions found in dumpstream\n"
" except revision 0.")},
{"renumber-revs", svndumpfilter__renumber_revs, 0,
N_("Renumber revisions left after filtering.") },
{"skip-missing-merge-sources",
svndumpfilter__skip_missing_merge_sources, 0,
N_("Skip missing merge sources.") },
{"preserve-revprops", svndumpfilter__preserve_revprops, 0,
N_("Don't filter revision properties.") },
{"targets", svndumpfilter__targets, 1,
N_("Read additional prefixes, one per line, from\n"
" file ARG.")},
{NULL}
};
/* Array of available subcommands.
* The entire list must be terminated with an entry of nulls.
*/
static const svn_opt_subcommand_desc2_t cmd_table[] =
{
{"exclude", subcommand_exclude, {0},
N_("Filter out nodes with given prefixes from dumpstream.\n"
"usage: svndumpfilter exclude PATH_PREFIX...\n"),
{svndumpfilter__drop_empty_revs, svndumpfilter__drop_all_empty_revs,
svndumpfilter__renumber_revs,
svndumpfilter__skip_missing_merge_sources, svndumpfilter__targets,
svndumpfilter__preserve_revprops, svndumpfilter__quiet,
svndumpfilter__glob} },
{"include", subcommand_include, {0},
N_("Filter out nodes without given prefixes from dumpstream.\n"
"usage: svndumpfilter include PATH_PREFIX...\n"),
{svndumpfilter__drop_empty_revs, svndumpfilter__drop_all_empty_revs,
svndumpfilter__renumber_revs,
svndumpfilter__skip_missing_merge_sources, svndumpfilter__targets,
svndumpfilter__preserve_revprops, svndumpfilter__quiet,
svndumpfilter__glob} },
{"help", subcommand_help, {"?", "h"},
N_("Describe the usage of this program or its subcommands.\n"
"usage: svndumpfilter help [SUBCOMMAND...]\n"),
{0} },
{ NULL, NULL, {0}, NULL, {0} }
};
/* Baton for passing option/argument state to a subcommand function. */
struct svndumpfilter_opt_state
{
svn_opt_revision_t start_revision; /* -r X[:Y] is */
svn_opt_revision_t end_revision; /* not implemented. */
svn_boolean_t quiet; /* --quiet */
svn_boolean_t glob; /* --pattern */
svn_boolean_t version; /* --version */
svn_boolean_t drop_empty_revs; /* --drop-empty-revs */
svn_boolean_t drop_all_empty_revs; /* --drop-all-empty-revs */
svn_boolean_t help; /* --help or -? */
svn_boolean_t renumber_revs; /* --renumber-revs */
svn_boolean_t preserve_revprops; /* --preserve-revprops */
svn_boolean_t skip_missing_merge_sources;
/* --skip-missing-merge-sources */
const char *targets_file; /* --targets-file */
apr_array_header_t *prefixes; /* mainargs. */
};
static svn_error_t *
parse_baton_initialize(struct parse_baton_t **pb,
struct svndumpfilter_opt_state *opt_state,
svn_boolean_t do_exclude,
apr_pool_t *pool)
{
struct parse_baton_t *baton = apr_palloc(pool, sizeof(*baton));
/* Read the stream from STDIN. Users can redirect a file. */
SVN_ERR(create_stdio_stream(&(baton->in_stream),
apr_file_open_stdin, pool));
/* Have the parser dump results to STDOUT. Users can redirect a file. */
SVN_ERR(create_stdio_stream(&(baton->out_stream),
apr_file_open_stdout, pool));
baton->do_exclude = do_exclude;
/* Ignore --renumber-revs if there can't possibly be
anything to renumber. */
baton->do_renumber_revs =
(opt_state->renumber_revs && (opt_state->drop_empty_revs
|| opt_state->drop_all_empty_revs));
baton->drop_empty_revs = opt_state->drop_empty_revs;
baton->drop_all_empty_revs = opt_state->drop_all_empty_revs;
baton->preserve_revprops = opt_state->preserve_revprops;
baton->quiet = opt_state->quiet;
baton->glob = opt_state->glob;
baton->prefixes = opt_state->prefixes;
baton->skip_missing_merge_sources = opt_state->skip_missing_merge_sources;
baton->rev_drop_count = 0; /* used to shift revnums while filtering */
baton->dropped_nodes = apr_hash_make(pool);
baton->renumber_history = apr_hash_make(pool);
baton->last_live_revision = SVN_INVALID_REVNUM;
baton->oldest_original_rev = SVN_INVALID_REVNUM;
baton->allow_deltas = FALSE;
*pb = baton;
return SVN_NO_ERROR;
}
/* This implements `help` subcommand. */
static svn_error_t *
subcommand_help(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
struct svndumpfilter_opt_state *opt_state = baton;
const char *header =
_("general usage: svndumpfilter SUBCOMMAND [ARGS & OPTIONS ...]\n"
"Type 'svndumpfilter help <subcommand>' for help on a "
"specific subcommand.\n"
"Type 'svndumpfilter --version' to see the program version.\n"
"\n"
"Available subcommands:\n");
SVN_ERR(svn_opt_print_help4(os, "svndumpfilter",
opt_state ? opt_state->version : FALSE,
opt_state ? opt_state->quiet : FALSE,
/*###opt_state ? opt_state->verbose :*/ FALSE,
NULL, header, cmd_table, options_table,
NULL, NULL, pool));
return SVN_NO_ERROR;
}
/* Version compatibility check */
static svn_error_t *
check_lib_versions(void)
{
static const svn_version_checklist_t checklist[] =
{
{ "svn_subr", svn_subr_version },
{ "svn_repos", svn_repos_version },
{ "svn_delta", svn_delta_version },
{ NULL, NULL }
};
SVN_VERSION_DEFINE(my_version);
return svn_ver_check_list2(&my_version, checklist, svn_ver_equal);
}
/* Do the real work of filtering. */
static svn_error_t *
do_filter(apr_getopt_t *os,
void *baton,
svn_boolean_t do_exclude,
apr_pool_t *pool)
{
struct svndumpfilter_opt_state *opt_state = baton;
struct parse_baton_t *pb;
apr_hash_index_t *hi;
apr_array_header_t *keys;
int i, num_keys;
if (! opt_state->quiet)
{
apr_pool_t *subpool = svn_pool_create(pool);
if (opt_state->glob)
{
SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
do_exclude
? (opt_state->drop_empty_revs
|| opt_state->drop_all_empty_revs)
? _("Excluding (and dropping empty "
"revisions for) prefix patterns:\n")
: _("Excluding prefix patterns:\n")
: (opt_state->drop_empty_revs
|| opt_state->drop_all_empty_revs)
? _("Including (and dropping empty "
"revisions for) prefix patterns:\n")
: _("Including prefix patterns:\n")));
}
else
{
SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
do_exclude
? (opt_state->drop_empty_revs
|| opt_state->drop_all_empty_revs)
? _("Excluding (and dropping empty "
"revisions for) prefixes:\n")
: _("Excluding prefixes:\n")
: (opt_state->drop_empty_revs
|| opt_state->drop_all_empty_revs)
? _("Including (and dropping empty "
"revisions for) prefixes:\n")
: _("Including prefixes:\n")));
}
for (i = 0; i < opt_state->prefixes->nelts; i++)
{
svn_pool_clear(subpool);
SVN_ERR(svn_cmdline_fprintf
(stderr, subpool, " '%s'\n",
APR_ARRAY_IDX(opt_state->prefixes, i, const char *)));
}
SVN_ERR(svn_cmdline_fputs("\n", stderr, subpool));
svn_pool_destroy(subpool);
}
SVN_ERR(parse_baton_initialize(&pb, opt_state, do_exclude, pool));
SVN_ERR(svn_repos_parse_dumpstream3(pb->in_stream, &filtering_vtable, pb,
TRUE, NULL, NULL, pool));
/* The rest of this is just reporting. If we aren't reporting, get
outta here. */
if (opt_state->quiet)
return SVN_NO_ERROR;
SVN_ERR(svn_cmdline_fputs("\n", stderr, pool));
if (pb->rev_drop_count)
SVN_ERR(svn_cmdline_fprintf(stderr, pool,
Q_("Dropped %d revision.\n\n",
"Dropped %d revisions.\n\n",
pb->rev_drop_count),
pb->rev_drop_count));
if (pb->do_renumber_revs)
{
apr_pool_t *subpool = svn_pool_create(pool);
SVN_ERR(svn_cmdline_fputs(_("Revisions renumbered as follows:\n"),
stderr, subpool));
/* Get the keys of the hash, sort them, then print the hash keys
and values, sorted by keys. */
num_keys = apr_hash_count(pb->renumber_history);
keys = apr_array_make(pool, num_keys + 1, sizeof(svn_revnum_t));
for (hi = apr_hash_first(pool, pb->renumber_history);
hi;
hi = apr_hash_next(hi))
{
const svn_revnum_t *revnum = svn__apr_hash_index_key(hi);
APR_ARRAY_PUSH(keys, svn_revnum_t) = *revnum;
}
qsort(keys->elts, keys->nelts,
keys->elt_size, svn_sort_compare_revisions);
for (i = 0; i < keys->nelts; i++)
{
svn_revnum_t this_key;
struct revmap_t *this_val;
svn_pool_clear(subpool);
this_key = APR_ARRAY_IDX(keys, i, svn_revnum_t);
this_val = apr_hash_get(pb->renumber_history, &this_key,
sizeof(this_key));
if (this_val->was_dropped)
SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
_(" %ld => (dropped)\n"),
this_key));
else
SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
" %ld => %ld\n",
this_key, this_val->rev));
}
SVN_ERR(svn_cmdline_fputs("\n", stderr, subpool));
svn_pool_destroy(subpool);
}
if ((num_keys = apr_hash_count(pb->dropped_nodes)))
{
apr_pool_t *subpool = svn_pool_create(pool);
SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
Q_("Dropped %d node:\n",
"Dropped %d nodes:\n",
num_keys),
num_keys));
/* Get the keys of the hash, sort them, then print the hash keys
and values, sorted by keys. */
keys = apr_array_make(pool, num_keys + 1, sizeof(const char *));
for (hi = apr_hash_first(pool, pb->dropped_nodes);
hi;
hi = apr_hash_next(hi))
{
const char *path = svn__apr_hash_index_key(hi);
APR_ARRAY_PUSH(keys, const char *) = path;
}
qsort(keys->elts, keys->nelts, keys->elt_size, svn_sort_compare_paths);
for (i = 0; i < keys->nelts; i++)
{
svn_pool_clear(subpool);
SVN_ERR(svn_cmdline_fprintf
(stderr, subpool, " '%s'\n",
(const char *)APR_ARRAY_IDX(keys, i, const char *)));
}
SVN_ERR(svn_cmdline_fputs("\n", stderr, subpool));
svn_pool_destroy(subpool);
}
return SVN_NO_ERROR;
}
/* This implements `exclude' subcommand. */
static svn_error_t *
subcommand_exclude(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
return do_filter(os, baton, TRUE, pool);
}
/* This implements `include` subcommand. */
static svn_error_t *
subcommand_include(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
return do_filter(os, baton, FALSE, pool);
}
/** Main. **/
int
main(int argc, const char *argv[])
{
svn_error_t *err;
apr_status_t apr_err;
apr_pool_t *pool;
const svn_opt_subcommand_desc2_t *subcommand = NULL;
struct svndumpfilter_opt_state opt_state;
apr_getopt_t *os;
int opt_id;
apr_array_header_t *received_opts;
int i;
/* Initialize the app. */
if (svn_cmdline_init("svndumpfilter", stderr) != EXIT_SUCCESS)
return EXIT_FAILURE;
/* Create our top-level pool. Use a separate mutexless allocator,
* given this application is single threaded.
*/
pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
/* Check library versions */
err = check_lib_versions();
if (err)
return svn_cmdline_handle_exit_error(err, pool, "svndumpfilter: ");
received_opts = apr_array_make(pool, SVN_OPT_MAX_OPTIONS, sizeof(int));
/* Initialize the FS library. */
err = svn_fs_initialize(pool);
if (err)
return svn_cmdline_handle_exit_error(err, pool, "svndumpfilter: ");
if (argc <= 1)
{
SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
svn_pool_destroy(pool);
return EXIT_FAILURE;
}
/* Initialize opt_state. */
memset(&opt_state, 0, sizeof(opt_state));
opt_state.start_revision.kind = svn_opt_revision_unspecified;
opt_state.end_revision.kind = svn_opt_revision_unspecified;
/* Parse options. */
err = svn_cmdline__getopt_init(&os, argc, argv, pool);
if (err)
return svn_cmdline_handle_exit_error(err, pool, "svndumpfilter: ");
os->interleave = 1;
while (1)
{
const char *opt_arg;
/* Parse the next option. */
apr_err = apr_getopt_long(os, options_table, &opt_id, &opt_arg);
if (APR_STATUS_IS_EOF(apr_err))
break;
else if (apr_err)
{
SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
svn_pool_destroy(pool);
return EXIT_FAILURE;
}
/* Stash the option code in an array before parsing it. */
APR_ARRAY_PUSH(received_opts, int) = opt_id;
switch (opt_id)
{
case 'h':
case '?':
opt_state.help = TRUE;
break;
case svndumpfilter__version:
opt_state.version = TRUE;
break;
case svndumpfilter__quiet:
opt_state.quiet = TRUE;
break;
case svndumpfilter__glob:
opt_state.glob = TRUE;
break;
case svndumpfilter__drop_empty_revs:
opt_state.drop_empty_revs = TRUE;
break;
case svndumpfilter__drop_all_empty_revs:
opt_state.drop_all_empty_revs = TRUE;
break;
case svndumpfilter__renumber_revs:
opt_state.renumber_revs = TRUE;
break;
case svndumpfilter__preserve_revprops:
opt_state.preserve_revprops = TRUE;
break;
case svndumpfilter__skip_missing_merge_sources:
opt_state.skip_missing_merge_sources = TRUE;
break;
case svndumpfilter__targets:
opt_state.targets_file = opt_arg;
break;
default:
{
SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
svn_pool_destroy(pool);
return EXIT_FAILURE;
}
} /* close `switch' */
} /* close `while' */
/* Disallow simultaneous use of both --drop-empty-revs and
--drop-all-empty-revs. */
if (opt_state.drop_empty_revs && opt_state.drop_all_empty_revs)
{
err = svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
_("--drop-empty-revs cannot be used with "
"--drop-all-empty-revs"));
return svn_cmdline_handle_exit_error(err, pool, "svndumpfilter: ");
}
/* If the user asked for help, then the rest of the arguments are
the names of subcommands to get help on (if any), or else they're
just typos/mistakes. Whatever the case, the subcommand to
actually run is subcommand_help(). */
if (opt_state.help)
subcommand = svn_opt_get_canonical_subcommand2(cmd_table, "help");
/* If we're not running the `help' subcommand, then look for a
subcommand in the first argument. */
if (subcommand == NULL)
{
if (os->ind >= os->argc)
{
if (opt_state.version)
{
/* Use the "help" subcommand to handle the "--version" option. */
static const svn_opt_subcommand_desc2_t pseudo_cmd =
{ "--version", subcommand_help, {0}, "",
{svndumpfilter__version, /* must accept its own option */
svndumpfilter__quiet,
} };
subcommand = &pseudo_cmd;
}
else
{
svn_error_clear(svn_cmdline_fprintf
(stderr, pool,
_("Subcommand argument required\n")));
SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
svn_pool_destroy(pool);
return EXIT_FAILURE;
}
}
else
{
const char *first_arg = os->argv[os->ind++];
subcommand = svn_opt_get_canonical_subcommand2(cmd_table, first_arg);
if (subcommand == NULL)
{
const char* first_arg_utf8;
if ((err = svn_utf_cstring_to_utf8(&first_arg_utf8, first_arg,
pool)))
return svn_cmdline_handle_exit_error(err, pool,
"svndumpfilter: ");
svn_error_clear(
svn_cmdline_fprintf(stderr, pool,
_("Unknown subcommand: '%s'\n"),
first_arg_utf8));
SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
svn_pool_destroy(pool);
return EXIT_FAILURE;
}
}
}
/* If there's a second argument, it's probably [one of] prefixes.
Every subcommand except `help' requires at least one, so we parse
them out here and store in opt_state. */
if (subcommand->cmd_func != subcommand_help)
{
opt_state.prefixes = apr_array_make(pool, os->argc - os->ind,
sizeof(const char *));
for (i = os->ind ; i< os->argc; i++)
{
const char *prefix;
/* Ensure that each prefix is UTF8-encoded, in internal
style, and absolute. */
SVN_INT_ERR(svn_utf_cstring_to_utf8(&prefix, os->argv[i], pool));
prefix = svn_relpath__internal_style(prefix, pool);
if (prefix[0] != '/')
prefix = apr_pstrcat(pool, "/", prefix, (char *)NULL);
APR_ARRAY_PUSH(opt_state.prefixes, const char *) = prefix;
}
if (opt_state.targets_file)
{
svn_stringbuf_t *buffer, *buffer_utf8;
const char *utf8_targets_file;
apr_array_header_t *targets = apr_array_make(pool, 0,
sizeof(const char *));
/* We need to convert to UTF-8 now, even before we divide
the targets into an array, because otherwise we wouldn't
know what delimiter to use for svn_cstring_split(). */
SVN_INT_ERR(svn_utf_cstring_to_utf8(&utf8_targets_file,
opt_state.targets_file, pool));
SVN_INT_ERR(svn_stringbuf_from_file2(&buffer, utf8_targets_file,
pool));
SVN_INT_ERR(svn_utf_stringbuf_to_utf8(&buffer_utf8, buffer, pool));
targets = apr_array_append(pool,
svn_cstring_split(buffer_utf8->data, "\n\r",
TRUE, pool),
targets);
for (i = 0; i < targets->nelts; i++)
{
const char *prefix = APR_ARRAY_IDX(targets, i, const char *);
if (prefix[0] != '/')
prefix = apr_pstrcat(pool, "/", prefix, (char *)NULL);
APR_ARRAY_PUSH(opt_state.prefixes, const char *) = prefix;
}
}
if (apr_is_empty_array(opt_state.prefixes))
{
svn_error_clear(svn_cmdline_fprintf
(stderr, pool,
_("\nError: no prefixes supplied.\n")));
svn_pool_destroy(pool);
return EXIT_FAILURE;
}
}
/* Check that the subcommand wasn't passed any inappropriate options. */
for (i = 0; i < received_opts->nelts; i++)
{
opt_id = APR_ARRAY_IDX(received_opts, i, int);
/* All commands implicitly accept --help, so just skip over this
when we see it. Note that we don't want to include this option
in their "accepted options" list because it would be awfully
redundant to display it in every commands' help text. */
if (opt_id == 'h' || opt_id == '?')
continue;
if (! svn_opt_subcommand_takes_option3(subcommand, opt_id, NULL))
{
const char *optstr;
const apr_getopt_option_t *badopt =
svn_opt_get_option_from_code2(opt_id, options_table, subcommand,
pool);
svn_opt_format_option(&optstr, badopt, FALSE, pool);
if (subcommand->name[0] == '-')
SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
else
svn_error_clear(svn_cmdline_fprintf
(stderr, pool,
_("Subcommand '%s' doesn't accept option '%s'\n"
"Type 'svndumpfilter help %s' for usage.\n"),
subcommand->name, optstr, subcommand->name));
svn_pool_destroy(pool);
return EXIT_FAILURE;
}
}
/* Run the subcommand. */
err = (*subcommand->cmd_func)(os, &opt_state, pool);
if (err)
{
/* For argument-related problems, suggest using the 'help'
subcommand. */
if (err->apr_err == SVN_ERR_CL_INSUFFICIENT_ARGS
|| err->apr_err == SVN_ERR_CL_ARG_PARSING_ERROR)
{
err = svn_error_quick_wrap(err,
_("Try 'svndumpfilter help' for more "
"info"));
}
return svn_cmdline_handle_exit_error(err, pool, "svndumpfilter: ");
}
else
{
svn_pool_destroy(pool);
/* Flush stdout, making sure the user will see any print errors. */
SVN_INT_ERR(svn_cmdline_fflush(stdout));
return EXIT_SUCCESS;
}
}
Index: vendor/subversion/dist/subversion/svnrdump/load_editor.c
===================================================================
--- vendor/subversion/dist/subversion/svnrdump/load_editor.c (revision 286500)
+++ vendor/subversion/dist/subversion/svnrdump/load_editor.c (revision 286501)
@@ -1,1211 +1,1302 @@
/*
* load_editor.c: The svn_delta_editor_t editor used by svnrdump to
* load revisions.
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include "svn_cmdline.h"
#include "svn_pools.h"
#include "svn_delta.h"
#include "svn_repos.h"
#include "svn_props.h"
#include "svn_path.h"
#include "svn_ra.h"
#include "svn_subst.h"
#include "svn_io.h"
#include "svn_private_config.h"
#include "private/svn_repos_private.h"
#include "private/svn_ra_private.h"
#include "private/svn_mergeinfo_private.h"
#include "private/svn_fspath.h"
#include "svnrdump.h"
#define SVNRDUMP_PROP_LOCK SVN_PROP_PREFIX "rdump-lock"
+#define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r))
+
#if 0
#define LDR_DBG(x) SVN_DBG(x)
#else
#define LDR_DBG(x) while(0)
#endif
/**
* General baton used by the parser functions.
*/
struct parse_baton
{
/* Commit editor and baton used to transfer loaded revisions to
the target repository. */
const svn_delta_editor_t *commit_editor;
void *commit_edit_baton;
/* RA session(s) for committing to the target repository. */
svn_ra_session_t *session;
svn_ra_session_t *aux_session;
/* To bleep, or not to bleep? (What kind of question is that?) */
svn_boolean_t quiet;
/* UUID found in the dumpstream, if any; NULL otherwise. */
const char *uuid;
/* Root URL of the target repository. */
const char *root_url;
/* The "parent directory" of the target repository in which to load.
(This is essentially the difference between ROOT_URL and
SESSION's url, and roughly equivalent to the 'svnadmin load
--parent-dir' option.) */
const char *parent_dir;
/* A mapping of svn_revnum_t * dump stream revisions to their
corresponding svn_revnum_t * target repository revisions. */
/* ### See http://subversion.tigris.org/issues/show_bug.cgi?id=3903
### for discussion about improving the memory costs of this mapping. */
apr_hash_t *rev_map;
/* The most recent (youngest) revision from the dump stream mapped in
REV_MAP, or SVN_INVALID_REVNUM if no revisions have been mapped. */
svn_revnum_t last_rev_mapped;
/* The oldest revision loaded from the dump stream, or
SVN_INVALID_REVNUM if none have been loaded. */
svn_revnum_t oldest_dumpstream_rev;
};
/**
* Use to wrap the dir_context_t in commit.c so we can keep track of
* depth, relpath and parent for open_directory and close_directory.
*/
struct directory_baton
{
void *baton;
const char *relpath;
int depth;
+
+ /* The copy-from source of this directory, no matter whether it is
+ copied explicitly (the root node of a copy) or implicitly (being an
+ existing child of a copied directory). For a node that is newly
+ added (without history), even inside a copied parent, these are
+ NULL and SVN_INVALID_REVNUM. */
+ const char *copyfrom_path;
+ svn_revnum_t copyfrom_rev;
+
struct directory_baton *parent;
};
/**
* Baton used to represent a node; to be used by the parser
* functions. Contains a link to the revision baton.
*/
struct node_baton
{
const char *path;
svn_node_kind_t kind;
enum svn_node_action action;
+ /* Is this directory explicitly added? If not, then it already existed
+ or is a child of a copy. */
+ svn_boolean_t is_added;
+
svn_revnum_t copyfrom_rev;
const char *copyfrom_path;
+ const char *copyfrom_url;
void *file_baton;
const char *base_checksum;
+ /* (const char *name) -> (svn_prop_t *) */
+ apr_hash_t *prop_changes;
+
struct revision_baton *rb;
};
/**
* Baton used to represet a revision; used by the parser
* functions. Contains a link to the parser baton.
*/
struct revision_baton
{
svn_revnum_t rev;
apr_hash_t *revprop_table;
apr_int32_t rev_offset;
const svn_string_t *datestamp;
const svn_string_t *author;
struct parse_baton *pb;
struct directory_baton *db;
apr_pool_t *pool;
};
/* Record the mapping of FROM_REV to TO_REV in REV_MAP, ensuring that
anything added to the hash is allocated in the hash's pool. */
static void
set_revision_mapping(apr_hash_t *rev_map,
svn_revnum_t from_rev,
svn_revnum_t to_rev)
{
svn_revnum_t *mapped_revs = apr_palloc(apr_hash_pool_get(rev_map),
sizeof(svn_revnum_t) * 2);
mapped_revs[0] = from_rev;
mapped_revs[1] = to_rev;
apr_hash_set(rev_map, mapped_revs,
sizeof(svn_revnum_t), mapped_revs + 1);
}
/* Return the revision to which FROM_REV maps in REV_MAP, or
SVN_INVALID_REVNUM if no such mapping exists. */
static svn_revnum_t
get_revision_mapping(apr_hash_t *rev_map,
svn_revnum_t from_rev)
{
svn_revnum_t *to_rev = apr_hash_get(rev_map, &from_rev,
sizeof(from_rev));
return to_rev ? *to_rev : SVN_INVALID_REVNUM;
}
/* Prepend the mergeinfo source paths in MERGEINFO_ORIG with
PARENT_DIR, and return it in *MERGEINFO_VAL. */
/* ### FIXME: Consider somehow sharing code with
### libsvn_repos/load-fs-vtable.c:prefix_mergeinfo_paths() */
static svn_error_t *
prefix_mergeinfo_paths(svn_string_t **mergeinfo_val,
const svn_string_t *mergeinfo_orig,
const char *parent_dir,
apr_pool_t *pool)
{
apr_hash_t *prefixed_mergeinfo, *mergeinfo;
apr_hash_index_t *hi;
void *rangelist;
SVN_ERR(svn_mergeinfo_parse(&mergeinfo, mergeinfo_orig->data, pool));
prefixed_mergeinfo = apr_hash_make(pool);
for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
{
const void *key;
const char *path, *merge_source;
apr_hash_this(hi, &key, NULL, &rangelist);
merge_source = svn_relpath_canonicalize(key, pool);
/* The svn:mergeinfo property syntax demands a repos abspath */
path = svn_fspath__canonicalize(svn_relpath_join(parent_dir,
merge_source, pool),
pool);
svn_hash_sets(prefixed_mergeinfo, path, rangelist);
}
return svn_mergeinfo_to_string(mergeinfo_val, prefixed_mergeinfo, pool);
}
/* Examine the mergeinfo in INITIAL_VAL, renumber revisions in rangelists
as appropriate, and return the (possibly new) mergeinfo in *FINAL_VAL
(allocated from POOL). */
/* ### FIXME: Consider somehow sharing code with
### libsvn_repos/load-fs-vtable.c:renumber_mergeinfo_revs() */
static svn_error_t *
renumber_mergeinfo_revs(svn_string_t **final_val,
const svn_string_t *initial_val,
struct revision_baton *rb,
apr_pool_t *pool)
{
apr_pool_t *subpool = svn_pool_create(pool);
svn_mergeinfo_t mergeinfo, predates_stream_mergeinfo;
svn_mergeinfo_t final_mergeinfo = apr_hash_make(subpool);
apr_hash_index_t *hi;
SVN_ERR(svn_mergeinfo_parse(&mergeinfo, initial_val->data, subpool));
/* Issue #3020
http://subversion.tigris.org/issues/show_bug.cgi?id=3020#desc16
Remove mergeinfo older than the oldest revision in the dump stream
and adjust its revisions by the difference between the head rev of
the target repository and the current dump stream rev. */
if (rb->pb->oldest_dumpstream_rev > 1)
{
SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(
&predates_stream_mergeinfo, mergeinfo,
rb->pb->oldest_dumpstream_rev - 1, 0,
TRUE, subpool, subpool));
SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(
&mergeinfo, mergeinfo,
rb->pb->oldest_dumpstream_rev - 1, 0,
FALSE, subpool, subpool));
SVN_ERR(svn_mergeinfo__adjust_mergeinfo_rangelists(
&predates_stream_mergeinfo,
predates_stream_mergeinfo,
-rb->rev_offset, subpool, subpool));
}
else
{
predates_stream_mergeinfo = NULL;
}
for (hi = apr_hash_first(subpool, mergeinfo); hi; hi = apr_hash_next(hi))
{
svn_rangelist_t *rangelist;
struct parse_baton *pb = rb->pb;
int i;
const void *path;
apr_ssize_t pathlen;
void *val;
apr_hash_this(hi, &path, &pathlen, &val);
rangelist = val;
/* Possibly renumber revisions in merge source's rangelist. */
for (i = 0; i < rangelist->nelts; i++)
{
svn_revnum_t rev_from_map;
svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i,
svn_merge_range_t *);
rev_from_map = get_revision_mapping(pb->rev_map, range->start);
if (SVN_IS_VALID_REVNUM(rev_from_map))
{
range->start = rev_from_map;
}
else if (range->start == pb->oldest_dumpstream_rev - 1)
{
/* Since the start revision of svn_merge_range_t are not
inclusive there is one possible valid start revision that
won't be found in the PB->REV_MAP mapping of load stream
revsions to loaded revisions: The revision immediately
preceeding the oldest revision from the load stream.
This is a valid revision for mergeinfo, but not a valid
copy from revision (which PB->REV_MAP also maps for) so it
will never be in the mapping.
If that is what we have here, then find the mapping for the
oldest rev from the load stream and subtract 1 to get the
renumbered, non-inclusive, start revision. */
rev_from_map = get_revision_mapping(pb->rev_map,
pb->oldest_dumpstream_rev);
if (SVN_IS_VALID_REVNUM(rev_from_map))
range->start = rev_from_map - 1;
}
else
{
/* If we can't remap the start revision then don't even bother
trying to remap the end revision. It's possible we might
actually succeed at the latter, which can result in invalid
mergeinfo with a start rev > end rev. If that gets into the
repository then a world of bustage breaks loose anytime that
bogus mergeinfo is parsed. See
http://subversion.tigris.org/issues/show_bug.cgi?id=3020#desc16.
*/
continue;
}
rev_from_map = get_revision_mapping(pb->rev_map, range->end);
if (SVN_IS_VALID_REVNUM(rev_from_map))
range->end = rev_from_map;
}
apr_hash_set(final_mergeinfo, path, pathlen, rangelist);
}
if (predates_stream_mergeinfo)
{
SVN_ERR(svn_mergeinfo_merge2(final_mergeinfo, predates_stream_mergeinfo,
subpool, subpool));
}
- SVN_ERR(svn_mergeinfo_sort(final_mergeinfo, subpool));
+ SVN_ERR(svn_mergeinfo__canonicalize_ranges(final_mergeinfo, subpool));
- /* Mergeinfo revision sources for r0 and r1 are invalid; you can't merge r0
- or r1. However, svndumpfilter can be abused to produce r1 merge source
- revs. So if we encounter any, then strip them out, no need to put them
- into the load target. */
- SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(&final_mergeinfo,
- final_mergeinfo,
- 1, 0, FALSE,
- subpool, subpool));
-
SVN_ERR(svn_mergeinfo_to_string(final_val, final_mergeinfo, pool));
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
static svn_error_t *
commit_callback(const svn_commit_info_t *commit_info,
void *baton,
apr_pool_t *pool)
{
struct revision_baton *rb = baton;
struct parse_baton *pb = rb->pb;
/* ### Don't print directly; generate a notification. */
if (! pb->quiet)
SVN_ERR(svn_cmdline_printf(pool, "* Loaded revision %ld.\n",
commit_info->revision));
/* Add the mapping of the dumpstream revision to the committed revision. */
set_revision_mapping(pb->rev_map, rb->rev, commit_info->revision);
/* If the incoming dump stream has non-contiguous revisions (e.g. from
using svndumpfilter --drop-empty-revs without --renumber-revs) then
we must account for the missing gaps in PB->REV_MAP. Otherwise we
might not be able to map all mergeinfo source revisions to the correct
revisions in the target repos. */
if ((pb->last_rev_mapped != SVN_INVALID_REVNUM)
&& (rb->rev != pb->last_rev_mapped + 1))
{
svn_revnum_t i;
for (i = pb->last_rev_mapped + 1; i < rb->rev; i++)
{
set_revision_mapping(pb->rev_map, i, pb->last_rev_mapped);
}
}
/* Update our "last revision mapped". */
pb->last_rev_mapped = rb->rev;
return SVN_NO_ERROR;
}
/* Implements `svn_ra__lock_retry_func_t'. */
static svn_error_t *
lock_retry_func(void *baton,
const svn_string_t *reposlocktoken,
apr_pool_t *pool)
{
return svn_cmdline_printf(pool,
_("Failed to get lock on destination "
"repos, currently held by '%s'\n"),
reposlocktoken->data);
}
static svn_error_t *
fetch_base_func(const char **filename,
void *baton,
const char *path,
svn_revnum_t base_revision,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
struct revision_baton *rb = baton;
svn_stream_t *fstream;
svn_error_t *err;
if (! SVN_IS_VALID_REVNUM(base_revision))
base_revision = rb->rev - 1;
SVN_ERR(svn_stream_open_unique(&fstream, filename, NULL,
svn_io_file_del_on_pool_cleanup,
result_pool, scratch_pool));
err = svn_ra_get_file(rb->pb->aux_session, path, base_revision,
fstream, NULL, NULL, scratch_pool);
if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
{
svn_error_clear(err);
SVN_ERR(svn_stream_close(fstream));
*filename = NULL;
return SVN_NO_ERROR;
}
else if (err)
return svn_error_trace(err);
SVN_ERR(svn_stream_close(fstream));
return SVN_NO_ERROR;
}
static svn_error_t *
fetch_props_func(apr_hash_t **props,
void *baton,
const char *path,
svn_revnum_t base_revision,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
struct revision_baton *rb = baton;
svn_node_kind_t node_kind;
if (! SVN_IS_VALID_REVNUM(base_revision))
base_revision = rb->rev - 1;
SVN_ERR(svn_ra_check_path(rb->pb->aux_session, path, base_revision,
&node_kind, scratch_pool));
if (node_kind == svn_node_file)
{
SVN_ERR(svn_ra_get_file(rb->pb->aux_session, path, base_revision,
NULL, NULL, props, result_pool));
}
else if (node_kind == svn_node_dir)
{
apr_array_header_t *tmp_props;
SVN_ERR(svn_ra_get_dir2(rb->pb->aux_session, NULL, NULL, props, path,
base_revision, 0 /* Dirent fields */,
result_pool));
tmp_props = svn_prop_hash_to_array(*props, result_pool);
SVN_ERR(svn_categorize_props(tmp_props, NULL, NULL, &tmp_props,
result_pool));
*props = svn_prop_array_to_hash(tmp_props, result_pool);
}
else
{
*props = apr_hash_make(result_pool);
}
return SVN_NO_ERROR;
}
static svn_error_t *
fetch_kind_func(svn_node_kind_t *kind,
void *baton,
const char *path,
svn_revnum_t base_revision,
apr_pool_t *scratch_pool)
{
struct revision_baton *rb = baton;
if (! SVN_IS_VALID_REVNUM(base_revision))
base_revision = rb->rev - 1;
SVN_ERR(svn_ra_check_path(rb->pb->aux_session, path, base_revision,
kind, scratch_pool));
return SVN_NO_ERROR;
}
static svn_delta_shim_callbacks_t *
get_shim_callbacks(struct revision_baton *rb,
apr_pool_t *pool)
{
svn_delta_shim_callbacks_t *callbacks =
svn_delta_shim_callbacks_default(pool);
callbacks->fetch_props_func = fetch_props_func;
callbacks->fetch_kind_func = fetch_kind_func;
callbacks->fetch_base_func = fetch_base_func;
callbacks->fetch_baton = rb;
return callbacks;
}
/* Acquire a lock (of sorts) on the repository associated with the
* given RA SESSION. This lock is just a revprop change attempt in a
* time-delay loop. This function is duplicated by svnsync in
* svnsync/svnsync.c
*
* ### TODO: Make this function more generic and
* expose it through a header for use by other Subversion
* applications to avoid duplication.
*/
static svn_error_t *
get_lock(const svn_string_t **lock_string_p,
svn_ra_session_t *session,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
svn_boolean_t be_atomic;
SVN_ERR(svn_ra_has_capability(session, &be_atomic,
SVN_RA_CAPABILITY_ATOMIC_REVPROPS,
pool));
if (! be_atomic)
{
/* Pre-1.7 servers can't lock without a race condition. (Issue #3546) */
svn_error_t *err =
svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Target server does not support atomic revision "
"property edits; consider upgrading it to 1.7."));
svn_handle_warning2(stderr, err, "svnrdump: ");
svn_error_clear(err);
}
return svn_ra__get_operational_lock(lock_string_p, NULL, session,
SVNRDUMP_PROP_LOCK, FALSE,
10 /* retries */, lock_retry_func, NULL,
cancel_func, cancel_baton, pool);
}
static svn_error_t *
new_revision_record(void **revision_baton,
apr_hash_t *headers,
void *parse_baton,
apr_pool_t *pool)
{
struct revision_baton *rb;
struct parse_baton *pb;
apr_hash_index_t *hi;
svn_revnum_t head_rev;
rb = apr_pcalloc(pool, sizeof(*rb));
pb = parse_baton;
rb->pool = svn_pool_create(pool);
rb->pb = pb;
+ rb->db = NULL;
for (hi = apr_hash_first(pool, headers); hi; hi = apr_hash_next(hi))
{
const char *hname = svn__apr_hash_index_key(hi);
const char *hval = svn__apr_hash_index_val(hi);
if (strcmp(hname, SVN_REPOS_DUMPFILE_REVISION_NUMBER) == 0)
rb->rev = atoi(hval);
}
SVN_ERR(svn_ra_get_latest_revnum(pb->session, &head_rev, pool));
/* FIXME: This is a lame fallback loading multiple segments of dump in
several separate operations. It is highly susceptible to race conditions.
Calculate the revision 'offset' for finding copyfrom sources.
It might be positive or negative. */
rb->rev_offset = (apr_int32_t) ((rb->rev) - (head_rev + 1));
/* Stash the oldest (non-zero) dumpstream revision seen. */
if ((rb->rev > 0) && (!SVN_IS_VALID_REVNUM(pb->oldest_dumpstream_rev)))
pb->oldest_dumpstream_rev = rb->rev;
/* Set the commit_editor/ commit_edit_baton to NULL and wait for
them to be created in new_node_record */
rb->pb->commit_editor = NULL;
rb->pb->commit_edit_baton = NULL;
rb->revprop_table = apr_hash_make(rb->pool);
*revision_baton = rb;
return SVN_NO_ERROR;
}
static svn_error_t *
magic_header_record(int version,
void *parse_baton,
apr_pool_t *pool)
{
return SVN_NO_ERROR;
}
static svn_error_t *
uuid_record(const char *uuid,
void *parse_baton,
apr_pool_t *pool)
{
struct parse_baton *pb;
pb = parse_baton;
pb->uuid = apr_pstrdup(pool, uuid);
return SVN_NO_ERROR;
}
+/* Push information about another directory onto the linked list RB->db.
+ *
+ * CHILD_BATON is the baton returned by the commit editor. RELPATH is the
+ * repository-relative path of this directory. IS_ADDED is true iff this
+ * directory is being added (with or without history). If added with
+ * history then COPYFROM_PATH/COPYFROM_REV are the copyfrom source, else
+ * are NULL/SVN_INVALID_REVNUM.
+ */
+static void
+push_directory(struct revision_baton *rb,
+ void *child_baton,
+ const char *relpath,
+ svn_boolean_t is_added,
+ const char *copyfrom_path,
+ svn_revnum_t copyfrom_rev)
+{
+ struct directory_baton *child_db = apr_pcalloc(rb->pool, sizeof (*child_db));
+
+ SVN_ERR_ASSERT_NO_RETURN(
+ is_added || (copyfrom_path == NULL && copyfrom_rev == SVN_INVALID_REVNUM));
+
+ /* If this node is an existing (not newly added) child of a copied node,
+ calculate where it was copied from. */
+ if (!is_added
+ && ARE_VALID_COPY_ARGS(rb->db->copyfrom_path, rb->db->copyfrom_rev))
+ {
+ const char *name = svn_relpath_basename(relpath, NULL);
+
+ copyfrom_path = svn_relpath_join(rb->db->copyfrom_path, name,
+ rb->pool);
+ copyfrom_rev = rb->db->copyfrom_rev;
+ }
+
+ child_db->baton = child_baton;
+ child_db->relpath = relpath;
+ child_db->copyfrom_path = copyfrom_path;
+ child_db->copyfrom_rev = copyfrom_rev;
+ child_db->parent = rb->db;
+ rb->db = child_db;
+}
+
static svn_error_t *
new_node_record(void **node_baton,
apr_hash_t *headers,
void *revision_baton,
apr_pool_t *pool)
{
struct revision_baton *rb = revision_baton;
const struct svn_delta_editor_t *commit_editor = rb->pb->commit_editor;
void *commit_edit_baton = rb->pb->commit_edit_baton;
struct node_baton *nb;
- struct directory_baton *child_db;
apr_hash_index_t *hi;
void *child_baton;
- char *relpath_compose;
const char *nb_dirname;
nb = apr_pcalloc(rb->pool, sizeof(*nb));
nb->rb = rb;
-
+ nb->is_added = FALSE;
nb->copyfrom_path = NULL;
+ nb->copyfrom_url = NULL;
nb->copyfrom_rev = SVN_INVALID_REVNUM;
+ nb->prop_changes = apr_hash_make(rb->pool);
/* If the creation of commit_editor is pending, create it now and
open_root on it; also create a top-level directory baton. */
if (!commit_editor)
{
/* The revprop_table should have been filled in with important
information like svn:log in set_revision_property. We can now
use it all this information to create our commit_editor. But
first, clear revprops that we aren't allowed to set with the
commit_editor. We'll set them separately using the RA API
after closing the editor (see close_revision). */
svn_hash_sets(rb->revprop_table, SVN_PROP_REVISION_AUTHOR, NULL);
svn_hash_sets(rb->revprop_table, SVN_PROP_REVISION_DATE, NULL);
SVN_ERR(svn_ra__register_editor_shim_callbacks(rb->pb->session,
get_shim_callbacks(rb, rb->pool)));
SVN_ERR(svn_ra_get_commit_editor3(rb->pb->session, &commit_editor,
&commit_edit_baton, rb->revprop_table,
commit_callback, revision_baton,
NULL, FALSE, rb->pool));
rb->pb->commit_editor = commit_editor;
rb->pb->commit_edit_baton = commit_edit_baton;
SVN_ERR(commit_editor->open_root(commit_edit_baton,
rb->rev - rb->rev_offset - 1,
rb->pool, &child_baton));
LDR_DBG(("Opened root %p\n", child_baton));
- /* child_db corresponds to the root directory baton here */
- child_db = apr_pcalloc(rb->pool, sizeof(*child_db));
- child_db->baton = child_baton;
- child_db->depth = 0;
- child_db->relpath = "";
- child_db->parent = NULL;
- rb->db = child_db;
+ /* child_baton corresponds to the root directory baton here */
+ push_directory(rb, child_baton, "", TRUE /*is_added*/,
+ NULL, SVN_INVALID_REVNUM);
}
for (hi = apr_hash_first(rb->pool, headers); hi; hi = apr_hash_next(hi))
{
const char *hname = svn__apr_hash_index_key(hi);
const char *hval = svn__apr_hash_index_val(hi);
/* Parse the different kinds of headers we can encounter and
stuff them into the node_baton for writing later */
if (strcmp(hname, SVN_REPOS_DUMPFILE_NODE_PATH) == 0)
nb->path = apr_pstrdup(rb->pool, hval);
if (strcmp(hname, SVN_REPOS_DUMPFILE_NODE_KIND) == 0)
nb->kind = strcmp(hval, "file") == 0 ? svn_node_file : svn_node_dir;
if (strcmp(hname, SVN_REPOS_DUMPFILE_NODE_ACTION) == 0)
{
if (strcmp(hval, "add") == 0)
nb->action = svn_node_action_add;
if (strcmp(hval, "change") == 0)
nb->action = svn_node_action_change;
if (strcmp(hval, "delete") == 0)
nb->action = svn_node_action_delete;
if (strcmp(hval, "replace") == 0)
nb->action = svn_node_action_replace;
}
if (strcmp(hname, SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5) == 0)
nb->base_checksum = apr_pstrdup(rb->pool, hval);
if (strcmp(hname, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV) == 0)
nb->copyfrom_rev = atoi(hval);
if (strcmp(hname, SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH) == 0)
nb->copyfrom_path = apr_pstrdup(rb->pool, hval);
}
nb_dirname = svn_relpath_dirname(nb->path, pool);
if (svn_path_compare_paths(nb_dirname,
rb->db->relpath) != 0)
{
char *ancestor_path;
apr_size_t residual_close_count;
apr_array_header_t *residual_open_path;
int i;
apr_size_t n;
/* Before attempting to handle the action, call open_directory
for all the path components and set the directory baton
accordingly */
ancestor_path =
svn_relpath_get_longest_ancestor(nb_dirname,
rb->db->relpath, pool);
residual_close_count =
svn_path_component_count(svn_relpath_skip_ancestor(ancestor_path,
rb->db->relpath));
residual_open_path =
svn_path_decompose(svn_relpath_skip_ancestor(ancestor_path,
nb_dirname), pool);
/* First close all as many directories as there are after
skip_ancestor, and then open fresh directories */
for (n = 0; n < residual_close_count; n ++)
{
/* Don't worry about destroying the actual rb->db object,
since the pool we're using has the lifetime of one
revision anyway */
LDR_DBG(("Closing dir %p\n", rb->db->baton));
SVN_ERR(commit_editor->close_directory(rb->db->baton, rb->pool));
rb->db = rb->db->parent;
}
for (i = 0; i < residual_open_path->nelts; i ++)
{
- relpath_compose =
+ char *relpath_compose =
svn_relpath_join(rb->db->relpath,
APR_ARRAY_IDX(residual_open_path, i, const char *),
rb->pool);
SVN_ERR(commit_editor->open_directory(relpath_compose,
rb->db->baton,
rb->rev - rb->rev_offset - 1,
rb->pool, &child_baton));
LDR_DBG(("Opened dir %p\n", child_baton));
- child_db = apr_pcalloc(rb->pool, sizeof(*child_db));
- child_db->baton = child_baton;
- child_db->depth = rb->db->depth + 1;
- child_db->relpath = relpath_compose;
- child_db->parent = rb->db;
- rb->db = child_db;
+ push_directory(rb, child_baton, relpath_compose, TRUE /*is_added*/,
+ NULL, SVN_INVALID_REVNUM);
}
}
/* Fix up the copyfrom information in light of mapped revisions and
non-root load targets, and convert copyfrom path into a full
URL. */
if (nb->copyfrom_path && SVN_IS_VALID_REVNUM(nb->copyfrom_rev))
{
svn_revnum_t copyfrom_rev;
/* Try to find the copyfrom revision in the revision map;
failing that, fall back to the revision offset approach. */
copyfrom_rev = get_revision_mapping(rb->pb->rev_map, nb->copyfrom_rev);
if (! SVN_IS_VALID_REVNUM(copyfrom_rev))
copyfrom_rev = nb->copyfrom_rev - rb->rev_offset;
if (! SVN_IS_VALID_REVNUM(copyfrom_rev))
return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
_("Relative source revision %ld is not"
" available in current repository"),
copyfrom_rev);
nb->copyfrom_rev = copyfrom_rev;
if (rb->pb->parent_dir)
nb->copyfrom_path = svn_relpath_join(rb->pb->parent_dir,
nb->copyfrom_path, rb->pool);
- nb->copyfrom_path = svn_path_url_add_component2(rb->pb->root_url,
+ nb->copyfrom_url = svn_path_url_add_component2(rb->pb->root_url,
nb->copyfrom_path,
rb->pool);
}
switch (nb->action)
{
case svn_node_action_delete:
case svn_node_action_replace:
LDR_DBG(("Deleting entry %s in %p\n", nb->path, rb->db->baton));
- SVN_ERR(commit_editor->delete_entry(nb->path, rb->rev - rb->rev_offset,
+ SVN_ERR(commit_editor->delete_entry(nb->path,
+ rb->rev - rb->rev_offset - 1,
rb->db->baton, rb->pool));
if (nb->action == svn_node_action_delete)
break;
else
/* FALL THROUGH */;
case svn_node_action_add:
+ nb->is_added = TRUE;
switch (nb->kind)
{
case svn_node_file:
SVN_ERR(commit_editor->add_file(nb->path, rb->db->baton,
- nb->copyfrom_path,
+ nb->copyfrom_url,
nb->copyfrom_rev,
rb->pool, &(nb->file_baton)));
LDR_DBG(("Added file %s to dir %p as %p\n",
nb->path, rb->db->baton, nb->file_baton));
break;
case svn_node_dir:
SVN_ERR(commit_editor->add_directory(nb->path, rb->db->baton,
- nb->copyfrom_path,
+ nb->copyfrom_url,
nb->copyfrom_rev,
rb->pool, &child_baton));
LDR_DBG(("Added dir %s to dir %p as %p\n",
nb->path, rb->db->baton, child_baton));
- child_db = apr_pcalloc(rb->pool, sizeof(*child_db));
- child_db->baton = child_baton;
- child_db->depth = rb->db->depth + 1;
- child_db->relpath = apr_pstrdup(rb->pool, nb->path);
- child_db->parent = rb->db;
- rb->db = child_db;
+ push_directory(rb, child_baton, nb->path, TRUE /*is_added*/,
+ nb->copyfrom_path, nb->copyfrom_rev);
break;
default:
break;
}
break;
case svn_node_action_change:
switch (nb->kind)
{
case svn_node_file:
SVN_ERR(commit_editor->open_file(nb->path, rb->db->baton,
SVN_INVALID_REVNUM, rb->pool,
&(nb->file_baton)));
break;
default:
SVN_ERR(commit_editor->open_directory(nb->path, rb->db->baton,
rb->rev - rb->rev_offset - 1,
rb->pool, &child_baton));
- child_db = apr_pcalloc(rb->pool, sizeof(*child_db));
- child_db->baton = child_baton;
- child_db->depth = rb->db->depth + 1;
- child_db->relpath = apr_pstrdup(rb->pool, nb->path);
- child_db->parent = rb->db;
- rb->db = child_db;
+ push_directory(rb, child_baton, nb->path, FALSE /*is_added*/,
+ NULL, SVN_INVALID_REVNUM);
break;
}
break;
}
*node_baton = nb;
return SVN_NO_ERROR;
}
static svn_error_t *
set_revision_property(void *baton,
const char *name,
const svn_string_t *value)
{
struct revision_baton *rb = baton;
SVN_ERR(svn_rdump__normalize_prop(name, &value, rb->pool));
SVN_ERR(svn_repos__validate_prop(name, value, rb->pool));
if (rb->rev > 0)
{
svn_hash_sets(rb->revprop_table,
apr_pstrdup(rb->pool, name),
svn_string_dup(value, rb->pool));
}
else if (rb->rev_offset == -1)
{
/* Special case: set revision 0 properties directly (which is
safe because the commit_editor hasn't been created yet), but
only when loading into an 'empty' filesystem. */
SVN_ERR(svn_ra_change_rev_prop2(rb->pb->session, 0,
name, NULL, value, rb->pool));
}
/* Remember any datestamp/ author that passes through (see comment
in close_revision). */
if (!strcmp(name, SVN_PROP_REVISION_DATE))
rb->datestamp = svn_string_dup(value, rb->pool);
if (!strcmp(name, SVN_PROP_REVISION_AUTHOR))
rb->author = svn_string_dup(value, rb->pool);
return SVN_NO_ERROR;
}
static svn_error_t *
set_node_property(void *baton,
const char *name,
const svn_string_t *value)
{
struct node_baton *nb = baton;
- const struct svn_delta_editor_t *commit_editor = nb->rb->pb->commit_editor;
apr_pool_t *pool = nb->rb->pool;
+ svn_prop_t *prop;
if (value && strcmp(name, SVN_PROP_MERGEINFO) == 0)
{
svn_string_t *renumbered_mergeinfo;
svn_string_t prop_val;
/* Tolerate mergeinfo with "\r\n" line endings because some
dumpstream sources might contain as much. If so normalize
the line endings to '\n' and make a notification to
PARSE_BATON->FEEDBACK_STREAM that we have made this
correction. */
if (strstr(value->data, "\r"))
{
const char *prop_eol_normalized;
SVN_ERR(svn_subst_translate_cstring2(value->data,
&prop_eol_normalized,
"\n", /* translate to LF */
FALSE, /* no repair */
NULL, /* no keywords */
FALSE, /* no expansion */
pool));
prop_val.data = prop_eol_normalized;
prop_val.len = strlen(prop_eol_normalized);
value = &prop_val;
/* ### TODO: notify? */
}
/* Renumber mergeinfo as appropriate. */
SVN_ERR(renumber_mergeinfo_revs(&renumbered_mergeinfo, value,
nb->rb, pool));
value = renumbered_mergeinfo;
if (nb->rb->pb->parent_dir)
{
/* Prefix the merge source paths with PB->parent_dir. */
/* ASSUMPTION: All source paths are included in the dump stream. */
svn_string_t *mergeinfo_val;
SVN_ERR(prefix_mergeinfo_paths(&mergeinfo_val, value,
nb->rb->pb->parent_dir, pool));
value = mergeinfo_val;
}
}
SVN_ERR(svn_rdump__normalize_prop(name, &value, pool));
SVN_ERR(svn_repos__validate_prop(name, value, pool));
- switch (nb->kind)
- {
- case svn_node_file:
- LDR_DBG(("Applying properties on %p\n", nb->file_baton));
- SVN_ERR(commit_editor->change_file_prop(nb->file_baton, name,
- value, pool));
- break;
- case svn_node_dir:
- LDR_DBG(("Applying properties on %p\n", nb->rb->db->baton));
- SVN_ERR(commit_editor->change_dir_prop(nb->rb->db->baton, name,
- value, pool));
- break;
- default:
- break;
- }
+ prop = apr_palloc(nb->rb->pool, sizeof (*prop));
+ prop->name = apr_pstrdup(pool, name);
+ prop->value = value ? svn_string_dup(value, pool) : NULL;
+ svn_hash_sets(nb->prop_changes, prop->name, prop);
+
return SVN_NO_ERROR;
}
static svn_error_t *
delete_node_property(void *baton,
const char *name)
{
struct node_baton *nb = baton;
- const struct svn_delta_editor_t *commit_editor = nb->rb->pb->commit_editor;
apr_pool_t *pool = nb->rb->pool;
+ svn_prop_t *prop;
SVN_ERR(svn_repos__validate_prop(name, NULL, pool));
- if (nb->kind == svn_node_file)
- SVN_ERR(commit_editor->change_file_prop(nb->file_baton, name,
- NULL, pool));
- else
- SVN_ERR(commit_editor->change_dir_prop(nb->rb->db->baton, name,
- NULL, pool));
+ prop = apr_palloc(pool, sizeof (*prop));
+ prop->name = apr_pstrdup(pool, name);
+ prop->value = NULL;
+ svn_hash_sets(nb->prop_changes, prop->name, prop);
return SVN_NO_ERROR;
}
+/* Delete all the properties of the node, if any.
+ *
+ * The commit editor doesn't have a method to delete a node's properties
+ * without knowing what they are, so we have to first find out what
+ * properties the node would have had. If it's copied (explicitly or
+ * implicitly), we look at the copy source. If it's only being changed,
+ * we look at the node's current path in the head revision.
+ */
static svn_error_t *
remove_node_props(void *baton)
{
struct node_baton *nb = baton;
+ struct revision_baton *rb = nb->rb;
apr_pool_t *pool = nb->rb->pool;
apr_hash_index_t *hi;
apr_hash_t *props;
+ const char *orig_path;
+ svn_revnum_t orig_rev;
+ /* Find the path and revision that has the node's original properties */
+ if (ARE_VALID_COPY_ARGS(nb->copyfrom_path, nb->copyfrom_rev))
+ {
+ LDR_DBG(("using nb->copyfrom %s@%ld", nb->copyfrom_path, nb->copyfrom_rev));
+ orig_path = nb->copyfrom_path;
+ orig_rev = nb->copyfrom_rev;
+ }
+ else if (!nb->is_added
+ && ARE_VALID_COPY_ARGS(rb->db->copyfrom_path, rb->db->copyfrom_rev))
+ {
+ /* If this is a dir, then it's described by rb->db;
+ if this is a file, then it's a child of the dir in rb->db. */
+ LDR_DBG(("using rb->db->copyfrom (k=%d) %s@%ld",
+ nb->kind, rb->db->copyfrom_path, rb->db->copyfrom_rev));
+ orig_path = (nb->kind == svn_node_dir)
+ ? rb->db->copyfrom_path
+ : svn_relpath_join(rb->db->copyfrom_path,
+ svn_relpath_basename(nb->path, NULL),
+ rb->pool);
+ orig_rev = rb->db->copyfrom_rev;
+ }
+ else
+ {
+ LDR_DBG(("using self.path@head %s@%ld", nb->path, SVN_INVALID_REVNUM));
+ /* ### Should we query at a known, fixed, "head" revision number
+ instead of passing SVN_INVALID_REVNUM and getting a moving target? */
+ orig_path = nb->path;
+ orig_rev = SVN_INVALID_REVNUM;
+ }
+ LDR_DBG(("Trying %s@%ld", orig_path, orig_rev));
+
if ((nb->action == svn_node_action_add
|| nb->action == svn_node_action_replace)
- && ! SVN_IS_VALID_REVNUM(nb->copyfrom_rev))
+ && ! ARE_VALID_COPY_ARGS(orig_path, orig_rev))
/* Add-without-history; no "old" properties to worry about. */
return SVN_NO_ERROR;
if (nb->kind == svn_node_file)
{
- SVN_ERR(svn_ra_get_file(nb->rb->pb->aux_session, nb->path,
- SVN_INVALID_REVNUM, NULL, NULL, &props, pool));
+ SVN_ERR(svn_ra_get_file(nb->rb->pb->aux_session,
+ orig_path, orig_rev, NULL, NULL, &props, pool));
}
else /* nb->kind == svn_node_dir */
{
SVN_ERR(svn_ra_get_dir2(nb->rb->pb->aux_session, NULL, NULL, &props,
- nb->path, SVN_INVALID_REVNUM, 0, pool));
+ orig_path, orig_rev, 0, pool));
}
for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi))
{
const char *name = svn__apr_hash_index_key(hi);
svn_prop_kind_t kind = svn_property_kind2(name);
if (kind == svn_prop_regular_kind)
SVN_ERR(set_node_property(nb, name, NULL));
}
return SVN_NO_ERROR;
}
static svn_error_t *
set_fulltext(svn_stream_t **stream,
void *node_baton)
{
struct node_baton *nb = node_baton;
const struct svn_delta_editor_t *commit_editor = nb->rb->pb->commit_editor;
svn_txdelta_window_handler_t handler;
void *handler_baton;
apr_pool_t *pool = nb->rb->pool;
LDR_DBG(("Setting fulltext for %p\n", nb->file_baton));
SVN_ERR(commit_editor->apply_textdelta(nb->file_baton, nb->base_checksum,
pool, &handler, &handler_baton));
*stream = svn_txdelta_target_push(handler, handler_baton,
svn_stream_empty(pool), pool);
return SVN_NO_ERROR;
}
static svn_error_t *
apply_textdelta(svn_txdelta_window_handler_t *handler,
void **handler_baton,
void *node_baton)
{
struct node_baton *nb = node_baton;
const struct svn_delta_editor_t *commit_editor = nb->rb->pb->commit_editor;
apr_pool_t *pool = nb->rb->pool;
LDR_DBG(("Applying textdelta to %p\n", nb->file_baton));
SVN_ERR(commit_editor->apply_textdelta(nb->file_baton, nb->base_checksum,
pool, handler, handler_baton));
return SVN_NO_ERROR;
}
static svn_error_t *
close_node(void *baton)
{
struct node_baton *nb = baton;
const struct svn_delta_editor_t *commit_editor = nb->rb->pb->commit_editor;
+ apr_pool_t *pool = nb->rb->pool;
+ apr_hash_index_t *hi;
+
+ for (hi = apr_hash_first(pool, nb->prop_changes);
+ hi; hi = apr_hash_next(hi))
+ {
+ const char *name = svn__apr_hash_index_key(hi);
+ svn_prop_t *prop = svn__apr_hash_index_val(hi);
+
+ switch (nb->kind)
+ {
+ case svn_node_file:
+ SVN_ERR(commit_editor->change_file_prop(nb->file_baton,
+ name, prop->value, pool));
+ break;
+ case svn_node_dir:
+ SVN_ERR(commit_editor->change_dir_prop(nb->rb->db->baton,
+ name, prop->value, pool));
+ break;
+ default:
+ break;
+ }
+ }
/* Pass a file node closure through to the editor *unless* we
deleted the file (which doesn't require us to open it). */
if ((nb->kind == svn_node_file) && (nb->file_baton))
{
LDR_DBG(("Closing file %p\n", nb->file_baton));
SVN_ERR(commit_editor->close_file(nb->file_baton, NULL, nb->rb->pool));
}
/* The svn_node_dir case is handled in close_revision */
return SVN_NO_ERROR;
}
static svn_error_t *
close_revision(void *baton)
{
struct revision_baton *rb = baton;
const svn_delta_editor_t *commit_editor = rb->pb->commit_editor;
void *commit_edit_baton = rb->pb->commit_edit_baton;
svn_revnum_t committed_rev = SVN_INVALID_REVNUM;
/* Fake revision 0 */
if (rb->rev == 0)
{
/* ### Don't print directly; generate a notification. */
if (! rb->pb->quiet)
SVN_ERR(svn_cmdline_printf(rb->pool, "* Loaded revision 0.\n"));
}
else if (commit_editor)
{
/* Close all pending open directories, and then close the edit
session itself */
while (rb->db && rb->db->parent)
{
LDR_DBG(("Closing dir %p\n", rb->db->baton));
SVN_ERR(commit_editor->close_directory(rb->db->baton, rb->pool));
rb->db = rb->db->parent;
}
/* root dir's baton */
LDR_DBG(("Closing edit on %p\n", commit_edit_baton));
SVN_ERR(commit_editor->close_directory(rb->db->baton, rb->pool));
SVN_ERR(commit_editor->close_edit(commit_edit_baton, rb->pool));
}
else
{
void *child_baton;
/* Legitimate revision with no node information */
SVN_ERR(svn_ra_get_commit_editor3(rb->pb->session, &commit_editor,
&commit_edit_baton, rb->revprop_table,
commit_callback, baton,
NULL, FALSE, rb->pool));
SVN_ERR(commit_editor->open_root(commit_edit_baton,
rb->rev - rb->rev_offset - 1,
rb->pool, &child_baton));
LDR_DBG(("Opened root %p\n", child_baton));
LDR_DBG(("Closing edit on %p\n", commit_edit_baton));
SVN_ERR(commit_editor->close_directory(child_baton, rb->pool));
SVN_ERR(commit_editor->close_edit(commit_edit_baton, rb->pool));
}
/* svn_fs_commit_txn() rewrites the datestamp and author properties;
we'll rewrite them again by hand after closing the commit_editor.
The only time we don't do this is for revision 0 when loaded into
a non-empty repository. */
if (rb->rev > 0)
{
committed_rev = get_revision_mapping(rb->pb->rev_map, rb->rev);
}
else if (rb->rev_offset == -1)
{
committed_rev = 0;
}
if (SVN_IS_VALID_REVNUM(committed_rev))
{
SVN_ERR(svn_repos__validate_prop(SVN_PROP_REVISION_DATE,
rb->datestamp, rb->pool));
SVN_ERR(svn_ra_change_rev_prop2(rb->pb->session, committed_rev,
SVN_PROP_REVISION_DATE,
NULL, rb->datestamp, rb->pool));
SVN_ERR(svn_repos__validate_prop(SVN_PROP_REVISION_AUTHOR,
rb->author, rb->pool));
SVN_ERR(svn_ra_change_rev_prop2(rb->pb->session, committed_rev,
SVN_PROP_REVISION_AUTHOR,
NULL, rb->author, rb->pool));
}
svn_pool_destroy(rb->pool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_rdump__load_dumpstream(svn_stream_t *stream,
svn_ra_session_t *session,
svn_ra_session_t *aux_session,
svn_boolean_t quiet,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
svn_repos_parse_fns3_t *parser;
struct parse_baton *parse_baton;
const svn_string_t *lock_string;
svn_boolean_t be_atomic;
svn_error_t *err;
const char *session_url, *root_url, *parent_dir;
SVN_ERR(svn_ra_has_capability(session, &be_atomic,
SVN_RA_CAPABILITY_ATOMIC_REVPROPS,
pool));
SVN_ERR(get_lock(&lock_string, session, cancel_func, cancel_baton, pool));
SVN_ERR(svn_ra_get_repos_root2(session, &root_url, pool));
SVN_ERR(svn_ra_get_session_url(session, &session_url, pool));
SVN_ERR(svn_ra_get_path_relative_to_root(session, &parent_dir,
session_url, pool));
parser = apr_pcalloc(pool, sizeof(*parser));
parser->magic_header_record = magic_header_record;
parser->uuid_record = uuid_record;
parser->new_revision_record = new_revision_record;
parser->new_node_record = new_node_record;
parser->set_revision_property = set_revision_property;
parser->set_node_property = set_node_property;
parser->delete_node_property = delete_node_property;
parser->remove_node_props = remove_node_props;
parser->set_fulltext = set_fulltext;
parser->apply_textdelta = apply_textdelta;
parser->close_node = close_node;
parser->close_revision = close_revision;
parse_baton = apr_pcalloc(pool, sizeof(*parse_baton));
parse_baton->session = session;
parse_baton->aux_session = aux_session;
parse_baton->quiet = quiet;
parse_baton->root_url = root_url;
parse_baton->parent_dir = parent_dir;
parse_baton->rev_map = apr_hash_make(pool);
parse_baton->last_rev_mapped = SVN_INVALID_REVNUM;
parse_baton->oldest_dumpstream_rev = SVN_INVALID_REVNUM;
err = svn_repos_parse_dumpstream3(stream, parser, parse_baton, FALSE,
cancel_func, cancel_baton, pool);
/* If all goes well, or if we're cancelled cleanly, don't leave a
stray lock behind. */
if ((! err) || (err && (err->apr_err == SVN_ERR_CANCELLED)))
err = svn_error_compose_create(
svn_ra__release_operational_lock(session, SVNRDUMP_PROP_LOCK,
lock_string, pool),
err);
return err;
}
Index: vendor/subversion/dist/subversion/svnserve/serve.c
===================================================================
--- vendor/subversion/dist/subversion/svnserve/serve.c (revision 286500)
+++ vendor/subversion/dist/subversion/svnserve/serve.c (revision 286501)
@@ -1,3694 +1,3710 @@
/*
* serve.c : Functions for serving the Subversion protocol
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include <limits.h> /* for UINT_MAX */
#include <stdarg.h>
#define APR_WANT_STRFUNC
#include <apr_want.h>
#include <apr_general.h>
#include <apr_lib.h>
#include <apr_strings.h>
#include "svn_compat.h"
#include "svn_private_config.h" /* For SVN_PATH_LOCAL_SEPARATOR */
#include "svn_hash.h"
#include "svn_types.h"
#include "svn_string.h"
#include "svn_pools.h"
#include "svn_error.h"
#include "svn_ra.h" /* for SVN_RA_CAPABILITY_* */
#include "svn_ra_svn.h"
#include "svn_repos.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_time.h"
#include "svn_config.h"
#include "svn_props.h"
#include "svn_mergeinfo.h"
#include "svn_user.h"
#include "private/svn_log.h"
#include "private/svn_mergeinfo_private.h"
#include "private/svn_ra_svn_private.h"
#include "private/svn_fspath.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h> /* For getpid() */
#endif
#include "server.h"
typedef struct commit_callback_baton_t {
apr_pool_t *pool;
svn_revnum_t *new_rev;
const char **date;
const char **author;
const char **post_commit_err;
} commit_callback_baton_t;
typedef struct report_driver_baton_t {
server_baton_t *sb;
const char *repos_url; /* Decoded repository URL. */
void *report_baton;
svn_error_t *err;
/* so update() can distinguish checkout from update in logging */
int entry_counter;
svn_boolean_t only_empty_entries;
/* for diff() logging */
svn_revnum_t *from_rev;
} report_driver_baton_t;
typedef struct log_baton_t {
const char *fs_path;
svn_ra_svn_conn_t *conn;
int stack_depth;
} log_baton_t;
typedef struct file_revs_baton_t {
svn_ra_svn_conn_t *conn;
apr_pool_t *pool; /* Pool provided in the handler call. */
} file_revs_baton_t;
typedef struct fs_warning_baton_t {
server_baton_t *server;
svn_ra_svn_conn_t *conn;
apr_pool_t *pool;
} fs_warning_baton_t;
typedef struct authz_baton_t {
server_baton_t *server;
svn_ra_svn_conn_t *conn;
} authz_baton_t;
/* Write LEN bytes of ERRSTR to LOG_FILE with svn_io_file_write(). */
static svn_error_t *
log_write(apr_file_t *log_file, const char *errstr, apr_size_t len,
apr_pool_t *pool)
{
return svn_io_file_write(log_file, errstr, &len, pool);
}
void
log_error(svn_error_t *err, apr_file_t *log_file, const char *remote_host,
const char *user, const char *repos, apr_pool_t *pool)
{
const char *timestr, *continuation;
char errbuf[256];
/* 8192 from MAX_STRING_LEN in from httpd-2.2.4/include/httpd.h */
char errstr[8192];
if (err == SVN_NO_ERROR)
return;
if (log_file == NULL)
return;
timestr = svn_time_to_cstring(apr_time_now(), pool);
remote_host = (remote_host ? remote_host : "-");
user = (user ? user : "-");
repos = (repos ? repos : "-");
continuation = "";
while (err != NULL)
{
const char *message = svn_err_best_message(err, errbuf, sizeof(errbuf));
/* based on httpd-2.2.4/server/log.c:log_error_core */
apr_size_t len = apr_snprintf(errstr, sizeof(errstr),
"%" APR_PID_T_FMT
" %s %s %s %s ERR%s %s %ld %d ",
getpid(), timestr, remote_host, user,
repos, continuation,
err->file ? err->file : "-", err->line,
err->apr_err);
len += escape_errorlog_item(errstr + len, message,
sizeof(errstr) - len);
/* Truncate for the terminator (as apr_snprintf does) */
if (len > sizeof(errstr) - sizeof(APR_EOL_STR)) {
len = sizeof(errstr) - sizeof(APR_EOL_STR);
}
strcpy(errstr + len, APR_EOL_STR);
len += strlen(APR_EOL_STR);
svn_error_clear(log_write(log_file, errstr, len, pool));
continuation = "-";
err = err->child;
}
}
/* Call log_error with log_file, remote_host, user, and repos
arguments from SERVER and CONN. */
static void
log_server_error(svn_error_t *err, server_baton_t *server,
svn_ra_svn_conn_t *conn, apr_pool_t *pool)
{
log_error(err, server->log_file, svn_ra_svn_conn_remote_host(conn),
server->user, server->repos_name, pool);
}
/* svn_error_create() a new error, log_server_error() it, and
return it. */
static svn_error_t *
error_create_and_log(apr_status_t apr_err, svn_error_t *child,
const char *message, server_baton_t *server,
svn_ra_svn_conn_t *conn, apr_pool_t *pool)
{
svn_error_t *err = svn_error_create(apr_err, child, message);
log_server_error(err, server, conn, pool);
return err;
}
/* Log a failure ERR, transmit ERR back to the client (as part of a
"failure" notification), consume ERR, and flush the connection. */
static svn_error_t *
log_fail_and_flush(svn_error_t *err, server_baton_t *server,
svn_ra_svn_conn_t *conn, apr_pool_t *pool)
{
svn_error_t *io_err;
log_server_error(err, server, conn, pool);
io_err = svn_ra_svn__write_cmd_failure(conn, pool, err);
svn_error_clear(err);
SVN_ERR(io_err);
return svn_ra_svn__flush(conn, pool);
}
/* Log a client command. */
static svn_error_t *log_command(server_baton_t *b,
svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *fmt, ...)
{
const char *remote_host, *timestr, *log, *line;
va_list ap;
apr_size_t nbytes;
if (b->log_file == NULL)
return SVN_NO_ERROR;
remote_host = svn_ra_svn_conn_remote_host(conn);
timestr = svn_time_to_cstring(apr_time_now(), pool);
va_start(ap, fmt);
log = apr_pvsprintf(pool, fmt, ap);
va_end(ap);
line = apr_psprintf(pool, "%" APR_PID_T_FMT
" %s %s %s %s %s" APR_EOL_STR,
getpid(), timestr,
(remote_host ? remote_host : "-"),
(b->user ? b->user : "-"), b->repos_name, log);
nbytes = strlen(line);
return log_write(b->log_file, line, nbytes, pool);
}
/* Log an authz failure */
static svn_error_t *
log_authz_denied(const char *path,
svn_repos_authz_access_t required,
server_baton_t *b,
svn_ra_svn_conn_t *conn,
apr_pool_t *pool)
{
const char *timestr, *remote_host, *line;
if (b->log_file == NULL)
return SVN_NO_ERROR;
if (!b->user)
return SVN_NO_ERROR;
timestr = svn_time_to_cstring(apr_time_now(), pool);
remote_host = svn_ra_svn_conn_remote_host(conn);
line = apr_psprintf(pool, "%" APR_PID_T_FMT
" %s %s %s %s Authorization Failed %s%s %s" APR_EOL_STR,
getpid(), timestr,
(remote_host ? remote_host : "-"),
(b->user ? b->user : "-"),
b->repos_name,
(required & svn_authz_recursive ? "recursive " : ""),
(required & svn_authz_write ? "write" : "read"),
(path && path[0] ? path : "/"));
return log_write(b->log_file, line, strlen(line), pool);
}
svn_error_t *load_pwdb_config(server_baton_t *server,
svn_ra_svn_conn_t *conn,
apr_pool_t *pool)
{
const char *pwdb_path;
svn_error_t *err;
svn_config_get(server->cfg, &pwdb_path, SVN_CONFIG_SECTION_GENERAL,
SVN_CONFIG_OPTION_PASSWORD_DB, NULL);
server->pwdb = NULL;
if (pwdb_path)
{
pwdb_path = svn_dirent_internal_style(pwdb_path, pool);
pwdb_path = svn_dirent_join(server->base, pwdb_path, pool);
err = svn_config_read3(&server->pwdb, pwdb_path, TRUE,
FALSE, FALSE, pool);
if (err)
{
log_server_error(err, server, conn, pool);
/* Because it may be possible to read the pwdb file with some
access methods and not others, ignore errors reading the pwdb
file and just don't present password authentication as an
option. Also, some authentications (e.g. --tunnel) can
proceed without it anyway.
### Not entirely sure why SVN_ERR_BAD_FILENAME is checked
### for here. That seems to have been introduced in r856914,
### and only in r870942 was the APR_EACCES check introduced. */
if (err->apr_err != SVN_ERR_BAD_FILENAME
&& ! APR_STATUS_IS_EACCES(err->apr_err))
{
/* Now that we've logged the error, clear it and return a
* nice, generic error to the user:
* http://subversion.tigris.org/issues/show_bug.cgi?id=2271 */
svn_error_clear(err);
return svn_error_create(SVN_ERR_AUTHN_FAILED, NULL, NULL);
}
else
/* Ignore SVN_ERR_BAD_FILENAME and APR_EACCES and proceed. */
svn_error_clear(err);
}
}
return SVN_NO_ERROR;
}
/* Canonicalize *ACCESS_FILE based on the type of argument. Results are
* placed in *ACCESS_FILE. SERVER baton is used to convert relative paths to
* absolute paths rooted at the server root. REPOS_ROOT is used to calculate
* an absolute URL for repos-relative URLs. */
static svn_error_t *
canonicalize_access_file(const char **access_file, server_baton_t *server,
const char *repos_root, apr_pool_t *pool)
{
if (svn_path_is_url(*access_file))
{
*access_file = svn_uri_canonicalize(*access_file, pool);
}
else if (svn_path_is_repos_relative_url(*access_file))
{
const char *repos_root_url;
SVN_ERR(svn_uri_get_file_url_from_dirent(&repos_root_url, repos_root,
pool));
SVN_ERR(svn_path_resolve_repos_relative_url(access_file, *access_file,
repos_root_url, pool));
*access_file = svn_uri_canonicalize(*access_file, pool);
}
else
{
*access_file = svn_dirent_internal_style(*access_file, pool);
*access_file = svn_dirent_join(server->base, *access_file, pool);
}
return SVN_NO_ERROR;
}
svn_error_t *load_authz_config(server_baton_t *server,
svn_ra_svn_conn_t *conn,
const char *repos_root,
apr_pool_t *pool)
{
const char *authzdb_path;
const char *groupsdb_path;
svn_error_t *err;
/* Read authz configuration. */
svn_config_get(server->cfg, &authzdb_path, SVN_CONFIG_SECTION_GENERAL,
SVN_CONFIG_OPTION_AUTHZ_DB, NULL);
svn_config_get(server->cfg, &groupsdb_path, SVN_CONFIG_SECTION_GENERAL,
SVN_CONFIG_OPTION_GROUPS_DB, NULL);
if (authzdb_path)
{
const char *case_force_val;
/* Canonicalize and add the base onto the authzdb_path (if needed). */
err = canonicalize_access_file(&authzdb_path, server,
repos_root, pool);
/* Same for the groupsdb_path if it is present. */
if (groupsdb_path && !err)
err = canonicalize_access_file(&groupsdb_path, server,
repos_root, pool);
if (!err)
err = svn_repos_authz_read2(&server->authzdb, authzdb_path,
groupsdb_path, TRUE, pool);
if (err)
{
log_server_error(err, server, conn, pool);
svn_error_clear(err);
return svn_error_create(SVN_ERR_AUTHZ_INVALID_CONFIG, NULL, NULL);
}
/* Are we going to be case-normalizing usernames when we consult
* this authz file? */
svn_config_get(server->cfg, &case_force_val, SVN_CONFIG_SECTION_GENERAL,
SVN_CONFIG_OPTION_FORCE_USERNAME_CASE, NULL);
if (case_force_val)
{
if (strcmp(case_force_val, "upper") == 0)
server->username_case = CASE_FORCE_UPPER;
else if (strcmp(case_force_val, "lower") == 0)
server->username_case = CASE_FORCE_LOWER;
else
server->username_case = CASE_ASIS;
}
}
else
{
server->authzdb = NULL;
server->username_case = CASE_ASIS;
}
return SVN_NO_ERROR;
}
/* Set *FS_PATH to the portion of URL that is the path within the
repository, if URL is inside REPOS_URL (if URL is not inside
REPOS_URL, then error, with the effect on *FS_PATH undefined).
If the resultant fs path would be the empty string (i.e., URL and
REPOS_URL are the same), then set *FS_PATH to "/".
Assume that REPOS_URL and URL are already URI-decoded. */
static svn_error_t *get_fs_path(const char *repos_url, const char *url,
const char **fs_path)
{
apr_size_t len;
len = strlen(repos_url);
if (strncmp(url, repos_url, len) != 0)
return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
"'%s' is not the same repository as '%s'",
url, repos_url);
*fs_path = url + len;
if (! **fs_path)
*fs_path = "/";
return SVN_NO_ERROR;
}
/* --- AUTHENTICATION AND AUTHORIZATION FUNCTIONS --- */
/* Convert TEXT to upper case if TO_UPPERCASE is TRUE, else
converts it to lower case. */
static void convert_case(char *text, svn_boolean_t to_uppercase)
{
char *c = text;
while (*c)
{
*c = (char)(to_uppercase ? apr_toupper(*c) : apr_tolower(*c));
++c;
}
}
/* Set *ALLOWED to TRUE if PATH is accessible in the REQUIRED mode to
the user described in BATON according to the authz rules in BATON.
Use POOL for temporary allocations only. If no authz rules are
present in BATON, grant access by default. */
static svn_error_t *authz_check_access(svn_boolean_t *allowed,
const char *path,
svn_repos_authz_access_t required,
server_baton_t *b,
svn_ra_svn_conn_t *conn,
apr_pool_t *pool)
{
/* If authz cannot be performed, grant access. This is NOT the same
as the default policy when authz is performed on a path with no
rules. In the latter case, the default is to deny access, and is
set by svn_repos_authz_check_access. */
if (!b->authzdb)
{
*allowed = TRUE;
return SVN_NO_ERROR;
}
/* If the authz request is for the empty path (ie. ""), replace it
with the root path. This happens because of stripping done at
various levels in svnserve that remove the leading / on an
absolute path. Passing such a malformed path to the authz
routines throws them into an infinite loop and makes them miss
ACLs. */
if (path)
path = svn_fspath__canonicalize(path, pool);
/* If we have a username, and we've not yet used it + any username
case normalization that might be requested to determine "the
username we used for authz purposes", do so now. */
if (b->user && (! b->authz_user))
{
char *authz_user = apr_pstrdup(b->pool, b->user);
if (b->username_case == CASE_FORCE_UPPER)
convert_case(authz_user, TRUE);
else if (b->username_case == CASE_FORCE_LOWER)
convert_case(authz_user, FALSE);
b->authz_user = authz_user;
}
SVN_ERR(svn_repos_authz_check_access(b->authzdb, b->authz_repos_name,
path, b->authz_user, required,
allowed, pool));
if (!*allowed)
SVN_ERR(log_authz_denied(path, required, b, conn, pool));
return SVN_NO_ERROR;
}
/* Set *ALLOWED to TRUE if PATH is readable by the user described in
* BATON. Use POOL for temporary allocations only. ROOT is not used.
* Implements the svn_repos_authz_func_t interface.
*/
static svn_error_t *authz_check_access_cb(svn_boolean_t *allowed,
svn_fs_root_t *root,
const char *path,
void *baton,
apr_pool_t *pool)
{
authz_baton_t *sb = baton;
return authz_check_access(allowed, path, svn_authz_read,
sb->server, sb->conn, pool);
}
/* If authz is enabled in the specified BATON, return a read authorization
function. Otherwise, return NULL. */
static svn_repos_authz_func_t authz_check_access_cb_func(server_baton_t *baton)
{
if (baton->authzdb)
return authz_check_access_cb;
return NULL;
}
/* Set *ALLOWED to TRUE if the REQUIRED access to PATH is granted,
* according to the state in BATON. Use POOL for temporary
* allocations only. ROOT is not used. Implements the
* svn_repos_authz_callback_t interface.
*/
static svn_error_t *authz_commit_cb(svn_repos_authz_access_t required,
svn_boolean_t *allowed,
svn_fs_root_t *root,
const char *path,
void *baton,
apr_pool_t *pool)
{
authz_baton_t *sb = baton;
return authz_check_access(allowed, path, required,
sb->server, sb->conn, pool);
}
enum access_type get_access(server_baton_t *b, enum authn_type auth)
{
const char *var = (auth == AUTHENTICATED) ? SVN_CONFIG_OPTION_AUTH_ACCESS :
SVN_CONFIG_OPTION_ANON_ACCESS;
const char *val, *def = (auth == AUTHENTICATED) ? "write" : "read";
enum access_type result;
svn_config_get(b->cfg, &val, SVN_CONFIG_SECTION_GENERAL, var, def);
result = (strcmp(val, "write") == 0 ? WRITE_ACCESS :
strcmp(val, "read") == 0 ? READ_ACCESS : NO_ACCESS);
return (result == WRITE_ACCESS && b->read_only) ? READ_ACCESS : result;
}
static enum access_type current_access(server_baton_t *b)
{
return get_access(b, (b->user) ? AUTHENTICATED : UNAUTHENTICATED);
}
/* Send authentication mechs for ACCESS_TYPE to the client. If NEEDS_USERNAME
is true, don't send anonymous mech even if that would give the desired
access. */
static svn_error_t *send_mechs(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
server_baton_t *b, enum access_type required,
svn_boolean_t needs_username)
{
if (!needs_username && get_access(b, UNAUTHENTICATED) >= required)
SVN_ERR(svn_ra_svn__write_word(conn, pool, "ANONYMOUS"));
if (b->tunnel_user && get_access(b, AUTHENTICATED) >= required)
SVN_ERR(svn_ra_svn__write_word(conn, pool, "EXTERNAL"));
if (b->pwdb && get_access(b, AUTHENTICATED) >= required)
SVN_ERR(svn_ra_svn__write_word(conn, pool, "CRAM-MD5"));
return SVN_NO_ERROR;
}
/* Context for cleanup handler. */
struct cleanup_fs_access_baton
{
svn_fs_t *fs;
apr_pool_t *pool;
};
/* Pool cleanup handler. Make sure fs's access_t points to NULL when
the command pool is destroyed. */
static apr_status_t cleanup_fs_access(void *data)
{
svn_error_t *serr;
struct cleanup_fs_access_baton *baton = data;
serr = svn_fs_set_access(baton->fs, NULL);
if (serr)
{
apr_status_t apr_err = serr->apr_err;
svn_error_clear(serr);
return apr_err;
}
return APR_SUCCESS;
}
/* Create an svn_fs_access_t in POOL for USER and associate it with
B's filesystem. Also, register a cleanup handler with POOL which
de-associates the svn_fs_access_t from B's filesystem. */
static svn_error_t *
create_fs_access(server_baton_t *b, apr_pool_t *pool)
{
svn_fs_access_t *fs_access;
struct cleanup_fs_access_baton *cleanup_baton;
if (!b->user)
return SVN_NO_ERROR;
SVN_ERR(svn_fs_create_access(&fs_access, b->user, pool));
SVN_ERR(svn_fs_set_access(b->fs, fs_access));
cleanup_baton = apr_pcalloc(pool, sizeof(*cleanup_baton));
cleanup_baton->pool = pool;
cleanup_baton->fs = b->fs;
apr_pool_cleanup_register(pool, cleanup_baton, cleanup_fs_access,
apr_pool_cleanup_null);
return SVN_NO_ERROR;
}
/* Authenticate, once the client has chosen a mechanism and possibly
* sent an initial mechanism token. On success, set *success to true
* and b->user to the authenticated username (or NULL for anonymous).
* On authentication failure, report failure to the client and set
* *success to FALSE. On communications failure, return an error.
* If NEEDS_USERNAME is TRUE, don't allow anonymous authentication. */
static svn_error_t *auth(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
const char *mech, const char *mecharg,
server_baton_t *b, enum access_type required,
svn_boolean_t needs_username,
svn_boolean_t *success)
{
const char *user;
*success = FALSE;
if (get_access(b, AUTHENTICATED) >= required
&& b->tunnel_user && strcmp(mech, "EXTERNAL") == 0)
{
if (*mecharg && strcmp(mecharg, b->tunnel_user) != 0)
return svn_ra_svn__write_tuple(conn, pool, "w(c)", "failure",
"Requested username does not match");
b->user = b->tunnel_user;
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w()", "success"));
*success = TRUE;
return SVN_NO_ERROR;
}
if (get_access(b, UNAUTHENTICATED) >= required
&& strcmp(mech, "ANONYMOUS") == 0 && ! needs_username)
{
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w()", "success"));
*success = TRUE;
return SVN_NO_ERROR;
}
if (get_access(b, AUTHENTICATED) >= required
&& b->pwdb && strcmp(mech, "CRAM-MD5") == 0)
{
SVN_ERR(svn_ra_svn_cram_server(conn, pool, b->pwdb, &user, success));
b->user = apr_pstrdup(b->pool, user);
return SVN_NO_ERROR;
}
return svn_ra_svn__write_tuple(conn, pool, "w(c)", "failure",
"Must authenticate with listed mechanism");
}
/* Perform an authentication request using the built-in SASL implementation. */
static svn_error_t *
internal_auth_request(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
server_baton_t *b, enum access_type required,
svn_boolean_t needs_username)
{
svn_boolean_t success;
const char *mech, *mecharg;
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success"));
SVN_ERR(send_mechs(conn, pool, b, required, needs_username));
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)c)", b->realm));
do
{
SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "w(?c)", &mech, &mecharg));
if (!*mech)
break;
SVN_ERR(auth(conn, pool, mech, mecharg, b, required, needs_username,
&success));
}
while (!success);
return SVN_NO_ERROR;
}
/* Perform an authentication request in order to get an access level of
* REQUIRED or higher. Since the client may escape the authentication
* exchange, the caller should check current_access(b) to see if
* authentication succeeded. */
static svn_error_t *auth_request(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
server_baton_t *b, enum access_type required,
svn_boolean_t needs_username)
{
#ifdef SVN_HAVE_SASL
if (b->use_sasl)
return cyrus_auth_request(conn, pool, b, required, needs_username);
#endif
return internal_auth_request(conn, pool, b, required, needs_username);
}
/* Send a trivial auth notification on CONN which lists no mechanisms,
* indicating that authentication is unnecessary. Usually called in
* response to invocation of a svnserve command.
*/
static svn_error_t *trivial_auth_request(svn_ra_svn_conn_t *conn,
apr_pool_t *pool, server_baton_t *b)
{
return svn_ra_svn__write_cmd_response(conn, pool, "()c", "");
}
/* Ensure that the client has the REQUIRED access by checking the
* access directives (both blanket and per-directory) in BATON. If
* PATH is NULL, then only the blanket access configuration will
* impact the result.
*
* If NEEDS_USERNAME is TRUE, then a lookup is only successful if the
* user described in BATON is authenticated and, well, has a username
* assigned to him.
*
* Use POOL for temporary allocations only.
*/
static svn_boolean_t lookup_access(apr_pool_t *pool,
server_baton_t *baton,
svn_ra_svn_conn_t *conn,
svn_repos_authz_access_t required,
const char *path,
svn_boolean_t needs_username)
{
enum access_type req = (required & svn_authz_write) ?
WRITE_ACCESS : READ_ACCESS;
svn_boolean_t authorized;
svn_error_t *err;
/* Get authz's opinion on the access. */
err = authz_check_access(&authorized, path, required, baton, conn, pool);
/* If an error made lookup fail, deny access. */
if (err)
{
log_server_error(err, baton, conn, pool);
svn_error_clear(err);
return FALSE;
}
/* If the required access is blanket-granted AND granted by authz
AND we already have a username if one is required, then the
lookup has succeeded. */
if (current_access(baton) >= req
&& authorized
&& (! needs_username || baton->user))
return TRUE;
return FALSE;
}
/* Check that the client has the REQUIRED access by consulting the
* authentication and authorization states stored in BATON. If the
* client does not have the required access credentials, attempt to
* authenticate the client to get that access, using CONN for
* communication.
*
* This function is supposed to be called to handle the authentication
* half of a standard svn protocol reply. If an error is returned, it
* probably means that the server can terminate the client connection
* with an apologetic error, as it implies an authentication failure.
*
* PATH and NEEDS_USERNAME are passed along to lookup_access, their
* behaviour is documented there.
*/
static svn_error_t *must_have_access(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
server_baton_t *b,
svn_repos_authz_access_t required,
const char *path,
svn_boolean_t needs_username)
{
enum access_type req = (required & svn_authz_write) ?
WRITE_ACCESS : READ_ACCESS;
/* See whether the user already has the required access. If so,
nothing needs to be done. Create the FS access and send a
trivial auth request. */
if (lookup_access(pool, b, conn, required, path, needs_username))
{
SVN_ERR(create_fs_access(b, pool));
return trivial_auth_request(conn, pool, b);
}
/* If the required blanket access can be obtained by authenticating,
try that. Unfortunately, we can't tell until after
authentication whether authz will work or not. We force
requiring a username because we need one to be able to check
authz configuration again with a different user credentials than
the first time round. */
if (b->user == NULL
&& get_access(b, AUTHENTICATED) >= req
&& (b->tunnel_user || b->pwdb || b->use_sasl))
SVN_ERR(auth_request(conn, pool, b, req, TRUE));
/* Now that an authentication has been done get the new take of
authz on the request. */
if (! lookup_access(pool, b, conn, required, path, needs_username))
return svn_error_create(SVN_ERR_RA_SVN_CMD_ERR,
error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED,
NULL, NULL, b, conn, pool),
NULL);
/* Else, access is granted, and there is much rejoicing. */
SVN_ERR(create_fs_access(b, pool));
return SVN_NO_ERROR;
}
/* --- REPORTER COMMAND SET --- */
/* To allow for pipelining, reporter commands have no reponses. If we
* get an error, we ignore all subsequent reporter commands and return
* the error finish_report, to be handled by the calling command.
*/
static svn_error_t *set_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
report_driver_baton_t *b = baton;
const char *path, *lock_token, *depth_word;
svn_revnum_t rev;
/* Default to infinity, for old clients that don't send depth. */
svn_depth_t depth = svn_depth_infinity;
svn_boolean_t start_empty;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "crb?(?c)?w",
&path, &rev, &start_empty, &lock_token,
&depth_word));
if (depth_word)
depth = svn_depth_from_word(depth_word);
path = svn_relpath_canonicalize(path, pool);
if (b->from_rev && strcmp(path, "") == 0)
*b->from_rev = rev;
if (!b->err)
b->err = svn_repos_set_path3(b->report_baton, path, rev, depth,
start_empty, lock_token, pool);
b->entry_counter++;
if (!start_empty)
b->only_empty_entries = FALSE;
return SVN_NO_ERROR;
}
static svn_error_t *delete_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
report_driver_baton_t *b = baton;
const char *path;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &path));
path = svn_relpath_canonicalize(path, pool);
if (!b->err)
b->err = svn_repos_delete_path(b->report_baton, path, pool);
return SVN_NO_ERROR;
}
static svn_error_t *link_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
report_driver_baton_t *b = baton;
const char *path, *url, *lock_token, *fs_path, *depth_word;
svn_revnum_t rev;
svn_boolean_t start_empty;
/* Default to infinity, for old clients that don't send depth. */
svn_depth_t depth = svn_depth_infinity;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "ccrb?(?c)?w",
&path, &url, &rev, &start_empty,
&lock_token, &depth_word));
/* ### WHAT?! The link path is an absolute URL?! Didn't see that
coming... -- cmpilato */
path = svn_relpath_canonicalize(path, pool);
url = svn_uri_canonicalize(url, pool);
if (depth_word)
depth = svn_depth_from_word(depth_word);
if (!b->err)
b->err = get_fs_path(svn_path_uri_decode(b->repos_url, pool),
svn_path_uri_decode(url, pool),
&fs_path);
if (!b->err)
b->err = svn_repos_link_path3(b->report_baton, path, fs_path, rev,
depth, start_empty, lock_token, pool);
b->entry_counter++;
return SVN_NO_ERROR;
}
static svn_error_t *finish_report(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
report_driver_baton_t *b = baton;
/* No arguments to parse. */
SVN_ERR(trivial_auth_request(conn, pool, b->sb));
if (!b->err)
b->err = svn_repos_finish_report(b->report_baton, pool);
return SVN_NO_ERROR;
}
static svn_error_t *abort_report(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
report_driver_baton_t *b = baton;
/* No arguments to parse. */
svn_error_clear(svn_repos_abort_report(b->report_baton, pool));
return SVN_NO_ERROR;
}
static const svn_ra_svn_cmd_entry_t report_commands[] = {
{ "set-path", set_path },
{ "delete-path", delete_path },
{ "link-path", link_path },
{ "finish-report", finish_report, TRUE },
{ "abort-report", abort_report, TRUE },
{ NULL }
};
/* Accept a report from the client, drive the network editor with the
* result, and then write an empty command response. If there is a
* non-protocol failure, accept_report will abort the edit and return
* a command error to be reported by handle_commands().
*
* If only_empty_entry is not NULL and the report contains only one
* item, and that item is empty, set *only_empty_entry to TRUE, else
* set it to FALSE.
*
* If from_rev is not NULL, set *from_rev to the revision number from
* the set-path on ""; if somehow set-path "" never happens, set
* *from_rev to SVN_INVALID_REVNUM.
*/
static svn_error_t *accept_report(svn_boolean_t *only_empty_entry,
svn_revnum_t *from_rev,
svn_ra_svn_conn_t *conn, apr_pool_t *pool,
server_baton_t *b, svn_revnum_t rev,
const char *target, const char *tgt_path,
svn_boolean_t text_deltas,
svn_depth_t depth,
svn_boolean_t send_copyfrom_args,
svn_boolean_t ignore_ancestry)
{
const svn_delta_editor_t *editor;
void *edit_baton, *report_baton;
report_driver_baton_t rb;
svn_error_t *err;
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
/* Make an svn_repos report baton. Tell it to drive the network editor
* when the report is complete. */
svn_ra_svn_get_editor(&editor, &edit_baton, conn, pool, NULL, NULL);
SVN_CMD_ERR(svn_repos_begin_report3(&report_baton, rev, b->repos,
b->fs_path->data, target, tgt_path,
text_deltas, depth, ignore_ancestry,
send_copyfrom_args,
editor, edit_baton,
authz_check_access_cb_func(b),
&ab, svn_ra_svn_zero_copy_limit(conn),
pool));
rb.sb = b;
rb.repos_url = svn_path_uri_decode(b->repos_url, pool);
rb.report_baton = report_baton;
rb.err = NULL;
rb.entry_counter = 0;
rb.only_empty_entries = TRUE;
rb.from_rev = from_rev;
if (from_rev)
*from_rev = SVN_INVALID_REVNUM;
err = svn_ra_svn__handle_commands2(conn, pool, report_commands, &rb, TRUE);
if (err)
{
/* Network or protocol error while handling commands. */
svn_error_clear(rb.err);
return err;
}
else if (rb.err)
{
/* Some failure during the reporting or editing operations. */
SVN_CMD_ERR(rb.err);
}
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
if (only_empty_entry)
*only_empty_entry = rb.entry_counter == 1 && rb.only_empty_entries;
return SVN_NO_ERROR;
}
/* --- MAIN COMMAND SET --- */
/* Write out a list of property diffs. PROPDIFFS is an array of svn_prop_t
* values. */
static svn_error_t *write_prop_diffs(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const apr_array_header_t *propdiffs)
{
int i;
for (i = 0; i < propdiffs->nelts; ++i)
{
const svn_prop_t *prop = &APR_ARRAY_IDX(propdiffs, i, svn_prop_t);
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "c(?s)",
prop->name, prop->value));
}
return SVN_NO_ERROR;
}
/* Write out a lock to the client. */
static svn_error_t *write_lock(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
svn_lock_t *lock)
{
const char *cdate, *edate;
cdate = svn_time_to_cstring(lock->creation_date, pool);
edate = lock->expiration_date
? svn_time_to_cstring(lock->expiration_date, pool) : NULL;
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "ccc(?c)c(?c)", lock->path,
lock->token, lock->owner, lock->comment,
cdate, edate));
return SVN_NO_ERROR;
}
/* ### This really belongs in libsvn_repos. */
/* Get the explicit properties and/or inherited properties for a PATH in
ROOT, with hardcoded committed-info values. */
static svn_error_t *
get_props(apr_hash_t **props,
apr_array_header_t **iprops,
authz_baton_t *b,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
/* Get the explicit properties. */
if (props)
{
svn_string_t *str;
svn_revnum_t crev;
const char *cdate, *cauthor, *uuid;
SVN_ERR(svn_fs_node_proplist(props, root, path, pool));
/* Hardcode the values for the committed revision, date, and author. */
SVN_ERR(svn_repos_get_committed_info(&crev, &cdate, &cauthor, root,
path, pool));
str = svn_string_create(apr_psprintf(pool, "%ld", crev),
pool);
svn_hash_sets(*props, SVN_PROP_ENTRY_COMMITTED_REV, str);
str = (cdate) ? svn_string_create(cdate, pool) : NULL;
svn_hash_sets(*props, SVN_PROP_ENTRY_COMMITTED_DATE, str);
str = (cauthor) ? svn_string_create(cauthor, pool) : NULL;
svn_hash_sets(*props, SVN_PROP_ENTRY_LAST_AUTHOR, str);
/* Hardcode the values for the UUID. */
SVN_ERR(svn_fs_get_uuid(svn_fs_root_fs(root), &uuid, pool));
str = (uuid) ? svn_string_create(uuid, pool) : NULL;
svn_hash_sets(*props, SVN_PROP_ENTRY_UUID, str);
}
/* Get any inherited properties the user is authorized to. */
if (iprops)
{
SVN_ERR(svn_repos_fs_get_inherited_props(
iprops, root, path, NULL,
authz_check_access_cb_func(b->server),
b, pool, pool));
}
return SVN_NO_ERROR;
}
/* Set BATON->FS_PATH for the repository URL found in PARAMS. */
static svn_error_t *reparent(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
const char *url;
const char *fs_path;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &url));
url = svn_uri_canonicalize(url, pool);
SVN_ERR(trivial_auth_request(conn, pool, b));
SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repos_url, pool),
svn_path_uri_decode(url, pool),
&fs_path));
SVN_ERR(log_command(b, conn, pool, "%s", svn_log__reparent(fs_path, pool)));
svn_stringbuf_set(b->fs_path, fs_path);
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
return SVN_NO_ERROR;
}
static svn_error_t *get_latest_rev(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_revnum_t rev;
SVN_ERR(log_command(b, conn, pool, "get-latest-rev"));
SVN_ERR(trivial_auth_request(conn, pool, b));
SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "r", rev));
return SVN_NO_ERROR;
}
static svn_error_t *get_dated_rev(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_revnum_t rev;
apr_time_t tm;
const char *timestr;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &timestr));
SVN_ERR(log_command(b, conn, pool, "get-dated-rev %s", timestr));
SVN_ERR(trivial_auth_request(conn, pool, b));
SVN_CMD_ERR(svn_time_from_cstring(&tm, timestr, pool));
SVN_CMD_ERR(svn_repos_dated_revision(&rev, b->repos, tm, pool));
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "r", rev));
return SVN_NO_ERROR;
}
/* Common logic for change_rev_prop() and change_rev_prop2(). */
static svn_error_t *do_change_rev_prop(svn_ra_svn_conn_t *conn,
server_baton_t *b,
svn_revnum_t rev,
const char *name,
const svn_string_t *const *old_value_p,
const svn_string_t *value,
apr_pool_t *pool)
{
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, FALSE));
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__change_rev_prop(rev, name, pool)));
SVN_CMD_ERR(svn_repos_fs_change_rev_prop4(b->repos, rev, b->user,
name, old_value_p, value,
TRUE, TRUE,
authz_check_access_cb_func(b), &ab,
pool));
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
return SVN_NO_ERROR;
}
static svn_error_t *change_rev_prop2(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_revnum_t rev;
const char *name;
svn_string_t *value;
const svn_string_t *const *old_value_p;
svn_string_t *old_value;
svn_boolean_t dont_care;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rc(?s)(b?s)",
&rev, &name, &value,
&dont_care, &old_value));
/* Argument parsing. */
if (dont_care)
old_value_p = NULL;
else
old_value_p = (const svn_string_t *const *)&old_value;
/* Input validation. */
if (dont_care && old_value)
{
svn_error_t *err;
err = svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
"'previous-value' and 'dont-care' cannot both be "
"set in 'change-rev-prop2' request");
return log_fail_and_flush(err, b, conn, pool);
}
/* Do it. */
SVN_ERR(do_change_rev_prop(conn, b, rev, name, old_value_p, value, pool));
return SVN_NO_ERROR;
}
static svn_error_t *change_rev_prop(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_revnum_t rev;
const char *name;
svn_string_t *value;
/* Because the revprop value was at one time mandatory, the usual
optional element pattern "(?s)" isn't used. */
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rc?s", &rev, &name, &value));
SVN_ERR(do_change_rev_prop(conn, b, rev, name, NULL, value, pool));
return SVN_NO_ERROR;
}
static svn_error_t *rev_proplist(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_revnum_t rev;
apr_hash_t *props;
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "r", &rev));
SVN_ERR(log_command(b, conn, pool, "%s", svn_log__rev_proplist(rev, pool)));
SVN_ERR(trivial_auth_request(conn, pool, b));
SVN_CMD_ERR(svn_repos_fs_revision_proplist(&props, b->repos, rev,
authz_check_access_cb_func(b), &ab,
pool));
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success"));
SVN_ERR(svn_ra_svn__write_proplist(conn, pool, props));
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))"));
return SVN_NO_ERROR;
}
static svn_error_t *rev_prop(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_revnum_t rev;
const char *name;
svn_string_t *value;
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rc", &rev, &name));
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__rev_prop(rev, name, pool)));
SVN_ERR(trivial_auth_request(conn, pool, b));
SVN_CMD_ERR(svn_repos_fs_revision_prop(&value, b->repos, rev, name,
authz_check_access_cb_func(b), &ab,
pool));
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "(?s)", value));
return SVN_NO_ERROR;
}
static svn_error_t *commit_done(const svn_commit_info_t *commit_info,
void *baton, apr_pool_t *pool)
{
commit_callback_baton_t *ccb = baton;
*ccb->new_rev = commit_info->revision;
*ccb->date = commit_info->date
? apr_pstrdup(ccb->pool, commit_info->date): NULL;
*ccb->author = commit_info->author
? apr_pstrdup(ccb->pool, commit_info->author) : NULL;
*ccb->post_commit_err = commit_info->post_commit_err
? apr_pstrdup(ccb->pool, commit_info->post_commit_err) : NULL;
return SVN_NO_ERROR;
}
/* Add the LOCK_TOKENS (if any) to the filesystem access context,
* checking path authorizations using the state in SB as we go.
* LOCK_TOKENS is an array of svn_ra_svn_item_t structs. Return a
* client error if LOCK_TOKENS is not a list of lists. If a lock
* violates the authz configuration, return SVN_ERR_RA_NOT_AUTHORIZED
* to the client. Use POOL for temporary allocations only.
*/
static svn_error_t *add_lock_tokens(svn_ra_svn_conn_t *conn,
const apr_array_header_t *lock_tokens,
server_baton_t *sb,
apr_pool_t *pool)
{
int i;
svn_fs_access_t *fs_access;
SVN_ERR(svn_fs_get_access(&fs_access, sb->fs));
/* If there is no access context, nowhere to add the tokens. */
if (! fs_access)
return SVN_NO_ERROR;
for (i = 0; i < lock_tokens->nelts; ++i)
{
const char *path, *token, *full_path;
svn_ra_svn_item_t *path_item, *token_item;
svn_ra_svn_item_t *item = &APR_ARRAY_IDX(lock_tokens, i,
svn_ra_svn_item_t);
if (item->kind != SVN_RA_SVN_LIST)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
"Lock tokens aren't a list of lists");
path_item = &APR_ARRAY_IDX(item->u.list, 0, svn_ra_svn_item_t);
if (path_item->kind != SVN_RA_SVN_STRING)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
"Lock path isn't a string");
token_item = &APR_ARRAY_IDX(item->u.list, 1, svn_ra_svn_item_t);
if (token_item->kind != SVN_RA_SVN_STRING)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
"Lock token isn't a string");
path = path_item->u.string->data;
full_path = svn_fspath__join(sb->fs_path->data,
svn_relpath_canonicalize(path, pool),
pool);
if (! lookup_access(pool, sb, conn, svn_authz_write,
full_path, TRUE))
return error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL, NULL,
sb, conn, pool);
token = token_item->u.string->data;
SVN_ERR(svn_fs_access_add_lock_token2(fs_access, path, token));
}
return SVN_NO_ERROR;
}
/* Unlock the paths with lock tokens in LOCK_TOKENS, ignoring any errors.
LOCK_TOKENS contains svn_ra_svn_item_t elements, assumed to be lists. */
static svn_error_t *unlock_paths(const apr_array_header_t *lock_tokens,
server_baton_t *sb,
svn_ra_svn_conn_t *conn,
apr_pool_t *pool)
{
int i;
apr_pool_t *iterpool;
iterpool = svn_pool_create(pool);
for (i = 0; i < lock_tokens->nelts; ++i)
{
svn_ra_svn_item_t *item, *path_item, *token_item;
const char *path, *token, *full_path;
svn_error_t *err;
svn_pool_clear(iterpool);
item = &APR_ARRAY_IDX(lock_tokens, i, svn_ra_svn_item_t);
path_item = &APR_ARRAY_IDX(item->u.list, 0, svn_ra_svn_item_t);
token_item = &APR_ARRAY_IDX(item->u.list, 1, svn_ra_svn_item_t);
path = path_item->u.string->data;
token = token_item->u.string->data;
full_path = svn_fspath__join(sb->fs_path->data,
svn_relpath_canonicalize(path, iterpool),
iterpool);
/* The lock may have become defunct after the commit, so ignore such
errors. */
err = svn_repos_fs_unlock(sb->repos, full_path, token,
FALSE, iterpool);
log_server_error(err, sb, conn, iterpool);
svn_error_clear(err);
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_error_t *commit(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
const char *log_msg = NULL,
*date = NULL,
*author = NULL,
*post_commit_err = NULL;
apr_array_header_t *lock_tokens;
svn_boolean_t keep_locks;
apr_array_header_t *revprop_list = NULL;
apr_hash_t *revprop_table;
const svn_delta_editor_t *editor;
void *edit_baton;
svn_boolean_t aborted;
commit_callback_baton_t ccb;
svn_revnum_t new_rev;
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
if (params->nelts == 1)
{
/* Clients before 1.2 don't send lock-tokens, keep-locks,
and rev-props fields. */
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &log_msg));
lock_tokens = NULL;
keep_locks = TRUE;
revprop_list = NULL;
}
else
{
/* Clients before 1.5 don't send the rev-props field. */
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "clb?l", &log_msg,
&lock_tokens, &keep_locks,
&revprop_list));
}
/* The handling for locks is a little problematic, because the
protocol won't let us send several auth requests once one has
succeeded. So we request write access and a username before
adding tokens (if we have any), and subsequently fail if a lock
violates authz. */
SVN_ERR(must_have_access(conn, pool, b, svn_authz_write,
NULL,
(lock_tokens && lock_tokens->nelts)));
/* Authorize the lock tokens and give them to the FS if we got
any. */
if (lock_tokens && lock_tokens->nelts)
SVN_CMD_ERR(add_lock_tokens(conn, lock_tokens, b, pool));
/* Ignore LOG_MSG, per the protocol. See ra_svn_commit(). */
if (revprop_list)
SVN_ERR(svn_ra_svn__parse_proplist(revprop_list, pool, &revprop_table));
else
{
revprop_table = apr_hash_make(pool);
svn_hash_sets(revprop_table, SVN_PROP_REVISION_LOG,
svn_string_create(log_msg, pool));
}
/* Get author from the baton, making sure clients can't circumvent
the authentication via the revision props. */
svn_hash_sets(revprop_table, SVN_PROP_REVISION_AUTHOR,
b->user ? svn_string_create(b->user, pool) : NULL);
ccb.pool = pool;
ccb.new_rev = &new_rev;
ccb.date = &date;
ccb.author = &author;
ccb.post_commit_err = &post_commit_err;
/* ### Note that svn_repos_get_commit_editor5 actually wants a decoded URL. */
SVN_CMD_ERR(svn_repos_get_commit_editor5
(&editor, &edit_baton, b->repos, NULL,
svn_path_uri_decode(b->repos_url, pool),
b->fs_path->data, revprop_table,
commit_done, &ccb,
authz_commit_cb, &ab, pool));
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
SVN_ERR(svn_ra_svn_drive_editor2(conn, pool, editor, edit_baton,
&aborted, FALSE));
if (!aborted)
{
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__commit(new_rev, pool)));
SVN_ERR(trivial_auth_request(conn, pool, b));
/* In tunnel mode, deltify before answering the client, because
answering may cause the client to terminate the connection
and thus kill the server. But otherwise, deltify after
answering the client, to avoid user-visible delay. */
if (b->tunnel)
SVN_ERR(svn_fs_deltify_revision(b->fs, new_rev, pool));
/* Unlock the paths. */
if (! keep_locks && lock_tokens && lock_tokens->nelts)
SVN_ERR(unlock_paths(lock_tokens, b, conn, pool));
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "r(?c)(?c)(?c)",
new_rev, date, author, post_commit_err));
if (! b->tunnel)
SVN_ERR(svn_fs_deltify_revision(b->fs, new_rev, pool));
}
return SVN_NO_ERROR;
}
static svn_error_t *get_file(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
const char *path, *full_path, *hex_digest;
svn_revnum_t rev;
svn_fs_root_t *root;
svn_stream_t *contents;
apr_hash_t *props = NULL;
apr_array_header_t *inherited_props;
svn_string_t write_str;
char buf[4096];
apr_size_t len;
svn_boolean_t want_props, want_contents;
apr_uint64_t wants_inherited_props;
svn_checksum_t *checksum;
svn_error_t *err, *write_err;
int i;
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
/* Parse arguments. */
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)bb?B", &path, &rev,
&want_props, &want_contents,
&wants_inherited_props));
if (wants_inherited_props == SVN_RA_SVN_UNSPECIFIED_NUMBER)
wants_inherited_props = FALSE;
full_path = svn_fspath__join(b->fs_path->data,
svn_relpath_canonicalize(path, pool), pool);
/* Check authorizations */
SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
full_path, FALSE));
if (!SVN_IS_VALID_REVNUM(rev))
SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__get_file(full_path, rev,
want_contents, want_props, pool)));
/* Fetch the properties and a stream for the contents. */
SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, pool));
SVN_CMD_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, root,
full_path, TRUE, pool));
hex_digest = svn_checksum_to_cstring_display(checksum, pool);
/* Fetch the file's explicit and/or inherited properties if
requested. Although the wants-iprops boolean was added to the
protocol in 1.8 a standard 1.8 client never requests iprops. */
if (want_props || wants_inherited_props)
SVN_CMD_ERR(get_props(want_props ? &props : NULL,
wants_inherited_props ? &inherited_props : NULL,
&ab, root, full_path,
pool));
if (want_contents)
SVN_CMD_ERR(svn_fs_file_contents(&contents, root, full_path, pool));
/* Send successful command response with revision and props. */
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((?c)r(!", "success",
hex_digest, rev));
SVN_ERR(svn_ra_svn__write_proplist(conn, pool, props));
if (wants_inherited_props)
{
apr_pool_t *iterpool = svn_pool_create(pool);
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)(?!"));
for (i = 0; i < inherited_props->nelts; i++)
{
svn_prop_inherited_item_t *iprop =
APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *);
svn_pool_clear(iterpool);
SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(c(!",
iprop->path_or_url));
SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, iprop->prop_hash));
SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))!",
iprop->path_or_url));
}
svn_pool_destroy(iterpool);
}
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))"));
/* Now send the file's contents. */
if (want_contents)
{
err = SVN_NO_ERROR;
while (1)
{
len = sizeof(buf);
err = svn_stream_read(contents, buf, &len);
if (err)
break;
if (len > 0)
{
write_str.data = buf;
write_str.len = len;
SVN_ERR(svn_ra_svn__write_string(conn, pool, &write_str));
}
if (len < sizeof(buf))
{
err = svn_stream_close(contents);
break;
}
}
write_err = svn_ra_svn__write_cstring(conn, pool, "");
if (write_err)
{
svn_error_clear(err);
return write_err;
}
SVN_CMD_ERR(err);
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
}
return SVN_NO_ERROR;
}
static svn_error_t *get_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
const char *path, *full_path;
svn_revnum_t rev;
apr_hash_t *entries, *props = NULL;
apr_array_header_t *inherited_props;
apr_hash_index_t *hi;
svn_fs_root_t *root;
apr_pool_t *subpool;
svn_boolean_t want_props, want_contents;
apr_uint64_t wants_inherited_props;
apr_uint64_t dirent_fields;
apr_array_header_t *dirent_fields_list = NULL;
svn_ra_svn_item_t *elt;
int i;
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)bb?l?B", &path, &rev,
&want_props, &want_contents,
&dirent_fields_list,
&wants_inherited_props));
if (wants_inherited_props == SVN_RA_SVN_UNSPECIFIED_NUMBER)
wants_inherited_props = FALSE;
if (! dirent_fields_list)
{
dirent_fields = SVN_DIRENT_ALL;
}
else
{
dirent_fields = 0;
for (i = 0; i < dirent_fields_list->nelts; ++i)
{
elt = &APR_ARRAY_IDX(dirent_fields_list, i, svn_ra_svn_item_t);
if (elt->kind != SVN_RA_SVN_WORD)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
"Dirent field not a string");
if (strcmp(SVN_RA_SVN_DIRENT_KIND, elt->u.word) == 0)
dirent_fields |= SVN_DIRENT_KIND;
else if (strcmp(SVN_RA_SVN_DIRENT_SIZE, elt->u.word) == 0)
dirent_fields |= SVN_DIRENT_SIZE;
else if (strcmp(SVN_RA_SVN_DIRENT_HAS_PROPS, elt->u.word) == 0)
dirent_fields |= SVN_DIRENT_HAS_PROPS;
else if (strcmp(SVN_RA_SVN_DIRENT_CREATED_REV, elt->u.word) == 0)
dirent_fields |= SVN_DIRENT_CREATED_REV;
else if (strcmp(SVN_RA_SVN_DIRENT_TIME, elt->u.word) == 0)
dirent_fields |= SVN_DIRENT_TIME;
else if (strcmp(SVN_RA_SVN_DIRENT_LAST_AUTHOR, elt->u.word) == 0)
dirent_fields |= SVN_DIRENT_LAST_AUTHOR;
}
}
full_path = svn_fspath__join(b->fs_path->data,
svn_relpath_canonicalize(path, pool), pool);
/* Check authorizations */
SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
full_path, FALSE));
if (!SVN_IS_VALID_REVNUM(rev))
SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__get_dir(full_path, rev,
want_contents, want_props,
dirent_fields, pool)));
/* Fetch the root of the appropriate revision. */
SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, pool));
/* Fetch the directory's explicit and/or inherited properties if
requested. Although the wants-iprops boolean was added to the
protocol in 1.8 a standard 1.8 client never requests iprops. */
if (want_props || wants_inherited_props)
SVN_CMD_ERR(get_props(want_props ? &props : NULL,
wants_inherited_props ? &inherited_props : NULL,
&ab, root, full_path,
pool));
+ /* Fetch the directories' entries before starting the response, to allow
+ proper error handling in cases like when FULL_PATH doesn't exist */
+ if (want_contents)
+ SVN_CMD_ERR(svn_fs_dir_entries(&entries, root, full_path, pool));
+
/* Begin response ... */
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(r(!", "success", rev));
SVN_ERR(svn_ra_svn__write_proplist(conn, pool, props));
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)(!"));
/* Fetch the directory entries if requested and send them immediately. */
if (want_contents)
{
/* Use epoch for a placeholder for a missing date. */
const char *missing_date = svn_time_to_cstring(0, pool);
- SVN_CMD_ERR(svn_fs_dir_entries(&entries, root, full_path, pool));
-
/* Transform the hash table's FS entries into dirents. This probably
* belongs in libsvn_repos. */
subpool = svn_pool_create(pool);
for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
{
const char *name = svn__apr_hash_index_key(hi);
svn_fs_dirent_t *fsent = svn__apr_hash_index_val(hi);
const char *file_path;
/* The fields in the entry tuple. */
svn_node_kind_t entry_kind = svn_node_none;
svn_filesize_t entry_size = 0;
svn_boolean_t has_props = FALSE;
/* If 'created rev' was not requested, send 0. We can't use
* SVN_INVALID_REVNUM as the tuple field is not optional.
* See the email thread on dev@, 2012-03-28, subject
* "buildbot failure in ASF Buildbot on svn-slik-w2k3-x64-ra",
* <http://svn.haxx.se/dev/archive-2012-03/0655.shtml>. */
svn_revnum_t created_rev = 0;
const char *cdate = NULL;
const char *last_author = NULL;
svn_pool_clear(subpool);
file_path = svn_fspath__join(full_path, name, subpool);
if (! lookup_access(subpool, b, conn, svn_authz_read,
file_path, FALSE))
continue;
if (dirent_fields & SVN_DIRENT_KIND)
entry_kind = fsent->kind;
if (dirent_fields & SVN_DIRENT_SIZE)
if (entry_kind != svn_node_dir)
SVN_CMD_ERR(svn_fs_file_length(&entry_size, root, file_path,
subpool));
if (dirent_fields & SVN_DIRENT_HAS_PROPS)
{
apr_hash_t *file_props;
/* has_props */
SVN_CMD_ERR(svn_fs_node_proplist(&file_props, root, file_path,
subpool));
has_props = (apr_hash_count(file_props) > 0);
}
if ((dirent_fields & SVN_DIRENT_LAST_AUTHOR)
|| (dirent_fields & SVN_DIRENT_TIME)
|| (dirent_fields & SVN_DIRENT_CREATED_REV))
{
/* created_rev, last_author, time */
SVN_CMD_ERR(svn_repos_get_committed_info(&created_rev,
&cdate,
&last_author,
root,
file_path,
subpool));
}
/* The client does not properly handle a missing CDATE. For
interoperability purposes, we must fill in some junk.
See libsvn_ra_svn/client.c:ra_svn_get_dir() */
if (cdate == NULL)
cdate = missing_date;
/* Send the entry. */
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "cwnbr(?c)(?c)", name,
svn_node_kind_to_word(entry_kind),
(apr_uint64_t) entry_size,
has_props, created_rev,
cdate, last_author));
}
svn_pool_destroy(subpool);
}
if (wants_inherited_props)
{
apr_pool_t *iterpool = svn_pool_create(pool);
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)(?!"));
for (i = 0; i < inherited_props->nelts; i++)
{
svn_prop_inherited_item_t *iprop =
APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *);
svn_pool_clear(iterpool);
SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(c(!",
iprop->path_or_url));
SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, iprop->prop_hash));
SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))!",
iprop->path_or_url));
}
svn_pool_destroy(iterpool);
}
/* Finish response. */
return svn_ra_svn__write_tuple(conn, pool, "!))");
}
static svn_error_t *update(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_revnum_t rev;
const char *target, *full_path, *depth_word;
svn_boolean_t recurse;
apr_uint64_t send_copyfrom_args; /* Optional; default FALSE */
apr_uint64_t ignore_ancestry; /* Optional; default FALSE */
/* Default to unknown. Old clients won't send depth, but we'll
handle that by converting recurse if necessary. */
svn_depth_t depth = svn_depth_unknown;
svn_boolean_t is_checkout;
/* Parse the arguments. */
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cb?wB?B", &rev, &target,
&recurse, &depth_word,
&send_copyfrom_args, &ignore_ancestry));
target = svn_relpath_canonicalize(target, pool);
if (depth_word)
depth = svn_depth_from_word(depth_word);
else
depth = SVN_DEPTH_INFINITY_OR_FILES(recurse);
full_path = svn_fspath__join(b->fs_path->data, target, pool);
/* Check authorization and authenticate the user if necessary. */
SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, full_path, FALSE));
if (!SVN_IS_VALID_REVNUM(rev))
SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
SVN_ERR(accept_report(&is_checkout, NULL,
conn, pool, b, rev, target, NULL, TRUE,
depth,
(send_copyfrom_args == TRUE) /* send_copyfrom_args */,
(ignore_ancestry == TRUE) /* ignore_ancestry */));
if (is_checkout)
{
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__checkout(full_path, rev,
depth, pool)));
}
else
{
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__update(full_path, rev, depth,
send_copyfrom_args, pool)));
}
return SVN_NO_ERROR;
}
static svn_error_t *switch_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_revnum_t rev;
const char *target, *depth_word;
const char *switch_url, *switch_path;
svn_boolean_t recurse;
/* Default to unknown. Old clients won't send depth, but we'll
handle that by converting recurse if necessary. */
svn_depth_t depth = svn_depth_unknown;
apr_uint64_t send_copyfrom_args; /* Optional; default FALSE */
apr_uint64_t ignore_ancestry; /* Optional; default TRUE */
/* Parse the arguments. */
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cbc?w?BB", &rev, &target,
&recurse, &switch_url, &depth_word,
&send_copyfrom_args, &ignore_ancestry));
target = svn_relpath_canonicalize(target, pool);
switch_url = svn_uri_canonicalize(switch_url, pool);
if (depth_word)
depth = svn_depth_from_word(depth_word);
else
depth = SVN_DEPTH_INFINITY_OR_FILES(recurse);
SVN_ERR(trivial_auth_request(conn, pool, b));
if (!SVN_IS_VALID_REVNUM(rev))
SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repos_url, pool),
svn_path_uri_decode(switch_url, pool),
&switch_path));
{
const char *full_path = svn_fspath__join(b->fs_path->data, target, pool);
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__switch(full_path, switch_path, rev,
depth, pool)));
}
return accept_report(NULL, NULL,
conn, pool, b, rev, target, switch_path, TRUE,
depth,
(send_copyfrom_args == TRUE) /* send_copyfrom_args */,
(ignore_ancestry != FALSE) /* ignore_ancestry */);
}
static svn_error_t *status(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_revnum_t rev;
const char *target, *depth_word;
svn_boolean_t recurse;
/* Default to unknown. Old clients won't send depth, but we'll
handle that by converting recurse if necessary. */
svn_depth_t depth = svn_depth_unknown;
/* Parse the arguments. */
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "cb?(?r)?w",
&target, &recurse, &rev, &depth_word));
target = svn_relpath_canonicalize(target, pool);
if (depth_word)
depth = svn_depth_from_word(depth_word);
else
depth = SVN_DEPTH_INFINITY_OR_EMPTY(recurse);
SVN_ERR(trivial_auth_request(conn, pool, b));
if (!SVN_IS_VALID_REVNUM(rev))
SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
{
const char *full_path = svn_fspath__join(b->fs_path->data, target, pool);
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__status(full_path, rev, depth, pool)));
}
return accept_report(NULL, NULL, conn, pool, b, rev, target, NULL, FALSE,
depth, FALSE, FALSE);
}
static svn_error_t *diff(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_revnum_t rev;
const char *target, *versus_url, *versus_path, *depth_word;
svn_boolean_t recurse, ignore_ancestry;
svn_boolean_t text_deltas;
/* Default to unknown. Old clients won't send depth, but we'll
handle that by converting recurse if necessary. */
svn_depth_t depth = svn_depth_unknown;
/* Parse the arguments. */
if (params->nelts == 5)
{
/* Clients before 1.4 don't send the text_deltas boolean or depth. */
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cbbc", &rev, &target,
&recurse, &ignore_ancestry, &versus_url));
text_deltas = TRUE;
depth_word = NULL;
}
else
{
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cbbcb?w",
&rev, &target, &recurse,
&ignore_ancestry, &versus_url,
&text_deltas, &depth_word));
}
target = svn_relpath_canonicalize(target, pool);
versus_url = svn_uri_canonicalize(versus_url, pool);
if (depth_word)
depth = svn_depth_from_word(depth_word);
else
depth = SVN_DEPTH_INFINITY_OR_FILES(recurse);
SVN_ERR(trivial_auth_request(conn, pool, b));
if (!SVN_IS_VALID_REVNUM(rev))
SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repos_url, pool),
svn_path_uri_decode(versus_url, pool),
&versus_path));
{
const char *full_path = svn_fspath__join(b->fs_path->data, target, pool);
svn_revnum_t from_rev;
SVN_ERR(accept_report(NULL, &from_rev,
conn, pool, b, rev, target, versus_path,
text_deltas, depth, FALSE, ignore_ancestry));
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__diff(full_path, from_rev, versus_path,
rev, depth, ignore_ancestry,
pool)));
}
return SVN_NO_ERROR;
}
/* Regardless of whether a client's capabilities indicate an
understanding of this command (by way of SVN_RA_SVN_CAP_MERGEINFO),
we provide a response.
ASSUMPTION: When performing a 'merge' with two URLs at different
revisions, the client will call this command more than once. */
static svn_error_t *get_mergeinfo(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_revnum_t rev;
apr_array_header_t *paths, *canonical_paths;
svn_mergeinfo_catalog_t mergeinfo;
int i;
apr_hash_index_t *hi;
const char *inherit_word;
svn_mergeinfo_inheritance_t inherit;
svn_boolean_t include_descendants;
apr_pool_t *iterpool;
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "l(?r)wb", &paths, &rev,
&inherit_word, &include_descendants));
inherit = svn_inheritance_from_word(inherit_word);
/* Canonicalize the paths which mergeinfo has been requested for. */
canonical_paths = apr_array_make(pool, paths->nelts, sizeof(const char *));
for (i = 0; i < paths->nelts; i++)
{
svn_ra_svn_item_t *item = &APR_ARRAY_IDX(paths, i, svn_ra_svn_item_t);
const char *full_path;
if (item->kind != SVN_RA_SVN_STRING)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
_("Path is not a string"));
full_path = svn_relpath_canonicalize(item->u.string->data, pool);
full_path = svn_fspath__join(b->fs_path->data, full_path, pool);
APR_ARRAY_PUSH(canonical_paths, const char *) = full_path;
}
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__get_mergeinfo(canonical_paths, inherit,
include_descendants,
pool)));
SVN_ERR(trivial_auth_request(conn, pool, b));
SVN_CMD_ERR(svn_repos_fs_get_mergeinfo(&mergeinfo, b->repos,
canonical_paths, rev,
inherit,
include_descendants,
authz_check_access_cb_func(b), &ab,
pool));
SVN_ERR(svn_mergeinfo__remove_prefix_from_catalog(&mergeinfo, mergeinfo,
b->fs_path->data, pool));
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success"));
iterpool = svn_pool_create(pool);
for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
{
const char *key = svn__apr_hash_index_key(hi);
svn_mergeinfo_t value = svn__apr_hash_index_val(hi);
svn_string_t *mergeinfo_string;
svn_pool_clear(iterpool);
SVN_ERR(svn_mergeinfo_to_string(&mergeinfo_string, value, iterpool));
SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "cs", key,
mergeinfo_string));
}
svn_pool_destroy(iterpool);
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))"));
return SVN_NO_ERROR;
}
/* Send a log entry to the client. */
static svn_error_t *log_receiver(void *baton,
svn_log_entry_t *log_entry,
apr_pool_t *pool)
{
log_baton_t *b = baton;
svn_ra_svn_conn_t *conn = b->conn;
apr_hash_index_t *h;
svn_boolean_t invalid_revnum = FALSE;
char action[2];
const char *author, *date, *message;
apr_uint64_t revprop_count;
if (log_entry->revision == SVN_INVALID_REVNUM)
{
/* If the stack depth is zero, we've seen the last revision, so don't
send it, just return. */
if (b->stack_depth == 0)
return SVN_NO_ERROR;
/* Because the svn protocol won't let us send an invalid revnum, we have
to fudge here and send an additional flag. */
log_entry->revision = 0;
invalid_revnum = TRUE;
b->stack_depth--;
}
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "(!"));
if (log_entry->changed_paths2)
{
for (h = apr_hash_first(pool, log_entry->changed_paths2); h;
h = apr_hash_next(h))
{
const char *path = svn__apr_hash_index_key(h);
svn_log_changed_path2_t *change = svn__apr_hash_index_val(h);
action[0] = change->action;
action[1] = '\0';
SVN_ERR(svn_ra_svn__write_tuple(
conn, pool, "cw(?cr)(cbb)",
path,
action,
change->copyfrom_path,
change->copyfrom_rev,
svn_node_kind_to_word(change->node_kind),
/* text_modified and props_modified are never unknown */
change->text_modified == svn_tristate_true,
change->props_modified == svn_tristate_true));
}
}
svn_compat_log_revprops_out(&author, &date, &message, log_entry->revprops);
svn_compat_log_revprops_clear(log_entry->revprops);
if (log_entry->revprops)
revprop_count = apr_hash_count(log_entry->revprops);
else
revprop_count = 0;
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)r(?c)(?c)(?c)bbn(!",
log_entry->revision,
author, date, message,
log_entry->has_children,
invalid_revnum, revprop_count));
SVN_ERR(svn_ra_svn__write_proplist(conn, pool, log_entry->revprops));
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)b",
log_entry->subtractive_merge));
if (log_entry->has_children)
b->stack_depth++;
return SVN_NO_ERROR;
}
static svn_error_t *log_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
svn_error_t *err, *write_err;
server_baton_t *b = baton;
svn_revnum_t start_rev, end_rev;
const char *full_path;
svn_boolean_t send_changed_paths, strict_node, include_merged_revisions;
apr_array_header_t *paths, *full_paths, *revprop_items, *revprops;
char *revprop_word;
svn_ra_svn_item_t *elt;
int i;
apr_uint64_t limit, include_merged_revs_param;
log_baton_t lb;
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "l(?r)(?r)bb?n?Bwl", &paths,
&start_rev, &end_rev, &send_changed_paths,
&strict_node, &limit,
&include_merged_revs_param,
&revprop_word, &revprop_items));
if (include_merged_revs_param == SVN_RA_SVN_UNSPECIFIED_NUMBER)
include_merged_revisions = FALSE;
else
include_merged_revisions = (svn_boolean_t) include_merged_revs_param;
if (revprop_word == NULL)
/* pre-1.5 client */
revprops = svn_compat_log_revprops_in(pool);
else if (strcmp(revprop_word, "all-revprops") == 0)
revprops = NULL;
else if (strcmp(revprop_word, "revprops") == 0)
{
SVN_ERR_ASSERT(revprop_items);
revprops = apr_array_make(pool, revprop_items->nelts,
sizeof(char *));
for (i = 0; i < revprop_items->nelts; i++)
{
elt = &APR_ARRAY_IDX(revprop_items, i, svn_ra_svn_item_t);
if (elt->kind != SVN_RA_SVN_STRING)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
_("Log revprop entry not a string"));
APR_ARRAY_PUSH(revprops, const char *) = elt->u.string->data;
}
}
else
return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
_("Unknown revprop word '%s' in log command"),
revprop_word);
/* If we got an unspecified number then the user didn't send us anything,
so we assume no limit. If it's larger than INT_MAX then someone is
messing with us, since we know the svn client libraries will never send
us anything that big, so play it safe and default to no limit. */
if (limit == SVN_RA_SVN_UNSPECIFIED_NUMBER || limit > INT_MAX)
limit = 0;
full_paths = apr_array_make(pool, paths->nelts, sizeof(const char *));
for (i = 0; i < paths->nelts; i++)
{
elt = &APR_ARRAY_IDX(paths, i, svn_ra_svn_item_t);
if (elt->kind != SVN_RA_SVN_STRING)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
_("Log path entry not a string"));
full_path = svn_relpath_canonicalize(elt->u.string->data, pool),
full_path = svn_fspath__join(b->fs_path->data, full_path, pool);
APR_ARRAY_PUSH(full_paths, const char *) = full_path;
}
SVN_ERR(trivial_auth_request(conn, pool, b));
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__log(full_paths, start_rev, end_rev,
(int) limit, send_changed_paths,
strict_node, include_merged_revisions,
revprops, pool)));
/* Get logs. (Can't report errors back to the client at this point.) */
lb.fs_path = b->fs_path->data;
lb.conn = conn;
lb.stack_depth = 0;
err = svn_repos_get_logs4(b->repos, full_paths, start_rev, end_rev,
(int) limit, send_changed_paths, strict_node,
include_merged_revisions, revprops,
authz_check_access_cb_func(b), &ab, log_receiver,
&lb, pool);
write_err = svn_ra_svn__write_word(conn, pool, "done");
if (write_err)
{
svn_error_clear(err);
return write_err;
}
SVN_CMD_ERR(err);
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
return SVN_NO_ERROR;
}
static svn_error_t *check_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_revnum_t rev;
const char *path, *full_path;
svn_fs_root_t *root;
svn_node_kind_t kind;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)", &path, &rev));
full_path = svn_fspath__join(b->fs_path->data,
svn_relpath_canonicalize(path, pool), pool);
/* Check authorizations */
SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
full_path, FALSE));
if (!SVN_IS_VALID_REVNUM(rev))
SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
SVN_ERR(log_command(b, conn, pool, "check-path %s@%d",
svn_path_uri_encode(full_path, pool), rev));
SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, pool));
SVN_CMD_ERR(svn_fs_check_path(&kind, root, full_path, pool));
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "w",
svn_node_kind_to_word(kind)));
return SVN_NO_ERROR;
}
static svn_error_t *stat_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_revnum_t rev;
const char *path, *full_path, *cdate;
svn_fs_root_t *root;
svn_dirent_t *dirent;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)", &path, &rev));
full_path = svn_fspath__join(b->fs_path->data,
svn_relpath_canonicalize(path, pool), pool);
/* Check authorizations */
SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
full_path, FALSE));
if (!SVN_IS_VALID_REVNUM(rev))
SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
SVN_ERR(log_command(b, conn, pool, "stat %s@%d",
svn_path_uri_encode(full_path, pool), rev));
SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, pool));
SVN_CMD_ERR(svn_repos_stat(&dirent, root, full_path, pool));
/* Need to return the equivalent of "(?l)", since that's what the
client is reading. */
if (dirent == NULL)
{
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "()"));
return SVN_NO_ERROR;
}
cdate = (dirent->time == (time_t) -1) ? NULL
: svn_time_to_cstring(dirent->time, pool);
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "((wnbr(?c)(?c)))",
svn_node_kind_to_word(dirent->kind),
(apr_uint64_t) dirent->size,
dirent->has_props, dirent->created_rev,
cdate, dirent->last_author));
return SVN_NO_ERROR;
}
static svn_error_t *get_locations(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
svn_error_t *err, *write_err;
server_baton_t *b = baton;
svn_revnum_t revision;
apr_array_header_t *location_revisions, *loc_revs_proto;
svn_ra_svn_item_t *elt;
int i;
const char *relative_path;
svn_revnum_t peg_revision;
apr_hash_t *fs_locations;
const char *abs_path;
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
/* Parse the arguments. */
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "crl", &relative_path,
&peg_revision,
&loc_revs_proto));
relative_path = svn_relpath_canonicalize(relative_path, pool);
abs_path = svn_fspath__join(b->fs_path->data, relative_path, pool);
location_revisions = apr_array_make(pool, loc_revs_proto->nelts,
sizeof(svn_revnum_t));
for (i = 0; i < loc_revs_proto->nelts; i++)
{
elt = &APR_ARRAY_IDX(loc_revs_proto, i, svn_ra_svn_item_t);
if (elt->kind != SVN_RA_SVN_NUMBER)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
"Get-locations location revisions entry "
"not a revision number");
revision = (svn_revnum_t)(elt->u.number);
APR_ARRAY_PUSH(location_revisions, svn_revnum_t) = revision;
}
SVN_ERR(trivial_auth_request(conn, pool, b));
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__get_locations(abs_path, peg_revision,
location_revisions, pool)));
/* All the parameters are fine - let's perform the query against the
* repository. */
/* We store both err and write_err here, so the client will get
* the "done" even if there was an error in fetching the results. */
err = svn_repos_trace_node_locations(b->fs, &fs_locations, abs_path,
peg_revision, location_revisions,
authz_check_access_cb_func(b), &ab,
pool);
/* Now, write the results to the connection. */
if (!err)
{
if (fs_locations)
{
apr_hash_index_t *iter;
for (iter = apr_hash_first(pool, fs_locations); iter;
iter = apr_hash_next(iter))
{
const svn_revnum_t *iter_key = svn__apr_hash_index_key(iter);
const char *iter_value = svn__apr_hash_index_val(iter);
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "rc",
*iter_key, iter_value));
}
}
}
write_err = svn_ra_svn__write_word(conn, pool, "done");
if (write_err)
{
svn_error_clear(err);
return write_err;
}
SVN_CMD_ERR(err);
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
return SVN_NO_ERROR;
}
static svn_error_t *gls_receiver(svn_location_segment_t *segment,
void *baton,
apr_pool_t *pool)
{
svn_ra_svn_conn_t *conn = baton;
return svn_ra_svn__write_tuple(conn, pool, "rr(?c)",
segment->range_start,
segment->range_end,
segment->path);
}
static svn_error_t *get_location_segments(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
apr_array_header_t *params,
void *baton)
{
svn_error_t *err, *write_err;
server_baton_t *b = baton;
svn_revnum_t peg_revision, start_rev, end_rev;
const char *relative_path;
const char *abs_path;
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
/* Parse the arguments. */
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)(?r)(?r)",
&relative_path, &peg_revision,
&start_rev, &end_rev));
relative_path = svn_relpath_canonicalize(relative_path, pool);
abs_path = svn_fspath__join(b->fs_path->data, relative_path, pool);
- if (SVN_IS_VALID_REVNUM(start_rev)
- && SVN_IS_VALID_REVNUM(end_rev)
- && (end_rev > start_rev))
+ SVN_ERR(trivial_auth_request(conn, pool, b));
+ SVN_ERR(log_command(baton, conn, pool, "%s",
+ svn_log__get_location_segments(abs_path, peg_revision,
+ start_rev, end_rev,
+ pool)));
+
+ /* No START_REV or PEG_REVISION? We'll use HEAD. */
+ if (!SVN_IS_VALID_REVNUM(start_rev) || !SVN_IS_VALID_REVNUM(peg_revision))
{
+ svn_revnum_t youngest;
+
+ SVN_CMD_ERR(svn_fs_youngest_rev(&youngest, b->fs, pool));
+
+ if (!SVN_IS_VALID_REVNUM(start_rev))
+ start_rev = youngest;
+ if (!SVN_IS_VALID_REVNUM(peg_revision))
+ peg_revision = youngest;
+ }
+
+ /* No END_REV? We'll use 0. */
+ if (!SVN_IS_VALID_REVNUM(end_rev))
+ end_rev = 0;
+
+ if (end_rev > start_rev)
+ {
err = svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
"Get-location-segments end revision must not be "
"younger than start revision");
return log_fail_and_flush(err, b, conn, pool);
}
- if (SVN_IS_VALID_REVNUM(peg_revision)
- && SVN_IS_VALID_REVNUM(start_rev)
- && (start_rev > peg_revision))
+ if (start_rev > peg_revision)
{
err = svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
"Get-location-segments start revision must not "
"be younger than peg revision");
return log_fail_and_flush(err, b, conn, pool);
}
-
- SVN_ERR(trivial_auth_request(conn, pool, b));
- SVN_ERR(log_command(baton, conn, pool, "%s",
- svn_log__get_location_segments(abs_path, peg_revision,
- start_rev, end_rev,
- pool)));
/* All the parameters are fine - let's perform the query against the
* repository. */
/* We store both err and write_err here, so the client will get
* the "done" even if there was an error in fetching the results. */
err = svn_repos_node_location_segments(b->repos, abs_path,
peg_revision, start_rev, end_rev,
gls_receiver, (void *)conn,
authz_check_access_cb_func(b), &ab,
pool);
write_err = svn_ra_svn__write_word(conn, pool, "done");
if (write_err)
{
svn_error_clear(err);
return write_err;
}
SVN_CMD_ERR(err);
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
return SVN_NO_ERROR;
}
/* This implements svn_write_fn_t. Write LEN bytes starting at DATA to the
client as a string. */
static svn_error_t *svndiff_handler(void *baton, const char *data,
apr_size_t *len)
{
file_revs_baton_t *b = baton;
svn_string_t str;
str.data = data;
str.len = *len;
return svn_ra_svn__write_string(b->conn, b->pool, &str);
}
/* This implements svn_close_fn_t. Mark the end of the data by writing an
empty string to the client. */
static svn_error_t *svndiff_close_handler(void *baton)
{
file_revs_baton_t *b = baton;
SVN_ERR(svn_ra_svn__write_cstring(b->conn, b->pool, ""));
return SVN_NO_ERROR;
}
/* This implements the svn_repos_file_rev_handler_t interface. */
static svn_error_t *file_rev_handler(void *baton, const char *path,
svn_revnum_t rev, apr_hash_t *rev_props,
svn_boolean_t merged_revision,
svn_txdelta_window_handler_t *d_handler,
void **d_baton,
apr_array_header_t *prop_diffs,
apr_pool_t *pool)
{
file_revs_baton_t *frb = baton;
svn_stream_t *stream;
SVN_ERR(svn_ra_svn__write_tuple(frb->conn, pool, "cr(!",
path, rev));
SVN_ERR(svn_ra_svn__write_proplist(frb->conn, pool, rev_props));
SVN_ERR(svn_ra_svn__write_tuple(frb->conn, pool, "!)(!"));
SVN_ERR(write_prop_diffs(frb->conn, pool, prop_diffs));
SVN_ERR(svn_ra_svn__write_tuple(frb->conn, pool, "!)b", merged_revision));
/* Store the pool for the delta stream. */
frb->pool = pool;
/* Prepare for the delta or just write an empty string. */
if (d_handler)
{
stream = svn_stream_create(baton, pool);
svn_stream_set_write(stream, svndiff_handler);
svn_stream_set_close(stream, svndiff_close_handler);
/* If the connection does not support SVNDIFF1 or if we don't want to use
* compression, use the non-compressing "version 0" implementation */
if ( svn_ra_svn_compression_level(frb->conn) > 0
&& svn_ra_svn_has_capability(frb->conn, SVN_RA_SVN_CAP_SVNDIFF1))
svn_txdelta_to_svndiff3(d_handler, d_baton, stream, 1,
svn_ra_svn_compression_level(frb->conn), pool);
else
svn_txdelta_to_svndiff3(d_handler, d_baton, stream, 0,
svn_ra_svn_compression_level(frb->conn), pool);
}
else
SVN_ERR(svn_ra_svn__write_cstring(frb->conn, pool, ""));
return SVN_NO_ERROR;
}
static svn_error_t *get_file_revs(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_error_t *err, *write_err;
file_revs_baton_t frb;
svn_revnum_t start_rev, end_rev;
const char *path;
const char *full_path;
apr_uint64_t include_merged_revs_param;
svn_boolean_t include_merged_revisions;
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
/* Parse arguments. */
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)(?r)?B",
&path, &start_rev, &end_rev,
&include_merged_revs_param));
path = svn_relpath_canonicalize(path, pool);
SVN_ERR(trivial_auth_request(conn, pool, b));
full_path = svn_fspath__join(b->fs_path->data, path, pool);
if (include_merged_revs_param == SVN_RA_SVN_UNSPECIFIED_NUMBER)
include_merged_revisions = FALSE;
else
include_merged_revisions = (svn_boolean_t) include_merged_revs_param;
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__get_file_revs(full_path, start_rev, end_rev,
include_merged_revisions,
pool)));
frb.conn = conn;
frb.pool = NULL;
err = svn_repos_get_file_revs2(b->repos, full_path, start_rev, end_rev,
include_merged_revisions,
authz_check_access_cb_func(b), &ab,
file_rev_handler, &frb, pool);
write_err = svn_ra_svn__write_word(conn, pool, "done");
if (write_err)
{
svn_error_clear(err);
return write_err;
}
SVN_CMD_ERR(err);
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
return SVN_NO_ERROR;
}
static svn_error_t *lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
const char *path;
const char *comment;
const char *full_path;
svn_boolean_t steal_lock;
svn_revnum_t current_rev;
svn_lock_t *l;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?c)b(?r)", &path, &comment,
&steal_lock, &current_rev));
full_path = svn_fspath__join(b->fs_path->data,
svn_relpath_canonicalize(path, pool), pool);
SVN_ERR(must_have_access(conn, pool, b, svn_authz_write,
full_path, TRUE));
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__lock_one_path(full_path, steal_lock, pool)));
SVN_CMD_ERR(svn_repos_fs_lock(&l, b->repos, full_path, NULL, comment, 0,
0, /* No expiration time. */
current_rev, steal_lock, pool));
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(!", "success"));
SVN_ERR(write_lock(conn, pool, l));
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)"));
return SVN_NO_ERROR;
}
static svn_error_t *lock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
apr_array_header_t *path_revs;
const char *comment;
svn_boolean_t steal_lock;
int i;
apr_pool_t *subpool;
const char *path;
const char *full_path;
svn_revnum_t current_rev;
apr_array_header_t *log_paths;
svn_lock_t *l;
svn_error_t *err = SVN_NO_ERROR, *write_err;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?c)bl", &comment, &steal_lock,
&path_revs));
subpool = svn_pool_create(pool);
/* Because we can only send a single auth reply per request, we send
a reply before parsing the lock commands. This means an authz
access denial will abort the processing of the locks and return
an error. */
SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, TRUE));
/* Loop through the lock requests. */
log_paths = apr_array_make(pool, path_revs->nelts, sizeof(full_path));
for (i = 0; i < path_revs->nelts; ++i)
{
svn_ra_svn_item_t *item = &APR_ARRAY_IDX(path_revs, i,
svn_ra_svn_item_t);
svn_pool_clear(subpool);
if (item->kind != SVN_RA_SVN_LIST)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
"Lock requests should be list of lists");
SVN_ERR(svn_ra_svn__parse_tuple(item->u.list, pool, "c(?r)", &path,
&current_rev));
/* Allocate the full_path out of pool so it will survive for use
* by operational logging, after this loop. */
full_path = svn_fspath__join(b->fs_path->data,
svn_relpath_canonicalize(path, subpool),
pool);
APR_ARRAY_PUSH(log_paths, const char *) = full_path;
if (! lookup_access(pool, b, conn, svn_authz_write, full_path, TRUE))
{
err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL, NULL,
b, conn, pool);
break;
}
err = svn_repos_fs_lock(&l, b->repos, full_path,
NULL, comment, FALSE,
0, /* No expiration time. */
current_rev,
steal_lock, subpool);
if (err)
{
if (SVN_ERR_IS_LOCK_ERROR(err))
{
write_err = svn_ra_svn__write_cmd_failure(conn, pool, err);
svn_error_clear(err);
err = NULL;
SVN_ERR(write_err);
}
else
break;
}
else
{
SVN_ERR(svn_ra_svn__write_tuple(conn, subpool, "w!", "success"));
SVN_ERR(write_lock(conn, subpool, l));
SVN_ERR(svn_ra_svn__write_tuple(conn, subpool, "!"));
}
}
svn_pool_destroy(subpool);
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__lock(log_paths, steal_lock, pool)));
/* NOTE: err might contain a fatal locking error from the loop above. */
write_err = svn_ra_svn__write_word(conn, pool, "done");
if (!write_err)
SVN_CMD_ERR(err);
svn_error_clear(err);
SVN_ERR(write_err);
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
return SVN_NO_ERROR;
}
static svn_error_t *unlock(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
const char *path, *token, *full_path;
svn_boolean_t break_lock;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?c)b", &path, &token,
&break_lock));
full_path = svn_fspath__join(b->fs_path->data,
svn_relpath_canonicalize(path, pool), pool);
/* Username required unless break_lock was specified. */
SVN_ERR(must_have_access(conn, pool, b, svn_authz_write,
full_path, ! break_lock));
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__unlock_one_path(full_path, break_lock, pool)));
SVN_CMD_ERR(svn_repos_fs_unlock(b->repos, full_path, token, break_lock,
pool));
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
return SVN_NO_ERROR;
}
static svn_error_t *unlock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_boolean_t break_lock;
apr_array_header_t *unlock_tokens;
int i;
apr_pool_t *subpool;
const char *path;
const char *full_path;
apr_array_header_t *log_paths;
const char *token;
svn_error_t *err = SVN_NO_ERROR, *write_err;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "bl", &break_lock,
&unlock_tokens));
/* Username required unless break_lock was specified. */
SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, ! break_lock));
subpool = svn_pool_create(pool);
/* Loop through the unlock requests. */
log_paths = apr_array_make(pool, unlock_tokens->nelts, sizeof(full_path));
for (i = 0; i < unlock_tokens->nelts; i++)
{
svn_ra_svn_item_t *item = &APR_ARRAY_IDX(unlock_tokens, i,
svn_ra_svn_item_t);
svn_pool_clear(subpool);
if (item->kind != SVN_RA_SVN_LIST)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
"Unlock request should be a list of lists");
SVN_ERR(svn_ra_svn__parse_tuple(item->u.list, subpool, "c(?c)", &path,
&token));
/* Allocate the full_path out of pool so it will survive for use
* by operational logging, after this loop. */
full_path = svn_fspath__join(b->fs_path->data,
svn_relpath_canonicalize(path, subpool),
pool);
APR_ARRAY_PUSH(log_paths, const char *) = full_path;
if (! lookup_access(subpool, b, conn, svn_authz_write, full_path,
! break_lock))
return svn_error_create(SVN_ERR_RA_SVN_CMD_ERR,
error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED,
NULL, NULL,
b, conn, pool),
NULL);
err = svn_repos_fs_unlock(b->repos, full_path, token, break_lock,
subpool);
if (err)
{
if (SVN_ERR_IS_UNLOCK_ERROR(err))
{
write_err = svn_ra_svn__write_cmd_failure(conn, pool, err);
svn_error_clear(err);
err = NULL;
SVN_ERR(write_err);
}
else
break;
}
else
SVN_ERR(svn_ra_svn__write_tuple(conn, subpool, "w(c)", "success",
path));
}
svn_pool_destroy(subpool);
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__unlock(log_paths, break_lock, pool)));
/* NOTE: err might contain a fatal unlocking error from the loop above. */
write_err = svn_ra_svn__write_word(conn, pool, "done");
if (! write_err)
SVN_CMD_ERR(err);
svn_error_clear(err);
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
return SVN_NO_ERROR;
}
static svn_error_t *get_lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
const char *path;
const char *full_path;
svn_lock_t *l;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &path));
full_path = svn_fspath__join(b->fs_path->data,
svn_relpath_canonicalize(path, pool), pool);
SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
full_path, FALSE));
SVN_ERR(log_command(b, conn, pool, "get-lock %s",
svn_path_uri_encode(full_path, pool)));
SVN_CMD_ERR(svn_fs_get_lock(&l, b->fs, full_path, pool));
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success"));
if (l)
SVN_ERR(write_lock(conn, pool, l));
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))"));
return SVN_NO_ERROR;
}
static svn_error_t *get_locks(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
const char *path;
const char *full_path;
const char *depth_word;
svn_depth_t depth;
apr_hash_t *locks;
apr_hash_index_t *hi;
svn_error_t *err;
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c?(?w)", &path, &depth_word));
depth = depth_word ? svn_depth_from_word(depth_word) : svn_depth_infinity;
if ((depth != svn_depth_empty) &&
(depth != svn_depth_files) &&
(depth != svn_depth_immediates) &&
(depth != svn_depth_infinity))
{
err = svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
"Invalid 'depth' specified in get-locks request");
return log_fail_and_flush(err, b, conn, pool);
}
full_path = svn_fspath__join(b->fs_path->data,
svn_relpath_canonicalize(path, pool), pool);
SVN_ERR(trivial_auth_request(conn, pool, b));
SVN_ERR(log_command(b, conn, pool, "get-locks %s",
svn_path_uri_encode(full_path, pool)));
SVN_CMD_ERR(svn_repos_fs_get_locks2(&locks, b->repos, full_path, depth,
authz_check_access_cb_func(b), &ab,
pool));
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success"));
for (hi = apr_hash_first(pool, locks); hi; hi = apr_hash_next(hi))
{
svn_lock_t *l = svn__apr_hash_index_val(hi);
SVN_ERR(write_lock(conn, pool, l));
}
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))"));
return SVN_NO_ERROR;
}
static svn_error_t *replay_one_revision(svn_ra_svn_conn_t *conn,
server_baton_t *b,
svn_revnum_t rev,
svn_revnum_t low_water_mark,
svn_boolean_t send_deltas,
apr_pool_t *pool)
{
const svn_delta_editor_t *editor;
void *edit_baton;
svn_fs_root_t *root;
svn_error_t *err;
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
SVN_ERR(log_command(b, conn, pool,
svn_log__replay(b->fs_path->data, rev, pool)));
svn_ra_svn_get_editor(&editor, &edit_baton, conn, pool, NULL, NULL);
err = svn_fs_revision_root(&root, b->fs, rev, pool);
if (! err)
err = svn_repos_replay2(root, b->fs_path->data, low_water_mark,
send_deltas, editor, edit_baton,
authz_check_access_cb_func(b), &ab, pool);
if (err)
svn_error_clear(editor->abort_edit(edit_baton, pool));
SVN_CMD_ERR(err);
return svn_ra_svn__write_cmd_finish_replay(conn, pool);
}
static svn_error_t *replay(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
svn_revnum_t rev, low_water_mark;
svn_boolean_t send_deltas;
server_baton_t *b = baton;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rrb", &rev, &low_water_mark,
&send_deltas));
SVN_ERR(trivial_auth_request(conn, pool, b));
SVN_ERR(replay_one_revision(conn, b, rev, low_water_mark,
send_deltas, pool));
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
return SVN_NO_ERROR;
}
static svn_error_t *replay_range(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
svn_revnum_t start_rev, end_rev, rev, low_water_mark;
svn_boolean_t send_deltas;
server_baton_t *b = baton;
apr_pool_t *iterpool;
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rrrb", &start_rev,
&end_rev, &low_water_mark,
&send_deltas));
SVN_ERR(trivial_auth_request(conn, pool, b));
iterpool = svn_pool_create(pool);
for (rev = start_rev; rev <= end_rev; rev++)
{
apr_hash_t *props;
svn_pool_clear(iterpool);
SVN_CMD_ERR(svn_repos_fs_revision_proplist(&props, b->repos, rev,
authz_check_access_cb_func(b),
&ab,
iterpool));
SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "w(!", "revprops"));
SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, props));
SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!)"));
SVN_ERR(replay_one_revision(conn, b, rev, low_water_mark,
send_deltas, iterpool));
}
svn_pool_destroy(iterpool);
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
return SVN_NO_ERROR;
}
static svn_error_t *
get_deleted_rev(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
apr_array_header_t *params,
void *baton)
{
server_baton_t *b = baton;
const char *path, *full_path;
svn_revnum_t peg_revision;
svn_revnum_t end_revision;
svn_revnum_t revision_deleted;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "crr",
&path, &peg_revision, &end_revision));
full_path = svn_fspath__join(b->fs_path->data,
svn_relpath_canonicalize(path, pool), pool);
SVN_ERR(log_command(b, conn, pool, "get-deleted-rev"));
SVN_ERR(trivial_auth_request(conn, pool, b));
SVN_ERR(svn_repos_deleted_rev(b->fs, full_path, peg_revision, end_revision,
&revision_deleted, pool));
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "r", revision_deleted));
return SVN_NO_ERROR;
}
static svn_error_t *
get_inherited_props(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
apr_array_header_t *params,
void *baton)
{
server_baton_t *b = baton;
const char *path, *full_path;
svn_revnum_t rev;
svn_fs_root_t *root;
apr_array_header_t *inherited_props;
int i;
apr_pool_t *iterpool = svn_pool_create(pool);
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
/* Parse arguments. */
SVN_ERR(svn_ra_svn__parse_tuple(params, iterpool, "c(?r)", &path, &rev));
full_path = svn_fspath__join(b->fs_path->data,
svn_relpath_canonicalize(path, iterpool),
pool);
/* Check authorizations */
SVN_ERR(must_have_access(conn, iterpool, b, svn_authz_read,
full_path, FALSE));
if (!SVN_IS_VALID_REVNUM(rev))
SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__get_inherited_props(full_path, rev,
iterpool)));
/* Fetch the properties and a stream for the contents. */
SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, iterpool));
SVN_CMD_ERR(get_props(NULL, &inherited_props, &ab, root, full_path, pool));
/* Send successful command response with revision and props. */
SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "w(!", "success"));
SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(?!"));
for (i = 0; i < inherited_props->nelts; i++)
{
svn_prop_inherited_item_t *iprop =
APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *);
svn_pool_clear(iterpool);
SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(c(!",
iprop->path_or_url));
SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, iprop->prop_hash));
SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))!",
iprop->path_or_url));
}
SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))"));
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static const svn_ra_svn_cmd_entry_t main_commands[] = {
{ "reparent", reparent },
{ "get-latest-rev", get_latest_rev },
{ "get-dated-rev", get_dated_rev },
{ "change-rev-prop", change_rev_prop },
{ "change-rev-prop2",change_rev_prop2 },
{ "rev-proplist", rev_proplist },
{ "rev-prop", rev_prop },
{ "commit", commit },
{ "get-file", get_file },
{ "get-dir", get_dir },
{ "update", update },
{ "switch", switch_cmd },
{ "status", status },
{ "diff", diff },
{ "get-mergeinfo", get_mergeinfo },
{ "log", log_cmd },
{ "check-path", check_path },
{ "stat", stat_cmd },
{ "get-locations", get_locations },
{ "get-location-segments", get_location_segments },
{ "get-file-revs", get_file_revs },
{ "lock", lock },
{ "lock-many", lock_many },
{ "unlock", unlock },
{ "unlock-many", unlock_many },
{ "get-lock", get_lock },
{ "get-locks", get_locks },
{ "replay", replay },
{ "replay-range", replay_range },
{ "get-deleted-rev", get_deleted_rev },
{ "get-iprops", get_inherited_props },
{ NULL }
};
/* Skip past the scheme part of a URL, including the tunnel specification
* if present. Return NULL if the scheme part is invalid for ra_svn. */
static const char *skip_scheme_part(const char *url)
{
if (strncmp(url, "svn", 3) != 0)
return NULL;
url += 3;
if (*url == '+')
url += strcspn(url, ":");
if (strncmp(url, "://", 3) != 0)
return NULL;
return url + 3;
}
/* Check that PATH is a valid repository path, meaning it doesn't contain any
'..' path segments.
NOTE: This is similar to svn_path_is_backpath_present, but that function
assumes the path separator is '/'. This function also checks for
segments delimited by the local path separator. */
static svn_boolean_t
repos_path_valid(const char *path)
{
const char *s = path;
while (*s)
{
/* Scan for the end of the segment. */
while (*path && *path != '/' && *path != SVN_PATH_LOCAL_SEPARATOR)
++path;
/* Check for '..'. */
#ifdef WIN32
/* On Windows, don't allow sequences of more than one character
consisting of just dots and spaces. Win32 functions treat
paths such as ".. " and "......." inconsistently. Make sure
no one can escape out of the root. */
if (path - s >= 2 && strspn(s, ". ") == (size_t)(path - s))
return FALSE;
#else /* ! WIN32 */
if (path - s == 2 && s[0] == '.' && s[1] == '.')
return FALSE;
#endif
/* Skip all separators. */
while (*path && (*path == '/' || *path == SVN_PATH_LOCAL_SEPARATOR))
++path;
s = path;
}
return TRUE;
}
/* Look for the repository given by URL, using ROOT as the virtual
* repository root. If we find one, fill in the repos, fs, cfg,
* repos_url, and fs_path fields of B. Set B->repos's client
* capabilities to CAPABILITIES, which must be at least as long-lived
* as POOL, and whose elements are SVN_RA_CAPABILITY_*.
*/
static svn_error_t *find_repos(const char *url, const char *root,
server_baton_t *b,
svn_ra_svn_conn_t *conn,
const apr_array_header_t *capabilities,
apr_pool_t *pool)
{
const char *path, *full_path, *repos_root, *fs_path, *hooks_env;
svn_stringbuf_t *url_buf;
/* Skip past the scheme and authority part. */
path = skip_scheme_part(url);
if (path == NULL)
return svn_error_createf(SVN_ERR_BAD_URL, NULL,
"Non-svn URL passed to svn server: '%s'", url);
if (! b->vhost)
{
path = strchr(path, '/');
if (path == NULL)
path = "";
}
path = svn_relpath_canonicalize(path, pool);
path = svn_path_uri_decode(path, pool);
/* Ensure that it isn't possible to escape the root by disallowing
'..' segments. */
if (!repos_path_valid(path))
return svn_error_create(SVN_ERR_BAD_FILENAME, NULL,
"Couldn't determine repository path");
/* Join the server-configured root with the client path. */
full_path = svn_dirent_join(svn_dirent_canonicalize(root, pool),
path, pool);
/* Search for a repository in the full path. */
repos_root = svn_repos_find_root_path(full_path, pool);
if (!repos_root)
return svn_error_createf(SVN_ERR_RA_SVN_REPOS_NOT_FOUND, NULL,
"No repository found in '%s'", url);
/* Open the repository and fill in b with the resulting information. */
SVN_ERR(svn_repos_open2(&b->repos, repos_root, b->fs_config, pool));
SVN_ERR(svn_repos_remember_client_capabilities(b->repos, capabilities));
b->fs = svn_repos_fs(b->repos);
fs_path = full_path + strlen(repos_root);
b->fs_path = svn_stringbuf_create(*fs_path ? fs_path : "/", pool);
url_buf = svn_stringbuf_create(url, pool);
svn_path_remove_components(url_buf,
svn_path_component_count(b->fs_path->data));
b->repos_url = url_buf->data;
b->authz_repos_name = svn_dirent_is_child(root, repos_root, pool);
if (b->authz_repos_name == NULL)
b->repos_name = svn_dirent_basename(repos_root, pool);
else
b->repos_name = b->authz_repos_name;
b->repos_name = svn_path_uri_encode(b->repos_name, pool);
/* If the svnserve configuration has not been loaded then load it from the
* repository. */
if (NULL == b->cfg)
{
b->base = svn_repos_conf_dir(b->repos, pool);
SVN_ERR(svn_config_read3(&b->cfg, svn_repos_svnserve_conf(b->repos, pool),
FALSE, /* must_exist */
FALSE, /* section_names_case_sensitive */
FALSE, /* option_names_case_sensitive */
pool));
SVN_ERR(load_pwdb_config(b, conn, pool));
SVN_ERR(load_authz_config(b, conn, repos_root, pool));
}
/* svnserve.conf has been loaded via the --config-file option so need
* to load pwdb and authz. */
else
{
SVN_ERR(load_pwdb_config(b, conn, pool));
SVN_ERR(load_authz_config(b, conn, repos_root, pool));
}
#ifdef SVN_HAVE_SASL
/* Should we use Cyrus SASL? */
SVN_ERR(svn_config_get_bool(b->cfg, &b->use_sasl, SVN_CONFIG_SECTION_SASL,
SVN_CONFIG_OPTION_USE_SASL, FALSE));
#endif
/* Use the repository UUID as the default realm. */
SVN_ERR(svn_fs_get_uuid(b->fs, &b->realm, pool));
svn_config_get(b->cfg, &b->realm, SVN_CONFIG_SECTION_GENERAL,
SVN_CONFIG_OPTION_REALM, b->realm);
/* Make sure it's possible for the client to authenticate. Note
that this doesn't take into account any authz configuration read
above, because we can't know about access it grants until paths
are given by the client. */
if (get_access(b, UNAUTHENTICATED) == NO_ACCESS
&& (get_access(b, AUTHENTICATED) == NO_ACCESS
|| (!b->tunnel_user && !b->pwdb && !b->use_sasl)))
return error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
"No access allowed to this repository",
b, conn, pool);
/* Configure hook script environment variables. */
svn_config_get(b->cfg, &hooks_env, SVN_CONFIG_SECTION_GENERAL,
SVN_CONFIG_OPTION_HOOKS_ENV, NULL);
if (hooks_env)
hooks_env = svn_dirent_internal_style(hooks_env, pool);
SVN_ERR(svn_repos_hooks_setenv(b->repos, hooks_env, pool));
return SVN_NO_ERROR;
}
/* Compute the authentication name EXTERNAL should be able to get, if any. */
static const char *get_tunnel_user(serve_params_t *params, apr_pool_t *pool)
{
/* Only offer EXTERNAL for connections tunneled over a login agent. */
if (!params->tunnel)
return NULL;
/* If a tunnel user was provided on the command line, use that. */
if (params->tunnel_user)
return params->tunnel_user;
return svn_user_get_name(pool);
}
static void
fs_warning_func(void *baton, svn_error_t *err)
{
fs_warning_baton_t *b = baton;
log_server_error(err, b->server, b->conn, b->pool);
/* TODO: Keep log_pool in the server baton, cleared after every log? */
svn_pool_clear(b->pool);
}
/* Return the normalized repository-relative path for the given PATH
* (may be a URL, full path or relative path) and fs contained in the
* server baton BATON. Allocate the result in POOL.
*/
static const char *
get_normalized_repo_rel_path(void *baton,
const char *path,
apr_pool_t *pool)
{
server_baton_t *sb = baton;
if (svn_path_is_url(path))
{
/* This is a copyfrom URL. */
path = svn_uri_skip_ancestor(sb->repos_url, path, pool);
path = svn_fspath__canonicalize(path, pool);
}
else
{
/* This is a base-relative path. */
if ((path)[0] != '/')
/* Get an absolute path for use in the FS. */
path = svn_fspath__join(sb->fs_path->data, path, pool);
}
return path;
}
/* Get the revision root for REVISION in fs given by server baton BATON
* and return it in *FS_ROOT. Use HEAD if REVISION is SVN_INVALID_REVNUM.
* Use POOL for allocations.
*/
static svn_error_t *
get_revision_root(svn_fs_root_t **fs_root,
void *baton,
svn_revnum_t revision,
apr_pool_t *pool)
{
server_baton_t *sb = baton;
if (!SVN_IS_VALID_REVNUM(revision))
SVN_ERR(svn_fs_youngest_rev(&revision, sb->fs, pool));
SVN_ERR(svn_fs_revision_root(fs_root, sb->fs, revision, pool));
return SVN_NO_ERROR;
}
static svn_error_t *
fetch_props_func(apr_hash_t **props,
void *baton,
const char *path,
svn_revnum_t base_revision,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_fs_root_t *fs_root;
svn_error_t *err;
path = get_normalized_repo_rel_path(baton, path, scratch_pool);
SVN_ERR(get_revision_root(&fs_root, baton, base_revision, scratch_pool));
err = svn_fs_node_proplist(props, fs_root, path, result_pool);
if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
{
svn_error_clear(err);
*props = apr_hash_make(result_pool);
return SVN_NO_ERROR;
}
else if (err)
return svn_error_trace(err);
return SVN_NO_ERROR;
}
static svn_error_t *
fetch_kind_func(svn_node_kind_t *kind,
void *baton,
const char *path,
svn_revnum_t base_revision,
apr_pool_t *scratch_pool)
{
svn_fs_root_t *fs_root;
path = get_normalized_repo_rel_path(baton, path, scratch_pool);
SVN_ERR(get_revision_root(&fs_root, baton, base_revision, scratch_pool));
SVN_ERR(svn_fs_check_path(kind, fs_root, path, scratch_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
fetch_base_func(const char **filename,
void *baton,
const char *path,
svn_revnum_t base_revision,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_stream_t *contents;
svn_stream_t *file_stream;
const char *tmp_filename;
svn_fs_root_t *fs_root;
svn_error_t *err;
path = get_normalized_repo_rel_path(baton, path, scratch_pool);
SVN_ERR(get_revision_root(&fs_root, baton, base_revision, scratch_pool));
err = svn_fs_file_contents(&contents, fs_root, path, scratch_pool);
if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
{
svn_error_clear(err);
*filename = NULL;
return SVN_NO_ERROR;
}
else if (err)
return svn_error_trace(err);
SVN_ERR(svn_stream_open_unique(&file_stream, &tmp_filename, NULL,
svn_io_file_del_on_pool_cleanup,
scratch_pool, scratch_pool));
SVN_ERR(svn_stream_copy3(contents, file_stream, NULL, NULL, scratch_pool));
*filename = apr_pstrdup(result_pool, tmp_filename);
return SVN_NO_ERROR;
}
svn_error_t *serve(svn_ra_svn_conn_t *conn, serve_params_t *params,
apr_pool_t *pool)
{
svn_error_t *err, *io_err;
apr_uint64_t ver;
const char *uuid, *client_url, *ra_client_string, *client_string;
apr_array_header_t *caplist, *cap_words;
server_baton_t b;
fs_warning_baton_t warn_baton;
svn_stringbuf_t *cap_log = svn_stringbuf_create_empty(pool);
b.tunnel = params->tunnel;
b.tunnel_user = get_tunnel_user(params, pool);
b.read_only = params->read_only;
b.user = NULL;
b.username_case = params->username_case;
b.authz_user = NULL;
b.base = params->base;
b.cfg = params->cfg;
b.pwdb = NULL;
b.authzdb = NULL;
b.realm = NULL;
b.log_file = params->log_file;
b.pool = pool;
b.use_sasl = FALSE;
b.vhost = params->vhost;
/* construct FS configuration parameters */
b.fs_config = apr_hash_make(pool);
svn_hash_sets(b.fs_config, SVN_FS_CONFIG_FSFS_CACHE_DELTAS,
params->cache_txdeltas ? "1" :"0");
svn_hash_sets(b.fs_config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS,
params->cache_fulltexts ? "1" :"0");
svn_hash_sets(b.fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS,
params->cache_revprops ? "1" :"0");
/* Send greeting. We don't support version 1 any more, so we can
* send an empty mechlist. */
if (params->compression_level > 0)
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "nn()(wwwwwwwwwww)",
(apr_uint64_t) 2, (apr_uint64_t) 2,
SVN_RA_SVN_CAP_EDIT_PIPELINE,
SVN_RA_SVN_CAP_SVNDIFF1,
SVN_RA_SVN_CAP_ABSENT_ENTRIES,
SVN_RA_SVN_CAP_COMMIT_REVPROPS,
SVN_RA_SVN_CAP_DEPTH,
SVN_RA_SVN_CAP_LOG_REVPROPS,
SVN_RA_SVN_CAP_ATOMIC_REVPROPS,
SVN_RA_SVN_CAP_PARTIAL_REPLAY,
SVN_RA_SVN_CAP_INHERITED_PROPS,
SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS,
SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE
));
else
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "nn()(wwwwwwwwww)",
(apr_uint64_t) 2, (apr_uint64_t) 2,
SVN_RA_SVN_CAP_EDIT_PIPELINE,
SVN_RA_SVN_CAP_ABSENT_ENTRIES,
SVN_RA_SVN_CAP_COMMIT_REVPROPS,
SVN_RA_SVN_CAP_DEPTH,
SVN_RA_SVN_CAP_LOG_REVPROPS,
SVN_RA_SVN_CAP_ATOMIC_REVPROPS,
SVN_RA_SVN_CAP_PARTIAL_REPLAY,
SVN_RA_SVN_CAP_INHERITED_PROPS,
SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS,
SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE
));
/* Read client response, which we assume to be in version 2 format:
* version, capability list, and client URL; then we do an auth
* request. */
SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "nlc?c(?c)",
&ver, &caplist, &client_url,
&ra_client_string,
&client_string));
if (ver != 2)
return SVN_NO_ERROR;
client_url = svn_uri_canonicalize(client_url, pool);
SVN_ERR(svn_ra_svn_set_capabilities(conn, caplist));
/* All released versions of Subversion support edit-pipeline,
* so we do not accept connections from clients that do not. */
if (! svn_ra_svn_has_capability(conn, SVN_RA_SVN_CAP_EDIT_PIPELINE))
return SVN_NO_ERROR;
/* find_repos needs the capabilities as a list of words (eventually
they get handed to the start-commit hook). While we could add a
new interface to re-retrieve them from conn and convert the
result to a list, it's simpler to just convert caplist by hand
here, since we already have it and turning 'svn_ra_svn_item_t's
into 'const char *'s is pretty easy.
We only record capabilities we care about. The client may report
more (because it doesn't know what the server cares about). */
{
int i;
svn_ra_svn_item_t *item;
cap_words = apr_array_make(pool, 1, sizeof(const char *));
for (i = 0; i < caplist->nelts; i++)
{
item = &APR_ARRAY_IDX(caplist, i, svn_ra_svn_item_t);
/* ra_svn_set_capabilities() already type-checked for us */
if (strcmp(item->u.word, SVN_RA_SVN_CAP_MERGEINFO) == 0)
{
APR_ARRAY_PUSH(cap_words, const char *)
= SVN_RA_CAPABILITY_MERGEINFO;
}
/* Save for operational log. */
if (cap_log->len > 0)
svn_stringbuf_appendcstr(cap_log, " ");
svn_stringbuf_appendcstr(cap_log, item->u.word);
}
}
err = find_repos(client_url, params->root, &b, conn, cap_words, pool);
if (!err)
{
SVN_ERR(auth_request(conn, pool, &b, READ_ACCESS, FALSE));
if (current_access(&b) == NO_ACCESS)
err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
"Not authorized for access",
&b, conn, pool);
}
if (err)
{
log_error(err, b.log_file, svn_ra_svn_conn_remote_host(conn),
b.user, NULL, pool);
io_err = svn_ra_svn__write_cmd_failure(conn, pool, err);
svn_error_clear(err);
SVN_ERR(io_err);
return svn_ra_svn__flush(conn, pool);
}
/* Log the open. */
if (ra_client_string == NULL || ra_client_string[0] == '\0')
ra_client_string = "-";
else
ra_client_string = svn_path_uri_encode(ra_client_string, pool);
if (client_string == NULL || client_string[0] == '\0')
client_string = "-";
else
client_string = svn_path_uri_encode(client_string, pool);
SVN_ERR(log_command(&b, conn, pool,
"open %" APR_UINT64_T_FMT " cap=(%s) %s %s %s",
ver, cap_log->data,
svn_path_uri_encode(b.fs_path->data, pool),
ra_client_string, client_string));
warn_baton.server = &b;
warn_baton.conn = conn;
warn_baton.pool = svn_pool_create(pool);
svn_fs_set_warning_func(b.fs, fs_warning_func, &warn_baton);
SVN_ERR(svn_fs_get_uuid(b.fs, &uuid, pool));
/* We can't claim mergeinfo capability until we know whether the
repository supports mergeinfo (i.e., is not a 1.4 repository),
but we don't get the repository url from the client until after
we've already sent the initial list of server capabilities. So
we list repository capabilities here, in our first response after
the client has sent the url. */
{
svn_boolean_t supports_mergeinfo;
SVN_ERR(svn_repos_has_capability(b.repos, &supports_mergeinfo,
SVN_REPOS_CAPABILITY_MERGEINFO, pool));
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(cc(!",
"success", uuid, b.repos_url));
if (supports_mergeinfo)
SVN_ERR(svn_ra_svn__write_word(conn, pool, SVN_RA_SVN_CAP_MERGEINFO));
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))"));
}
/* Set up editor shims. */
{
svn_delta_shim_callbacks_t *callbacks =
svn_delta_shim_callbacks_default(pool);
callbacks->fetch_base_func = fetch_base_func;
callbacks->fetch_props_func = fetch_props_func;
callbacks->fetch_kind_func = fetch_kind_func;
callbacks->fetch_baton = &b;
SVN_ERR(svn_ra_svn__set_shim_callbacks(conn, callbacks));
}
return svn_ra_svn__handle_commands2(conn, pool, main_commands, &b, FALSE);
}
Index: vendor/subversion/dist/subversion/svnsync/sync.c
===================================================================
--- vendor/subversion/dist/subversion/svnsync/sync.c (revision 286500)
+++ vendor/subversion/dist/subversion/svnsync/sync.c (revision 286501)
@@ -1,643 +1,758 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include "svn_hash.h"
#include "svn_cmdline.h"
#include "svn_config.h"
#include "svn_pools.h"
#include "svn_delta.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_props.h"
#include "svn_auth.h"
#include "svn_opt.h"
#include "svn_ra.h"
#include "svn_utf.h"
#include "svn_subst.h"
#include "svn_string.h"
+#include "private/svn_string_private.h"
+
#include "sync.h"
#include "svn_private_config.h"
#include <apr_network_io.h>
#include <apr_signal.h>
#include <apr_uuid.h>
/* Normalize the encoding and line ending style of *STR, so that it contains
* only LF (\n) line endings and is encoded in UTF-8. After return, *STR may
* point at a new svn_string_t* allocated in RESULT_POOL.
*
* If SOURCE_PROP_ENCODING is NULL, then *STR is presumed to be encoded in
* UTF-8.
*
* *WAS_NORMALIZED is set to TRUE when *STR needed line ending normalization.
* Otherwise it is set to FALSE.
*
* SCRATCH_POOL is used for temporary allocations.
*/
static svn_error_t *
normalize_string(const svn_string_t **str,
svn_boolean_t *was_normalized,
const char *source_prop_encoding,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_string_t *new_str;
*was_normalized = FALSE;
if (*str == NULL)
return SVN_NO_ERROR;
SVN_ERR_ASSERT((*str)->data != NULL);
if (source_prop_encoding == NULL)
source_prop_encoding = "UTF-8";
new_str = NULL;
SVN_ERR(svn_subst_translate_string2(&new_str, NULL, was_normalized,
*str, source_prop_encoding, TRUE,
result_pool, scratch_pool));
*str = new_str;
return SVN_NO_ERROR;
}
+/* Remove r0 references from the mergeinfo string *STR.
+ *
+ * r0 was never a valid mergeinfo reference and cannot be committed with
+ * recent servers, but can be committed through a server older than 1.6.18
+ * for HTTP or older than 1.6.17 for the other protocols. See issue #4476
+ * "Mergeinfo containing r0 makes svnsync and dump and load fail".
+ *
+ * Set *WAS_CHANGED to TRUE if *STR was changed, otherwise to FALSE.
+ */
+static svn_error_t *
+remove_r0_mergeinfo(const svn_string_t **str,
+ svn_boolean_t *was_changed,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_stringbuf_t *new_str = svn_stringbuf_create_empty(result_pool);
+ apr_array_header_t *lines;
+ int i;
+ SVN_ERR_ASSERT(*str && (*str)->data);
+
+ *was_changed = FALSE;
+
+ /* for each line */
+ lines = svn_cstring_split((*str)->data, "\n", FALSE, scratch_pool);
+
+ for (i = 0; i < lines->nelts; i++)
+ {
+ char *line = APR_ARRAY_IDX(lines, i, char *);
+ char *colon;
+ char *rangelist;
+
+ /* split at the last colon */
+ colon = strrchr(line, ':');
+
+ if (! colon)
+ return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
+ _("Missing colon in svn:mergeinfo "
+ "property"));
+
+ rangelist = colon + 1;
+
+ /* remove r0 */
+ if (colon[1] == '0')
+ {
+ if (strncmp(rangelist, "0*,", 3) == 0)
+ {
+ rangelist += 3;
+ }
+ else if (strcmp(rangelist, "0*") == 0
+ || strncmp(rangelist, "0,", 2) == 0
+ || strncmp(rangelist, "0-1*", 4) == 0
+ || strncmp(rangelist, "0-1,", 4) == 0
+ || strcmp(rangelist, "0-1") == 0)
+ {
+ rangelist += 2;
+ }
+ else if (strcmp(rangelist, "0") == 0)
+ {
+ rangelist += 1;
+ }
+ else if (strncmp(rangelist, "0-", 2) == 0)
+ {
+ rangelist[0] = '1';
+ }
+ }
+
+ /* reassemble */
+ if (rangelist[0])
+ {
+ if (new_str->len)
+ svn_stringbuf_appendbyte(new_str, '\n');
+ svn_stringbuf_appendbytes(new_str, line, colon + 1 - line);
+ svn_stringbuf_appendcstr(new_str, rangelist);
+ }
+ }
+
+ if (strcmp((*str)->data, new_str->data) != 0)
+ {
+ *was_changed = TRUE;
+ }
+
+ *str = svn_stringbuf__morph_into_string(new_str);
+ return SVN_NO_ERROR;
+}
+
+
/* Normalize the encoding and line ending style of the values of properties
* in REV_PROPS that "need translation" (according to
* svn_prop_needs_translation(), which is currently all svn:* props) so that
* they are encoded in UTF-8 and contain only LF (\n) line endings.
*
* The number of properties that needed line ending normalization is returned in
* *NORMALIZED_COUNT.
*
* No re-encoding is performed if SOURCE_PROP_ENCODING is NULL.
*/
svn_error_t *
svnsync_normalize_revprops(apr_hash_t *rev_props,
int *normalized_count,
const char *source_prop_encoding,
apr_pool_t *pool)
{
apr_hash_index_t *hi;
*normalized_count = 0;
for (hi = apr_hash_first(pool, rev_props);
hi;
hi = apr_hash_next(hi))
{
const char *propname = svn__apr_hash_index_key(hi);
const svn_string_t *propval = svn__apr_hash_index_val(hi);
if (svn_prop_needs_translation(propname))
{
svn_boolean_t was_normalized;
SVN_ERR(normalize_string(&propval, &was_normalized,
source_prop_encoding, pool, pool));
/* Replace the existing prop value. */
svn_hash_sets(rev_props, propname, propval);
if (was_normalized)
(*normalized_count)++; /* Count it. */
}
}
return SVN_NO_ERROR;
}
/*** Synchronization Editor ***/
/* This editor has a couple of jobs.
*
* First, it needs to filter out the propchanges that can't be passed over
* libsvn_ra.
*
* Second, it needs to adjust for the fact that we might not actually have
* permission to see all of the data from the remote repository, which means
* we could get revisions that are totally empty from our point of view.
*
* Third, it needs to adjust copyfrom paths, adding the root url for the
* destination repository to the beginning of them.
*/
/* Edit baton */
typedef struct edit_baton_t {
const svn_delta_editor_t *wrapped_editor;
void *wrapped_edit_baton;
const char *to_url; /* URL we're copying into, for correct copyfrom URLs */
const char *source_prop_encoding;
svn_boolean_t called_open_root;
svn_boolean_t got_textdeltas;
svn_revnum_t base_revision;
svn_boolean_t quiet;
+ svn_boolean_t mergeinfo_tweaked; /* Did we tweak svn:mergeinfo? */
svn_boolean_t strip_mergeinfo; /* Are we stripping svn:mergeinfo? */
svn_boolean_t migrate_svnmerge; /* Are we converting svnmerge.py data? */
svn_boolean_t mergeinfo_stripped; /* Did we strip svn:mergeinfo? */
svn_boolean_t svnmerge_migrated; /* Did we convert svnmerge.py data? */
svn_boolean_t svnmerge_blocked; /* Was there any blocked svnmerge data? */
int *normalized_node_props_counter; /* Where to count normalizations? */
} edit_baton_t;
/* A dual-purpose baton for files and directories. */
typedef struct node_baton_t {
void *edit_baton;
void *wrapped_node_baton;
} node_baton_t;
/*** Editor vtable functions ***/
static svn_error_t *
set_target_revision(void *edit_baton,
svn_revnum_t target_revision,
apr_pool_t *pool)
{
edit_baton_t *eb = edit_baton;
return eb->wrapped_editor->set_target_revision(eb->wrapped_edit_baton,
target_revision, pool);
}
static svn_error_t *
open_root(void *edit_baton,
svn_revnum_t base_revision,
apr_pool_t *pool,
void **root_baton)
{
edit_baton_t *eb = edit_baton;
node_baton_t *dir_baton = apr_palloc(pool, sizeof(*dir_baton));
SVN_ERR(eb->wrapped_editor->open_root(eb->wrapped_edit_baton,
base_revision, pool,
&dir_baton->wrapped_node_baton));
eb->called_open_root = TRUE;
dir_baton->edit_baton = edit_baton;
*root_baton = dir_baton;
return SVN_NO_ERROR;
}
static svn_error_t *
delete_entry(const char *path,
svn_revnum_t base_revision,
void *parent_baton,
apr_pool_t *pool)
{
node_baton_t *pb = parent_baton;
edit_baton_t *eb = pb->edit_baton;
return eb->wrapped_editor->delete_entry(path, base_revision,
pb->wrapped_node_baton, pool);
}
static svn_error_t *
add_directory(const char *path,
void *parent_baton,
const char *copyfrom_path,
svn_revnum_t copyfrom_rev,
apr_pool_t *pool,
void **child_baton)
{
node_baton_t *pb = parent_baton;
edit_baton_t *eb = pb->edit_baton;
node_baton_t *b = apr_palloc(pool, sizeof(*b));
/* if copyfrom_path is an fspath create a proper uri */
if (copyfrom_path && copyfrom_path[0] == '/')
copyfrom_path = svn_path_url_add_component2(eb->to_url,
copyfrom_path + 1, pool);
SVN_ERR(eb->wrapped_editor->add_directory(path, pb->wrapped_node_baton,
copyfrom_path,
copyfrom_rev, pool,
&b->wrapped_node_baton));
b->edit_baton = eb;
*child_baton = b;
return SVN_NO_ERROR;
}
static svn_error_t *
open_directory(const char *path,
void *parent_baton,
svn_revnum_t base_revision,
apr_pool_t *pool,
void **child_baton)
{
node_baton_t *pb = parent_baton;
edit_baton_t *eb = pb->edit_baton;
node_baton_t *db = apr_palloc(pool, sizeof(*db));
SVN_ERR(eb->wrapped_editor->open_directory(path, pb->wrapped_node_baton,
base_revision, pool,
&db->wrapped_node_baton));
db->edit_baton = eb;
*child_baton = db;
return SVN_NO_ERROR;
}
static svn_error_t *
add_file(const char *path,
void *parent_baton,
const char *copyfrom_path,
svn_revnum_t copyfrom_rev,
apr_pool_t *pool,
void **file_baton)
{
node_baton_t *pb = parent_baton;
edit_baton_t *eb = pb->edit_baton;
node_baton_t *fb = apr_palloc(pool, sizeof(*fb));
/* if copyfrom_path is an fspath create a proper uri */
if (copyfrom_path && copyfrom_path[0] == '/')
copyfrom_path = svn_path_url_add_component2(eb->to_url,
copyfrom_path + 1, pool);
SVN_ERR(eb->wrapped_editor->add_file(path, pb->wrapped_node_baton,
copyfrom_path, copyfrom_rev,
pool, &fb->wrapped_node_baton));
fb->edit_baton = eb;
*file_baton = fb;
return SVN_NO_ERROR;
}
static svn_error_t *
open_file(const char *path,
void *parent_baton,
svn_revnum_t base_revision,
apr_pool_t *pool,
void **file_baton)
{
node_baton_t *pb = parent_baton;
edit_baton_t *eb = pb->edit_baton;
node_baton_t *fb = apr_palloc(pool, sizeof(*fb));
SVN_ERR(eb->wrapped_editor->open_file(path, pb->wrapped_node_baton,
base_revision, pool,
&fb->wrapped_node_baton));
fb->edit_baton = eb;
*file_baton = fb;
return SVN_NO_ERROR;
}
static svn_error_t *
apply_textdelta(void *file_baton,
const char *base_checksum,
apr_pool_t *pool,
svn_txdelta_window_handler_t *handler,
void **handler_baton)
{
node_baton_t *fb = file_baton;
edit_baton_t *eb = fb->edit_baton;
if (! eb->quiet)
{
if (! eb->got_textdeltas)
SVN_ERR(svn_cmdline_printf(pool, _("Transmitting file data ")));
SVN_ERR(svn_cmdline_printf(pool, "."));
SVN_ERR(svn_cmdline_fflush(stdout));
}
eb->got_textdeltas = TRUE;
return eb->wrapped_editor->apply_textdelta(fb->wrapped_node_baton,
base_checksum, pool,
handler, handler_baton);
}
static svn_error_t *
close_file(void *file_baton,
const char *text_checksum,
apr_pool_t *pool)
{
node_baton_t *fb = file_baton;
edit_baton_t *eb = fb->edit_baton;
return eb->wrapped_editor->close_file(fb->wrapped_node_baton,
text_checksum, pool);
}
static svn_error_t *
absent_file(const char *path,
void *file_baton,
apr_pool_t *pool)
{
node_baton_t *fb = file_baton;
edit_baton_t *eb = fb->edit_baton;
return eb->wrapped_editor->absent_file(path, fb->wrapped_node_baton, pool);
}
static svn_error_t *
close_directory(void *dir_baton,
apr_pool_t *pool)
{
node_baton_t *db = dir_baton;
edit_baton_t *eb = db->edit_baton;
return eb->wrapped_editor->close_directory(db->wrapped_node_baton, pool);
}
static svn_error_t *
absent_directory(const char *path,
void *dir_baton,
apr_pool_t *pool)
{
node_baton_t *db = dir_baton;
edit_baton_t *eb = db->edit_baton;
return eb->wrapped_editor->absent_directory(path, db->wrapped_node_baton,
pool);
}
static svn_error_t *
change_file_prop(void *file_baton,
const char *name,
const svn_string_t *value,
apr_pool_t *pool)
{
node_baton_t *fb = file_baton;
edit_baton_t *eb = fb->edit_baton;
/* only regular properties can pass over libsvn_ra */
if (svn_property_kind2(name) != svn_prop_regular_kind)
return SVN_NO_ERROR;
/* Maybe drop svn:mergeinfo. */
if (eb->strip_mergeinfo && (strcmp(name, SVN_PROP_MERGEINFO) == 0))
{
eb->mergeinfo_stripped = TRUE;
return SVN_NO_ERROR;
}
/* Maybe drop (errantly set, as this is a file) svnmerge.py properties. */
if (eb->migrate_svnmerge && (strcmp(name, "svnmerge-integrated") == 0))
{
eb->svnmerge_migrated = TRUE;
return SVN_NO_ERROR;
}
/* Remember if we see any svnmerge-blocked properties. (They really
shouldn't be here, as this is a file, but whatever...) */
if (eb->migrate_svnmerge && (strcmp(name, "svnmerge-blocked") == 0))
{
eb->svnmerge_blocked = TRUE;
}
/* Normalize svn:* properties as necessary. */
if (svn_prop_needs_translation(name))
{
svn_boolean_t was_normalized;
+ svn_boolean_t mergeinfo_tweaked = FALSE;
+
+ /* Normalize encoding to UTF-8, and EOL style to LF. */
SVN_ERR(normalize_string(&value, &was_normalized,
eb->source_prop_encoding, pool, pool));
+ /* Correct malformed mergeinfo. */
+ if (value && strcmp(name, SVN_PROP_MERGEINFO) == 0)
+ {
+ SVN_ERR(remove_r0_mergeinfo(&value, &mergeinfo_tweaked,
+ pool, pool));
+ if (mergeinfo_tweaked)
+ eb->mergeinfo_tweaked = TRUE;
+ }
if (was_normalized)
(*(eb->normalized_node_props_counter))++;
}
return eb->wrapped_editor->change_file_prop(fb->wrapped_node_baton,
name, value, pool);
}
static svn_error_t *
change_dir_prop(void *dir_baton,
const char *name,
const svn_string_t *value,
apr_pool_t *pool)
{
node_baton_t *db = dir_baton;
edit_baton_t *eb = db->edit_baton;
/* Only regular properties can pass over libsvn_ra */
if (svn_property_kind2(name) != svn_prop_regular_kind)
return SVN_NO_ERROR;
/* Maybe drop svn:mergeinfo. */
if (eb->strip_mergeinfo && (strcmp(name, SVN_PROP_MERGEINFO) == 0))
{
eb->mergeinfo_stripped = TRUE;
return SVN_NO_ERROR;
}
/* Maybe convert svnmerge-integrated data into svn:mergeinfo. (We
ignore svnmerge-blocked for now.) */
/* ### FIXME: Consult the mirror repository's HEAD prop values and
### merge svn:mergeinfo, svnmerge-integrated, and svnmerge-blocked. */
if (eb->migrate_svnmerge && (strcmp(name, "svnmerge-integrated") == 0))
{
if (value)
{
/* svnmerge-integrated differs from svn:mergeinfo in a pair
of ways. First, it can use tabs, newlines, or spaces to
delimit source information. Secondly, the source paths
are relative URLs, whereas svn:mergeinfo uses relative
paths (not URI-encoded). */
svn_error_t *err;
svn_stringbuf_t *mergeinfo_buf = svn_stringbuf_create_empty(pool);
svn_mergeinfo_t mergeinfo;
int i;
apr_array_header_t *sources =
svn_cstring_split(value->data, " \t\n", TRUE, pool);
svn_string_t *new_value;
for (i = 0; i < sources->nelts; i++)
{
const char *rel_path;
apr_array_header_t *path_revs =
svn_cstring_split(APR_ARRAY_IDX(sources, i, const char *),
":", TRUE, pool);
/* ### TODO: Warn? */
if (path_revs->nelts != 2)
continue;
/* Append this source's mergeinfo data. */
rel_path = APR_ARRAY_IDX(path_revs, 0, const char *);
rel_path = svn_path_uri_decode(rel_path, pool);
svn_stringbuf_appendcstr(mergeinfo_buf, rel_path);
svn_stringbuf_appendcstr(mergeinfo_buf, ":");
svn_stringbuf_appendcstr(mergeinfo_buf,
APR_ARRAY_IDX(path_revs, 1,
const char *));
svn_stringbuf_appendcstr(mergeinfo_buf, "\n");
}
/* Try to parse the mergeinfo string we've created, just to
check for bogosity. If all goes well, we'll unparse it
again and use that as our property value. */
err = svn_mergeinfo_parse(&mergeinfo, mergeinfo_buf->data, pool);
if (err)
{
svn_error_clear(err);
return SVN_NO_ERROR;
}
SVN_ERR(svn_mergeinfo_to_string(&new_value, mergeinfo, pool));
value = new_value;
}
name = SVN_PROP_MERGEINFO;
eb->svnmerge_migrated = TRUE;
}
/* Remember if we see any svnmerge-blocked properties. */
if (eb->migrate_svnmerge && (strcmp(name, "svnmerge-blocked") == 0))
{
eb->svnmerge_blocked = TRUE;
}
/* Normalize svn:* properties as necessary. */
if (svn_prop_needs_translation(name))
{
svn_boolean_t was_normalized;
+ svn_boolean_t mergeinfo_tweaked = FALSE;
+
+ /* Normalize encoding to UTF-8, and EOL style to LF. */
SVN_ERR(normalize_string(&value, &was_normalized, eb->source_prop_encoding,
pool, pool));
+ /* Maybe adjust svn:mergeinfo. */
+ if (value && strcmp(name, SVN_PROP_MERGEINFO) == 0)
+ {
+ SVN_ERR(remove_r0_mergeinfo(&value, &mergeinfo_tweaked,
+ pool, pool));
+ if (mergeinfo_tweaked)
+ eb->mergeinfo_tweaked = TRUE;
+ }
if (was_normalized)
(*(eb->normalized_node_props_counter))++;
}
return eb->wrapped_editor->change_dir_prop(db->wrapped_node_baton,
name, value, pool);
}
static svn_error_t *
close_edit(void *edit_baton,
apr_pool_t *pool)
{
edit_baton_t *eb = edit_baton;
/* If we haven't opened the root yet, that means we're transfering
an empty revision, probably because we aren't allowed to see the
contents for some reason. In any event, we need to open the root
and close it again, before we can close out the edit, or the
commit will fail. */
if (! eb->called_open_root)
{
void *baton;
SVN_ERR(eb->wrapped_editor->open_root(eb->wrapped_edit_baton,
eb->base_revision, pool,
&baton));
SVN_ERR(eb->wrapped_editor->close_directory(baton, pool));
}
if (! eb->quiet)
{
if (eb->got_textdeltas)
SVN_ERR(svn_cmdline_printf(pool, "\n"));
+ if (eb->mergeinfo_tweaked)
+ SVN_ERR(svn_cmdline_printf(pool,
+ "NOTE: Adjusted Subversion mergeinfo in "
+ "this revision.\n"));
if (eb->mergeinfo_stripped)
SVN_ERR(svn_cmdline_printf(pool,
"NOTE: Dropped Subversion mergeinfo "
"from this revision.\n"));
if (eb->svnmerge_migrated)
SVN_ERR(svn_cmdline_printf(pool,
"NOTE: Migrated 'svnmerge-integrated' in "
"this revision.\n"));
if (eb->svnmerge_blocked)
SVN_ERR(svn_cmdline_printf(pool,
"NOTE: Saw 'svnmerge-blocked' in this "
"revision (but didn't migrate it).\n"));
}
return eb->wrapped_editor->close_edit(eb->wrapped_edit_baton, pool);
}
static svn_error_t *
abort_edit(void *edit_baton,
apr_pool_t *pool)
{
edit_baton_t *eb = edit_baton;
return eb->wrapped_editor->abort_edit(eb->wrapped_edit_baton, pool);
}
/*** Editor factory function ***/
svn_error_t *
svnsync_get_sync_editor(const svn_delta_editor_t *wrapped_editor,
void *wrapped_edit_baton,
svn_revnum_t base_revision,
const char *to_url,
const char *source_prop_encoding,
svn_boolean_t quiet,
const svn_delta_editor_t **editor,
void **edit_baton,
int *normalized_node_props_counter,
apr_pool_t *pool)
{
svn_delta_editor_t *tree_editor = svn_delta_default_editor(pool);
edit_baton_t *eb = apr_pcalloc(pool, sizeof(*eb));
tree_editor->set_target_revision = set_target_revision;
tree_editor->open_root = open_root;
tree_editor->delete_entry = delete_entry;
tree_editor->add_directory = add_directory;
tree_editor->open_directory = open_directory;
tree_editor->change_dir_prop = change_dir_prop;
tree_editor->close_directory = close_directory;
tree_editor->absent_directory = absent_directory;
tree_editor->add_file = add_file;
tree_editor->open_file = open_file;
tree_editor->apply_textdelta = apply_textdelta;
tree_editor->change_file_prop = change_file_prop;
tree_editor->close_file = close_file;
tree_editor->absent_file = absent_file;
tree_editor->close_edit = close_edit;
tree_editor->abort_edit = abort_edit;
eb->wrapped_editor = wrapped_editor;
eb->wrapped_edit_baton = wrapped_edit_baton;
eb->base_revision = base_revision;
eb->to_url = to_url;
eb->source_prop_encoding = source_prop_encoding;
eb->quiet = quiet;
eb->normalized_node_props_counter = normalized_node_props_counter;
if (getenv("SVNSYNC_UNSUPPORTED_STRIP_MERGEINFO"))
{
eb->strip_mergeinfo = TRUE;
}
if (getenv("SVNSYNC_UNSUPPORTED_MIGRATE_SVNMERGE"))
{
/* Current we can't merge property values. That's only possible
if all the properties to be merged were always modified in
exactly the same revisions, or if we allow ourselves to
lookup the current state of properties in the sync
destination. So for now, migrating svnmerge.py data implies
stripping pre-existing svn:mergeinfo. */
/* ### FIXME: Do a real migration by consulting the mirror
### repository's HEAD propvalues and merging svn:mergeinfo,
### svnmerge-integrated, and svnmerge-blocked together. */
eb->migrate_svnmerge = TRUE;
eb->strip_mergeinfo = TRUE;
}
*editor = tree_editor;
*edit_baton = eb;
return SVN_NO_ERROR;
}
Index: vendor/subversion/dist/win-tests.py
===================================================================
--- vendor/subversion/dist/win-tests.py (revision 286500)
+++ vendor/subversion/dist/win-tests.py (revision 286501)
@@ -1,864 +1,1032 @@
#
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
#
"""
Driver for running the tests on Windows.
For a list of options, run this script with the --help option.
"""
# $HeadURL: http://svn.apache.org/repos/asf/subversion/branches/1.8.x/win-tests.py $
-# $LastChangedRevision: 1492044 $
+# $LastChangedRevision: 1692801 $
import os, sys, subprocess
import filecmp
import shutil
import traceback
try:
# Python >=3.0
import configparser
except ImportError:
# Python <3.0
import ConfigParser as configparser
import string
import random
import getopt
try:
my_getopt = getopt.gnu_getopt
except AttributeError:
my_getopt = getopt.getopt
def _usage_exit():
"print usage, exit the script"
print("Driver for running the tests on Windows.")
print("Usage: python win-tests.py [option] [test-path]")
print("")
print("Valid options:")
print(" -r, --release : test the Release configuration")
print(" -d, --debug : test the Debug configuration (default)")
print(" --bin=PATH : use the svn binaries installed in PATH")
print(" -u URL, --url=URL : run ra_dav or ra_svn tests against URL;")
print(" will start svnserve for ra_svn tests")
print(" -v, --verbose : talk more")
print(" -q, --quiet : talk less")
print(" -f, --fs-type=type : filesystem type to use (fsfs is default)")
print(" -c, --cleanup : cleanup after running a test")
print(" -t, --test=TEST : Run the TEST test (all is default); use")
print(" TEST#n to run a particular test number,")
print(" multiples also accepted e.g. '2,4-7'")
print(" --log-level=LEVEL : Set log level to LEVEL (E.g. DEBUG)")
print(" --log-to-stdout : Write log results to stdout")
print(" --svnserve-args=list : comma-separated list of arguments for")
print(" svnserve")
print(" default is '-d,-r,<test-path-root>'")
print(" --asp.net-hack : use '_svn' instead of '.svn' for the admin")
print(" dir name")
print(" --httpd-dir : location where Apache HTTPD is installed")
print(" --httpd-port : port for Apache HTTPD; random port number")
print(" will be used, if not specified")
print(" --httpd-daemon : Run Apache httpd as daemon")
print(" --httpd-service : Run Apache httpd as Windows service (default)")
print(" --httpd-no-log : Disable httpd logging")
print(" --http-short-circuit : Use SVNPathAuthz short_circuit on HTTP server")
print(" --disable-http-v2 : Do not advertise support for HTTPv2 on server")
print(" --disable-bulk-updates : Disable bulk updates on HTTP server")
print(" --ssl-cert : Path to SSL server certificate to trust.")
print(" --javahl : Run the javahl tests instead of the normal tests")
print(" --list : print test doc strings only")
print(" --milestone-filter=RE : RE is a regular expression pattern that (when")
print(" used with --list) limits the tests listed to")
print(" those with an associated issue in the tracker")
print(" which has a target milestone that matches RE.")
print(" --mode-filter=TYPE : limit tests to expected TYPE = XFAIL, SKIP, PASS,")
print(" or 'ALL' (default)")
print(" --enable-sasl : enable Cyrus SASL authentication for")
print(" svnserve")
print(" -p, --parallel : run multiple tests in parallel")
print(" --server-minor-version : the minor version of the server being")
print(" tested")
print(" --config-file : Configuration file for tests")
print(" --fsfs-sharding : Specify shard size (for fsfs)")
print(" --fsfs-packing : Run 'svnadmin pack' automatically")
sys.exit(0)
CMDLINE_TEST_SCRIPT_PATH = 'subversion/tests/cmdline/'
CMDLINE_TEST_SCRIPT_NATIVE_PATH = CMDLINE_TEST_SCRIPT_PATH.replace('/', os.sep)
sys.path.insert(0, os.path.join('build', 'generator'))
sys.path.insert(1, 'build')
import gen_win
version_header = os.path.join('subversion', 'include', 'svn_version.h')
cp = configparser.ConfigParser()
cp.read('gen-make.opts')
gen_obj = gen_win.GeneratorBase('build.conf', version_header,
cp.items('options'))
all_tests = gen_obj.test_progs + gen_obj.bdb_test_progs \
+ gen_obj.scripts + gen_obj.bdb_scripts
client_tests = [x for x in all_tests if x.startswith(CMDLINE_TEST_SCRIPT_PATH)]
svn_dlls = []
for section in gen_obj.sections.values():
if section.options.get("msvc-export"):
dll_basename = section.name + "-" + str(gen_obj.version) + ".dll"
svn_dlls.append(os.path.join("subversion", section.name, dll_basename))
opts, args = my_getopt(sys.argv[1:], 'hrdvqct:pu:f:',
['release', 'debug', 'verbose', 'quiet', 'cleanup',
'test=', 'url=', 'svnserve-args=', 'fs-type=', 'asp.net-hack',
'httpd-dir=', 'httpd-port=', 'httpd-daemon',
'httpd-server', 'http-short-circuit', 'httpd-no-log',
'disable-http-v2', 'disable-bulk-updates', 'help',
'fsfs-packing', 'fsfs-sharding=', 'javahl',
'list', 'enable-sasl', 'bin=', 'parallel',
'config-file=', 'server-minor-version=', 'log-level=',
'log-to-stdout', 'mode-filter=', 'milestone-filter=',
'ssl-cert='])
if len(args) > 1:
print('Warning: non-option arguments after the first one will be ignored')
# Interpret the options and set parameters
base_url, fs_type, verbose, quiet, cleanup = None, None, None, None, None
repo_loc = 'local repository.'
objdir = 'Debug'
log = 'tests.log'
faillog = 'fails.log'
run_svnserve = None
svnserve_args = None
run_httpd = None
httpd_port = None
httpd_service = None
httpd_no_log = None
http_short_circuit = False
advertise_httpv2 = True
http_bulk_updates = True
list_tests = None
milestone_filter = None
test_javahl = None
enable_sasl = None
svn_bin = None
parallel = None
fsfs_sharding = None
fsfs_packing = None
server_minor_version = None
config_file = None
log_to_stdout = None
mode_filter=None
tests_to_run = []
log_level = None
ssl_cert = None
for opt, val in opts:
if opt in ('-h', '--help'):
_usage_exit()
elif opt in ('-u', '--url'):
base_url = val
elif opt in ('-f', '--fs-type'):
fs_type = val
elif opt in ('-v', '--verbose'):
verbose = 1
elif opt in ('-q', '--quiet'):
quiet = 1
elif opt in ('-c', '--cleanup'):
cleanup = 1
elif opt in ('-t', '--test'):
tests_to_run.append(val)
elif opt in ['-r', '--release']:
objdir = 'Release'
elif opt in ['-d', '--debug']:
objdir = 'Debug'
elif opt == '--svnserve-args':
svnserve_args = val.split(',')
run_svnserve = 1
elif opt == '--asp.net-hack':
os.environ['SVN_ASP_DOT_NET_HACK'] = opt
elif opt == '--httpd-dir':
abs_httpd_dir = os.path.abspath(val)
run_httpd = 1
elif opt == '--httpd-port':
httpd_port = int(val)
elif opt == '--httpd-daemon':
httpd_service = 0
elif opt == '--httpd-service':
httpd_service = 1
elif opt == '--httpd-no-log':
httpd_no_log = 1
elif opt == '--http-short-circuit':
http_short_circuit = True
elif opt == '--disable-http-v2':
advertise_httpv2 = False
elif opt == '--disable-bulk-updates':
http_bulk_updates = False
elif opt == '--fsfs-sharding':
fsfs_sharding = int(val)
elif opt == '--fsfs-packing':
fsfs_packing = 1
elif opt == '--javahl':
test_javahl = 1
elif opt == '--list':
list_tests = 1
elif opt == '--milestone-filter':
milestone_filter = val
elif opt == '--mode-filter':
mode_filter = val
elif opt == '--enable-sasl':
enable_sasl = 1
base_url = "svn://localhost/"
elif opt == '--server-minor-version':
server_minor_version = val
elif opt == '--bin':
svn_bin = val
elif opt in ('-p', '--parallel'):
parallel = 1
elif opt in ('--config-file'):
config_file = val
elif opt == '--log-to-stdout':
log_to_stdout = 1
elif opt == '--log-level':
log_level = val
elif opt == '--ssl-cert':
ssl_cert = val
# Calculate the source and test directory names
abs_srcdir = os.path.abspath("")
abs_objdir = os.path.join(abs_srcdir, objdir)
if len(args) == 0:
abs_builddir = abs_objdir
create_dirs = 0
else:
abs_builddir = os.path.abspath(args[0])
create_dirs = 1
# Default to fsfs explicitly
if not fs_type:
fs_type = 'fsfs'
# Don't run bdb tests if they want to test fsfs
if fs_type == 'fsfs':
all_tests = gen_obj.test_progs + gen_obj.scripts
if run_httpd:
if not httpd_port:
httpd_port = random.randrange(1024, 30000)
if not base_url:
base_url = 'http://localhost:' + str(httpd_port)
if base_url:
repo_loc = 'remote repository ' + base_url + '.'
if base_url[:4] == 'http':
log = 'dav-tests.log'
faillog = 'dav-fails.log'
elif base_url[:3] == 'svn':
log = 'svn-tests.log'
faillog = 'svn-fails.log'
run_svnserve = 1
else:
# Don't know this scheme, but who're we to judge whether it's
# correct or not?
log = 'url-tests.log'
faillog = 'url-fails.log'
# Have to move the executables where the tests expect them to be
copied_execs = [] # Store copied exec files to avoid the final dir scan
def create_target_dir(dirname):
tgt_dir = os.path.join(abs_builddir, dirname)
if not os.path.exists(tgt_dir):
if verbose:
print("mkdir: %s" % tgt_dir)
os.makedirs(tgt_dir)
def copy_changed_file(src, tgt):
if not os.path.isfile(src):
print('Could not find ' + src)
sys.exit(1)
if os.path.isdir(tgt):
tgt = os.path.join(tgt, os.path.basename(src))
if os.path.exists(tgt):
assert os.path.isfile(tgt)
if filecmp.cmp(src, tgt):
if verbose:
print("same: %s" % src)
print(" and: %s" % tgt)
return 0
if verbose:
print("copy: %s" % src)
print(" to: %s" % tgt)
shutil.copy(src, tgt)
return 1
def copy_execs(baton, dirname, names):
copied_execs = baton
for name in names:
if not name.endswith('.exe'):
continue
src = os.path.join(dirname, name)
tgt = os.path.join(abs_builddir, dirname, name)
create_target_dir(dirname)
if copy_changed_file(src, tgt):
copied_execs.append(tgt)
def locate_libs():
"Move DLLs to a known location and set env vars"
dlls = []
# look for APR 1.x dll's and use those if found
apr_test_path = os.path.join(gen_obj.apr_path, objdir, 'libapr-1.dll')
if os.path.exists(apr_test_path):
suffix = "-1"
else:
suffix = ""
if cp.has_option('options', '--with-static-apr'):
dlls.append(os.path.join(gen_obj.apr_path, objdir,
'libapr%s.dll' % (suffix)))
dlls.append(os.path.join(gen_obj.apr_util_path, objdir,
'libaprutil%s.dll' % (suffix)))
if gen_obj.libintl_path is not None:
dlls.append(os.path.join(gen_obj.libintl_path, 'bin', 'intl3_svn.dll'))
if gen_obj.bdb_lib is not None:
partial_path = os.path.join(gen_obj.bdb_path, 'bin', gen_obj.bdb_lib)
if objdir == 'Debug':
dlls.append(partial_path + 'd.dll')
else:
dlls.append(partial_path + '.dll')
if gen_obj.sasl_path is not None:
dlls.append(os.path.join(gen_obj.sasl_path, 'lib', 'libsasl.dll'))
for dll in dlls:
copy_changed_file(dll, abs_objdir)
# Copy the Subversion library DLLs
if not cp.has_option('options', '--disable-shared'):
for svn_dll in svn_dlls:
copy_changed_file(os.path.join(abs_objdir, svn_dll), abs_objdir)
# Copy the Apache modules
if run_httpd and cp.has_option('options', '--with-httpd'):
mod_dav_svn_path = os.path.join(abs_objdir, 'subversion',
'mod_dav_svn', 'mod_dav_svn.so')
mod_authz_svn_path = os.path.join(abs_objdir, 'subversion',
'mod_authz_svn', 'mod_authz_svn.so')
mod_dontdothat_path = os.path.join(abs_objdir, 'tools', 'server-side',
'mod_dontdothat', 'mod_dontdothat.so')
copy_changed_file(mod_dav_svn_path, abs_objdir)
copy_changed_file(mod_authz_svn_path, abs_objdir)
copy_changed_file(mod_dontdothat_path, abs_objdir)
os.environ['PATH'] = abs_objdir + os.pathsep + os.environ['PATH']
def fix_case(path):
path = os.path.normpath(path)
parts = path.split(os.path.sep)
drive = parts[0].upper()
parts = parts[1:]
path = drive + os.path.sep
for part in parts:
dirs = os.listdir(path)
for dir in dirs:
if dir.lower() == part.lower():
path = os.path.join(path, dir)
break
return path
class Svnserve:
"Run svnserve for ra_svn tests"
def __init__(self, svnserve_args, objdir, abs_objdir, abs_builddir):
self.args = svnserve_args
self.name = 'svnserve.exe'
self.kind = objdir
self.path = os.path.join(abs_objdir,
'subversion', 'svnserve', self.name)
self.root = os.path.join(abs_builddir, CMDLINE_TEST_SCRIPT_NATIVE_PATH)
self.proc_handle = None
def __del__(self):
"Stop svnserve when the object is deleted"
self.stop()
def _quote(self, arg):
if ' ' in arg:
return '"' + arg + '"'
else:
return arg
def start(self):
if not self.args:
args = [self.name, '-d', '-r', self.root]
else:
args = [self.name] + self.args
print('Starting %s %s' % (self.kind, self.name))
try:
import win32process
import win32con
args = ' '.join([self._quote(x) for x in args])
self.proc_handle = (
win32process.CreateProcess(self._quote(self.path), args,
None, None, 0,
win32con.CREATE_NEW_CONSOLE,
None, None, win32process.STARTUPINFO()))[0]
except ImportError:
os.spawnv(os.P_NOWAIT, self.path, args)
def stop(self):
if self.proc_handle is not None:
try:
import win32process
print('Stopping %s' % self.name)
win32process.TerminateProcess(self.proc_handle, 0)
return
except ImportError:
pass
print('Svnserve.stop not implemented')
class Httpd:
"Run httpd for DAV tests"
def __init__(self, abs_httpd_dir, abs_objdir, abs_builddir, httpd_port,
service, no_log, httpv2, short_circuit, bulk_updates):
self.name = 'apache.exe'
self.httpd_port = httpd_port
self.httpd_dir = abs_httpd_dir
if httpv2:
self.httpv2_option = 'on'
else:
self.httpv2_option = 'off'
if bulk_updates:
self.bulkupdates_option = 'on'
else:
self.bulkupdates_option = 'off'
self.service = service
self.proc_handle = None
self.path = os.path.join(self.httpd_dir, 'bin', self.name)
if short_circuit:
self.path_authz_option = 'short_circuit'
else:
self.path_authz_option = 'on'
if not os.path.exists(self.path):
self.name = 'httpd.exe'
self.path = os.path.join(self.httpd_dir, 'bin', self.name)
if not os.path.exists(self.path):
raise RuntimeError("Could not find a valid httpd binary!")
self.root_dir = os.path.join(CMDLINE_TEST_SCRIPT_NATIVE_PATH, 'httpd')
self.root = os.path.join(abs_builddir, self.root_dir)
self.authz_file = os.path.join(abs_builddir,
CMDLINE_TEST_SCRIPT_NATIVE_PATH,
'svn-test-work', 'authz')
self.dontdothat_file = os.path.join(abs_builddir,
CMDLINE_TEST_SCRIPT_NATIVE_PATH,
'svn-test-work', 'dontdothat')
self.httpd_config = os.path.join(self.root, 'httpd.conf')
self.httpd_users = os.path.join(self.root, 'users')
self.httpd_mime_types = os.path.join(self.root, 'mime.types')
+ self.httpd_groups = os.path.join(self.root, 'groups')
self.abs_builddir = abs_builddir
self.abs_objdir = abs_objdir
self.service_name = 'svn-test-httpd-' + str(httpd_port)
if self.service:
self.httpd_args = [self.name, '-n', self._quote(self.service_name),
'-f', self._quote(self.httpd_config)]
else:
self.httpd_args = [self.name, '-f', self._quote(self.httpd_config)]
create_target_dir(self.root_dir)
self._create_users_file()
+ self._create_groups_file()
self._create_mime_types_file()
self._create_dontdothat_file()
# Determine version.
if os.path.exists(os.path.join(self.httpd_dir,
'modules', 'mod_access_compat.so')):
self.httpd_ver = 2.3
elif os.path.exists(os.path.join(self.httpd_dir,
'modules', 'mod_auth_basic.so')):
self.httpd_ver = 2.2
else:
self.httpd_ver = 2.0
# Create httpd config file
fp = open(self.httpd_config, 'w')
# Limit the number of threads (default = 64)
fp.write('<IfModule mpm_winnt.c>\n')
fp.write('ThreadsPerChild 16\n')
fp.write('</IfModule>\n')
# Global Environment
fp.write('ServerRoot ' + self._quote(self.root) + '\n')
fp.write('DocumentRoot ' + self._quote(self.root) + '\n')
fp.write('ServerName localhost\n')
fp.write('PidFile pid\n')
fp.write('ErrorLog log\n')
fp.write('Listen ' + str(self.httpd_port) + '\n')
if not no_log:
fp.write('LogFormat "%h %l %u %t \\"%r\\" %>s %b" common\n')
fp.write('Customlog log common\n')
fp.write('LogLevel Debug\n')
else:
fp.write('LogLevel Crit\n')
# Write LoadModule for minimal system module
fp.write(self._sys_module('dav_module', 'mod_dav.so'))
if self.httpd_ver >= 2.3:
fp.write(self._sys_module('access_compat_module', 'mod_access_compat.so'))
fp.write(self._sys_module('authz_core_module', 'mod_authz_core.so'))
fp.write(self._sys_module('authz_user_module', 'mod_authz_user.so'))
fp.write(self._sys_module('authn_core_module', 'mod_authn_core.so'))
if self.httpd_ver >= 2.2:
fp.write(self._sys_module('auth_basic_module', 'mod_auth_basic.so'))
fp.write(self._sys_module('authn_file_module', 'mod_authn_file.so'))
+ fp.write(self._sys_module('authz_groupfile_module', 'mod_authz_groupfile.so'))
+ fp.write(self._sys_module('authz_host_module', 'mod_authz_host.so'))
else:
fp.write(self._sys_module('auth_module', 'mod_auth.so'))
fp.write(self._sys_module('alias_module', 'mod_alias.so'))
fp.write(self._sys_module('mime_module', 'mod_mime.so'))
fp.write(self._sys_module('log_config_module', 'mod_log_config.so'))
# Write LoadModule for Subversion modules
fp.write(self._svn_module('dav_svn_module', 'mod_dav_svn.so'))
fp.write(self._svn_module('authz_svn_module', 'mod_authz_svn.so'))
# And for mod_dontdothat
fp.write(self._svn_module('dontdothat_module', 'mod_dontdothat.so'))
# Don't handle .htaccess, symlinks, etc.
fp.write('<Directory />\n')
fp.write('AllowOverride None\n')
fp.write('Options None\n')
fp.write('</Directory>\n\n')
# Define two locations for repositories
fp.write(self._svn_repo('repositories'))
fp.write(self._svn_repo('local_tmp'))
+ fp.write(self._svn_authz_repo())
# And two redirects for the redirect tests
fp.write('RedirectMatch permanent ^/svn-test-work/repositories/'
'REDIRECT-PERM-(.*)$ /svn-test-work/repositories/$1\n')
fp.write('RedirectMatch ^/svn-test-work/repositories/'
'REDIRECT-TEMP-(.*)$ /svn-test-work/repositories/$1\n')
fp.write('TypesConfig ' + self._quote(self.httpd_mime_types) + '\n')
fp.write('HostNameLookups Off\n')
fp.close()
def __del__(self):
"Stop httpd when the object is deleted"
self.stop()
def _quote(self, arg):
if ' ' in arg:
return '"' + arg + '"'
else:
return arg
def _create_users_file(self):
"Create users file"
htpasswd = os.path.join(self.httpd_dir, 'bin', 'htpasswd.exe')
# Create the cheapest to compare password form for our testsuite
os.spawnv(os.P_WAIT, htpasswd, ['htpasswd.exe', '-bcp', self.httpd_users,
'jrandom', 'rayjandom'])
os.spawnv(os.P_WAIT, htpasswd, ['htpasswd.exe', '-bp', self.httpd_users,
'jconstant', 'rayjandom'])
+ os.spawnv(os.P_WAIT, htpasswd, ['htpasswd.exe', '-bp', self.httpd_users,
+ 'JRANDOM', 'rayjandom'])
+ os.spawnv(os.P_WAIT, htpasswd, ['htpasswd.exe', '-bp', self.httpd_users,
+ 'JCONSTANT', 'rayjandom'])
+ def _create_groups_file(self):
+ "Create groups for mod_authz_svn tests"
+ fp = open(self.httpd_groups, 'w')
+ fp.write('random: jrandom\n')
+ fp.write('constant: jconstant\n')
+ fp.close()
+
def _create_mime_types_file(self):
"Create empty mime.types file"
fp = open(self.httpd_mime_types, 'w')
fp.close()
def _create_dontdothat_file(self):
"Create empty mime.types file"
# If the tests have not previously been run or were cleaned
# up, then 'svn-test-work' does not exist yet.
parent_dir = os.path.dirname(self.dontdothat_file)
if not os.path.exists(parent_dir):
os.makedirs(parent_dir)
fp = open(self.dontdothat_file, 'w')
fp.write('[recursive-actions]\n')
fp.write('/ = deny\n')
fp.close()
def _sys_module(self, name, path):
full_path = os.path.join(self.httpd_dir, 'modules', path)
return 'LoadModule ' + name + " " + self._quote(full_path) + '\n'
def _svn_module(self, name, path):
full_path = os.path.join(self.abs_objdir, path)
return 'LoadModule ' + name + ' ' + self._quote(full_path) + '\n'
def _svn_repo(self, name):
path = os.path.join(self.abs_builddir,
CMDLINE_TEST_SCRIPT_NATIVE_PATH,
'svn-test-work', name)
location = '/svn-test-work/' + name
ddt_location = '/ddt-test-work/' + name
return \
'<Location ' + location + '>\n' \
' DAV svn\n' \
' SVNParentPath ' + self._quote(path) + '\n' \
' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
' SVNPathAuthz ' + self.path_authz_option + '\n' \
' SVNAllowBulkUpdates ' + self.bulkupdates_option + '\n' \
' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
' AuthType Basic\n' \
' AuthName "Subversion Repository"\n' \
' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
' Require valid-user\n' \
'</Location>\n' \
'<Location ' + ddt_location + '>\n' \
' DAV svn\n' \
' SVNParentPath ' + self._quote(path) + '\n' \
' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
' SVNPathAuthz ' + self.path_authz_option + '\n' \
' SVNAllowBulkUpdates ' + self.bulkupdates_option + '\n' \
' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
' AuthType Basic\n' \
' AuthName "Subversion Repository"\n' \
' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
' Require valid-user\n' \
' DontDoThatConfigFile ' + self._quote(self.dontdothat_file) + '\n' \
'</Location>\n'
+ def _svn_authz_repo(self):
+ local_tmp = os.path.join(self.abs_builddir,
+ CMDLINE_TEST_SCRIPT_NATIVE_PATH,
+ 'svn-test-work', 'local_tmp')
+ return \
+ '<Location /authz-test-work/anon>' + '\n' \
+ ' DAV svn' + '\n' \
+ ' SVNParentPath ' + local_tmp + '\n' \
+ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
+ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
+ ' SVNListParentPath On' + '\n' \
+ ' <IfModule mod_authz_core.c>' + '\n' \
+ ' Require all granted' + '\n' \
+ ' </IfModule>' + '\n' \
+ ' <IfModule !mod_authz_core.c>' + '\n' \
+ ' Allow from all' + '\n' \
+ ' </IfModule>' + '\n' \
+ ' SVNPathAuthz ' + self.path_authz_option + '\n' \
+ '</Location>' + '\n' \
+ '<Location /authz-test-work/mixed>' + '\n' \
+ ' DAV svn' + '\n' \
+ ' SVNParentPath ' + local_tmp + '\n' \
+ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
+ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
+ ' SVNListParentPath On' + '\n' \
+ ' AuthType Basic' + '\n' \
+ ' AuthName "Subversion Repository"' + '\n' \
+ ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
+ ' Require valid-user' + '\n' \
+ ' Satisfy Any' + '\n' \
+ ' SVNPathAuthz ' + self.path_authz_option + '\n' \
+ '</Location>' + '\n' \
+ '<Location /authz-test-work/mixed-noauthwhenanon>' + '\n' \
+ ' DAV svn' + '\n' \
+ ' SVNParentPath ' + local_tmp + '\n' \
+ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
+ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
+ ' SVNListParentPath On' + '\n' \
+ ' AuthType Basic' + '\n' \
+ ' AuthName "Subversion Repository"' + '\n' \
+ ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
+ ' Require valid-user' + '\n' \
+ ' AuthzSVNNoAuthWhenAnonymousAllowed On' + '\n' \
+ ' SVNPathAuthz On' + '\n' \
+ '</Location>' + '\n' \
+ '<Location /authz-test-work/authn>' + '\n' \
+ ' DAV svn' + '\n' \
+ ' SVNParentPath ' + local_tmp + '\n' \
+ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
+ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
+ ' SVNListParentPath On' + '\n' \
+ ' AuthType Basic' + '\n' \
+ ' AuthName "Subversion Repository"' + '\n' \
+ ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
+ ' Require valid-user' + '\n' \
+ ' SVNPathAuthz ' + self.path_authz_option + '\n' \
+ '</Location>' + '\n' \
+ '<Location /authz-test-work/authn-anonoff>' + '\n' \
+ ' DAV svn' + '\n' \
+ ' SVNParentPath ' + local_tmp + '\n' \
+ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
+ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
+ ' SVNListParentPath On' + '\n' \
+ ' AuthType Basic' + '\n' \
+ ' AuthName "Subversion Repository"' + '\n' \
+ ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
+ ' Require valid-user' + '\n' \
+ ' AuthzSVNAnonymous Off' + '\n' \
+ ' SVNPathAuthz On' + '\n' \
+ '</Location>' + '\n' \
+ '<Location /authz-test-work/authn-lcuser>' + '\n' \
+ ' DAV svn' + '\n' \
+ ' SVNParentPath ' + local_tmp + '\n' \
+ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
+ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
+ ' SVNListParentPath On' + '\n' \
+ ' AuthType Basic' + '\n' \
+ ' AuthName "Subversion Repository"' + '\n' \
+ ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
+ ' Require valid-user' + '\n' \
+ ' AuthzForceUsernameCase Lower' + '\n' \
+ ' SVNPathAuthz ' + self.path_authz_option + '\n' \
+ '</Location>' + '\n' \
+ '<Location /authz-test-work/authn-lcuser>' + '\n' \
+ ' DAV svn' + '\n' \
+ ' SVNParentPath ' + local_tmp + '\n' \
+ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
+ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
+ ' SVNListParentPath On' + '\n' \
+ ' AuthType Basic' + '\n' \
+ ' AuthName "Subversion Repository"' + '\n' \
+ ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
+ ' Require valid-user' + '\n' \
+ ' AuthzForceUsernameCase Lower' + '\n' \
+ ' SVNPathAuthz ' + self.path_authz_option + '\n' \
+ '</Location>' + '\n' \
+ '<Location /authz-test-work/authn-group>' + '\n' \
+ ' DAV svn' + '\n' \
+ ' SVNParentPath ' + local_tmp + '\n' \
+ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
+ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
+ ' SVNListParentPath On' + '\n' \
+ ' AuthType Basic' + '\n' \
+ ' AuthName "Subversion Repository"' + '\n' \
+ ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
+ ' AuthGroupFile ' + self._quote(self.httpd_groups) + '\n' \
+ ' Require group random' + '\n' \
+ ' AuthzSVNAuthoritative Off' + '\n' \
+ ' SVNPathAuthz On' + '\n' \
+ '</Location>' + '\n' \
+ '<IfModule mod_authz_core.c>' + '\n' \
+ '<Location /authz-test-work/sallrany>' + '\n' \
+ ' DAV svn' + '\n' \
+ ' SVNParentPath ' + local_tmp + '\n' \
+ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
+ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
+ ' SVNListParentPath On' + '\n' \
+ ' AuthType Basic' + '\n' \
+ ' AuthName "Subversion Repository"' + '\n' \
+ ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
+ ' AuthzSendForbiddenOnFailure On' + '\n' \
+ ' Satisfy All' + '\n' \
+ ' <RequireAny>' + '\n' \
+ ' Require valid-user' + '\n' \
+ ' Require expr req(\'ALLOW\') == \'1\'' + '\n' \
+ ' </RequireAny>' + '\n' \
+ ' SVNPathAuthz ' + self.path_authz_option + '\n' \
+ '</Location>' + '\n' \
+ '<Location /authz-test-work/sallrall>'+ '\n' \
+ ' DAV svn' + '\n' \
+ ' SVNParentPath ' + local_tmp + '\n' \
+ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
+ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
+ ' SVNListParentPath On' + '\n' \
+ ' AuthType Basic' + '\n' \
+ ' AuthName "Subversion Repository"' + '\n' \
+ ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
+ ' AuthzSendForbiddenOnFailure On' + '\n' \
+ ' Satisfy All' + '\n' \
+ ' <RequireAll>' + '\n' \
+ ' Require valid-user' + '\n' \
+ ' Require expr req(\'ALLOW\') == \'1\'' + '\n' \
+ ' </RequireAll>' + '\n' \
+ ' SVNPathAuthz ' + self.path_authz_option + '\n' \
+ '</Location>' + '\n' \
+ '</IfModule>' + '\n' \
+
def start(self):
if self.service:
self._start_service()
else:
self._start_daemon()
def stop(self):
if self.service:
self._stop_service()
else:
self._stop_daemon()
def _start_service(self):
"Install and start HTTPD service"
print('Installing service %s' % self.service_name)
os.spawnv(os.P_WAIT, self.path, self.httpd_args + ['-k', 'install'])
print('Starting service %s' % self.service_name)
os.spawnv(os.P_WAIT, self.path, self.httpd_args + ['-k', 'start'])
def _stop_service(self):
"Stop and uninstall HTTPD service"
os.spawnv(os.P_WAIT, self.path, self.httpd_args + ['-k', 'stop'])
os.spawnv(os.P_WAIT, self.path, self.httpd_args + ['-k', 'uninstall'])
def _start_daemon(self):
"Start HTTPD as daemon"
print('Starting httpd as daemon')
print(self.httpd_args)
try:
import win32process
import win32con
args = ' '.join([self._quote(x) for x in self.httpd_args])
self.proc_handle = (
win32process.CreateProcess(self._quote(self.path), args,
None, None, 0,
win32con.CREATE_NEW_CONSOLE,
None, None, win32process.STARTUPINFO()))[0]
except ImportError:
os.spawnv(os.P_NOWAIT, self.path, self.httpd_args)
def _stop_daemon(self):
"Stop the HTTPD daemon"
if self.proc_handle is not None:
try:
import win32process
print('Stopping %s' % self.name)
win32process.TerminateProcess(self.proc_handle, 0)
return
except ImportError:
pass
print('Httpd.stop_daemon not implemented')
# Move the binaries to the test directory
locate_libs()
if create_dirs:
old_cwd = os.getcwd()
try:
os.chdir(abs_objdir)
baton = copied_execs
for dirpath, dirs, files in os.walk('subversion'):
copy_execs(baton, dirpath, files)
for dirpath, dirs, files in os.walk('tools/server-side'):
copy_execs(baton, dirpath, files)
except:
os.chdir(old_cwd)
raise
else:
os.chdir(old_cwd)
# Create the base directory for Python tests
create_target_dir(CMDLINE_TEST_SCRIPT_NATIVE_PATH)
# Ensure the tests directory is correctly cased
abs_builddir = fix_case(abs_builddir)
daemon = None
# Run the tests
# No need to start any servers if we are only listing the tests.
if not list_tests:
if run_svnserve:
daemon = Svnserve(svnserve_args, objdir, abs_objdir, abs_builddir)
if run_httpd:
daemon = Httpd(abs_httpd_dir, abs_objdir, abs_builddir, httpd_port,
httpd_service, httpd_no_log,
advertise_httpv2, http_short_circuit,
http_bulk_updates)
# Start service daemon, if any
if daemon:
daemon.start()
# Find the full path and filename of any test that is specified just by
# its base name.
if len(tests_to_run) != 0:
tests = []
for t in tests_to_run:
tns = None
if '#' in t:
t, tns = t.split('#')
test = [x for x in all_tests if x.split('/')[-1] == t]
if not test and not (t.endswith('-test.exe') or t.endswith('_tests.py')):
# The lengths of '-test.exe' and of '_tests.py' are both 9.
test = [x for x in all_tests if x.split('/')[-1][:-9] == t]
if not test:
print("Skipping test '%s', test not found." % t)
elif tns:
tests.append('%s#%s' % (test[0], tns))
else:
tests.extend(test)
tests_to_run = tests
else:
tests_to_run = all_tests
if list_tests:
print('Listing %s configuration on %s' % (objdir, repo_loc))
else:
print('Testing %s configuration on %s' % (objdir, repo_loc))
sys.path.insert(0, os.path.join(abs_srcdir, 'build'))
if not test_javahl:
import run_tests
if log_to_stdout:
log_file = None
fail_log_file = None
else:
log_file = os.path.join(abs_builddir, log)
fail_log_file = os.path.join(abs_builddir, faillog)
+ if run_httpd:
+ httpd_version = "%.1f" % daemon.httpd_ver
+ else:
+ httpd_version = None
th = run_tests.TestHarness(abs_srcdir, abs_builddir,
log_file,
fail_log_file,
base_url, fs_type, 'serf',
server_minor_version, not quiet,
cleanup, enable_sasl, parallel, config_file,
fsfs_sharding, fsfs_packing,
list_tests, svn_bin, mode_filter,
milestone_filter,
+ httpd_version=httpd_version,
set_log_level=log_level, ssl_cert=ssl_cert)
old_cwd = os.getcwd()
try:
os.chdir(abs_builddir)
failed = th.run(tests_to_run)
except:
os.chdir(old_cwd)
raise
else:
os.chdir(old_cwd)
else:
failed = False
args = (
'java.exe',
'-Dtest.rootdir=' + os.path.join(abs_builddir, 'javahl'),
'-Dtest.srcdir=' + os.path.join(abs_srcdir,
'subversion/bindings/javahl'),
'-Dtest.rooturl=',
'-Dtest.fstype=' + fs_type ,
'-Dtest.tests=',
'-Djava.library.path='
+ os.path.join(abs_objdir,
'subversion/bindings/javahl/native'),
'-classpath',
os.path.join(abs_srcdir, 'subversion/bindings/javahl/classes') +';' +
gen_obj.junit_path
)
sys.stderr.flush()
print('Running org.apache.subversion tests:')
sys.stdout.flush()
r = subprocess.call(args + tuple(['org.apache.subversion.javahl.RunTests']))
sys.stdout.flush()
sys.stderr.flush()
if (r != 0):
print('[Test runner reported failure]')
failed = True
print('Running org.tigris.subversion tests:')
sys.stdout.flush()
r = subprocess.call(args + tuple(['org.tigris.subversion.javahl.RunTests']))
sys.stdout.flush()
sys.stderr.flush()
if (r != 0):
print('[Test runner reported failure]')
failed = True
# Stop service daemon, if any
if daemon:
del daemon
# Remove the execs again
for tgt in copied_execs:
try:
if os.path.isfile(tgt):
if verbose:
print("kill: %s" % tgt)
os.unlink(tgt)
except:
traceback.print_exc(file=sys.stdout)
pass
if failed:
sys.exit(1)

File Metadata

Mime Type
application/octet-stream
Expires
Sun, Apr 21, 10:01 AM (1 d, 23 h)
Storage Engine
chunks
Storage Format
Chunks
Storage Handle
gdUpTyaNcYNP
Default Alt Text
(6 MB)

Event Timeline